vpn.scm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2017 Julien Lepiller <julien@lepiller.eu>
  3. ;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org>
  4. ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
  5. ;;;
  6. ;;; This file is part of GNU Guix.
  7. ;;;
  8. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  9. ;;; under the terms of the GNU General Public License as published by
  10. ;;; the Free Software Foundation; either version 3 of the License, or (at
  11. ;;; your option) any later version.
  12. ;;;
  13. ;;; GNU Guix is distributed in the hope that it will be useful, but
  14. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;;; GNU General Public License for more details.
  17. ;;;
  18. ;;; You should have received a copy of the GNU General Public License
  19. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  20. (define-module (gnu services vpn)
  21. #:use-module (gnu services)
  22. #:use-module (gnu services configuration)
  23. #:use-module (gnu services shepherd)
  24. #:use-module (gnu system shadow)
  25. #:use-module (gnu packages admin)
  26. #:use-module (gnu packages vpn)
  27. #:use-module (guix packages)
  28. #:use-module (guix records)
  29. #:use-module (guix gexp)
  30. #:use-module (srfi srfi-1)
  31. #:use-module (ice-9 match)
  32. #:use-module (ice-9 regex)
  33. #:export (openvpn-client-service
  34. openvpn-server-service
  35. openvpn-client-service-type
  36. openvpn-server-service-type
  37. openvpn-client-configuration
  38. openvpn-server-configuration
  39. openvpn-remote-configuration
  40. openvpn-ccd-configuration
  41. generate-openvpn-client-documentation
  42. generate-openvpn-server-documentation))
  43. ;;;
  44. ;;; OpenVPN.
  45. ;;;
  46. (define (uglify-field-name name)
  47. (match name
  48. ('verbosity "verb")
  49. (_ (let ((str (symbol->string name)))
  50. (if (string-suffix? "?" str)
  51. (substring str 0 (1- (string-length str)))
  52. str)))))
  53. (define (serialize-field field-name val)
  54. (if (eq? field-name 'pid-file)
  55. (format #t "")
  56. (format #t "~a ~a\n" (uglify-field-name field-name) val)))
  57. (define serialize-string serialize-field)
  58. (define-maybe string)
  59. (define (serialize-boolean field-name val)
  60. (if val
  61. (serialize-field field-name "")
  62. (format #t "")))
  63. (define (ip-mask? val)
  64. (and (string? val)
  65. (if (string-match "^([0-9]+\\.){3}[0-9]+ ([0-9]+\\.){3}[0-9]+$" val)
  66. (let ((numbers (string-tokenize val char-set:digit)))
  67. (all-lte numbers (list 255 255 255 255 255 255 255 255)))
  68. #f)))
  69. (define serialize-ip-mask serialize-string)
  70. (define-syntax define-enumerated-field-type
  71. (lambda (x)
  72. (define (id-append ctx . parts)
  73. (datum->syntax ctx (apply symbol-append (map syntax->datum parts))))
  74. (syntax-case x ()
  75. ((_ name (option ...))
  76. #`(begin
  77. (define (#,(id-append #'name #'name #'?) x)
  78. (memq x '(option ...)))
  79. (define (#,(id-append #'name #'serialize- #'name) field-name val)
  80. (serialize-field field-name val)))))))
  81. (define-enumerated-field-type proto
  82. (udp tcp udp6 tcp6))
  83. (define-enumerated-field-type dev
  84. (tun tap))
  85. (define key-usage? boolean?)
  86. (define (serialize-key-usage field-name value)
  87. (if value
  88. (format #t "remote-cert-tls server\n")
  89. #f))
  90. (define bind? boolean?)
  91. (define (serialize-bind field-name value)
  92. (if value
  93. #f
  94. (format #t "nobind\n")))
  95. (define resolv-retry? boolean?)
  96. (define (serialize-resolv-retry field-name value)
  97. (if value
  98. (format #t "resolv-retry infinite\n")
  99. #f))
  100. (define (serialize-tls-auth role location)
  101. (if location
  102. (serialize-field 'tls-auth
  103. (string-append location " " (match role
  104. ('server "0")
  105. ('client "1"))))
  106. #f))
  107. (define (tls-auth? val)
  108. (or (eq? val #f)
  109. (string? val)))
  110. (define (serialize-tls-auth-server field-name val)
  111. (serialize-tls-auth 'server val))
  112. (define (serialize-tls-auth-client field-name val)
  113. (serialize-tls-auth 'client val))
  114. (define tls-auth-server? tls-auth?)
  115. (define tls-auth-client? tls-auth?)
  116. (define (serialize-number field-name val)
  117. (serialize-field field-name (number->string val)))
  118. (define (all-lte left right)
  119. (if (eq? left '())
  120. (eq? right '())
  121. (and (<= (string->number (car left)) (car right))
  122. (all-lte (cdr left) (cdr right)))))
  123. (define (cidr4? val)
  124. (if (string? val)
  125. (if (string-match "^([0-9]+\\.){3}[0-9]+/[0-9]+$" val)
  126. (let ((numbers (string-tokenize val char-set:digit)))
  127. (all-lte numbers (list 255 255 255 255 32)))
  128. #f)
  129. (eq? val #f)))
  130. (define (cidr6? val)
  131. (if (string? val)
  132. (string-match "^([0-9a-f]{0,4}:){0,8}/[0-9]{1,3}$" val)
  133. (eq? val #f)))
  134. (define (serialize-cidr4 field-name val)
  135. (if (eq? val #f) #f (serialize-field field-name val)))
  136. (define (serialize-cidr6 field-name val)
  137. (if (eq? val #f) #f (serialize-field field-name val)))
  138. (define (ip? val)
  139. (if (string? val)
  140. (if (string-match "^([0-9]+\\.){3}[0-9]+$" val)
  141. (let ((numbers (string-tokenize val char-set:digit)))
  142. (all-lte numbers (list 255 255 255 255)))
  143. #f)
  144. (eq? val #f)))
  145. (define (serialize-ip field-name val)
  146. (if (eq? val #f) #f (serialize-field field-name val)))
  147. (define (keepalive? val)
  148. (and (list? val)
  149. (and (number? (car val))
  150. (number? (car (cdr val))))))
  151. (define (serialize-keepalive field-name val)
  152. (format #t "~a ~a ~a\n" (uglify-field-name field-name)
  153. (number->string (car val)) (number->string (car (cdr val)))))
  154. (define gateway? boolean?)
  155. (define (serialize-gateway field-name val)
  156. (and val
  157. (format #t "push \"redirect-gateway\"\n")))
  158. (define-configuration openvpn-remote-configuration
  159. (name
  160. (string "my-server")
  161. "Server name.")
  162. (port
  163. (number 1194)
  164. "Port number the server listens to."))
  165. (define-configuration openvpn-ccd-configuration
  166. (name
  167. (string "client")
  168. "Client name.")
  169. (iroute
  170. (ip-mask #f)
  171. "Client own network")
  172. (ifconfig-push
  173. (ip-mask #f)
  174. "Client VPN IP."))
  175. (define (openvpn-remote-list? val)
  176. (and (list? val)
  177. (or (eq? val '())
  178. (and (openvpn-remote-configuration? (car val))
  179. (openvpn-remote-list? (cdr val))))))
  180. (define (serialize-openvpn-remote-list field-name val)
  181. (for-each (lambda (remote)
  182. (format #t "remote ~a ~a\n" (openvpn-remote-configuration-name remote)
  183. (number->string (openvpn-remote-configuration-port remote))))
  184. val))
  185. (define (openvpn-ccd-list? val)
  186. (and (list? val)
  187. (or (eq? val '())
  188. (and (openvpn-ccd-configuration? (car val))
  189. (openvpn-ccd-list? (cdr val))))))
  190. (define (serialize-openvpn-ccd-list field-name val)
  191. #f)
  192. (define (create-ccd-directory val)
  193. "Create a ccd directory containing files for the ccd configuration option
  194. of OpenVPN. Each file in this directory represents particular settings for a
  195. client. Each file is named after the name of the client."
  196. (let ((files (map (lambda (ccd)
  197. (list (openvpn-ccd-configuration-name ccd)
  198. (with-output-to-string
  199. (lambda ()
  200. (serialize-configuration
  201. ccd openvpn-ccd-configuration-fields)))))
  202. val)))
  203. (computed-file "ccd"
  204. (with-imported-modules '((guix build utils))
  205. #~(begin
  206. (use-modules (guix build utils))
  207. (use-modules (ice-9 match))
  208. (mkdir-p #$output)
  209. (for-each
  210. (lambda (ccd)
  211. (match ccd
  212. ((name config-string)
  213. (call-with-output-file
  214. (string-append #$output "/" name)
  215. (lambda (port) (display config-string port))))))
  216. '#$files))))))
  217. (define-syntax define-split-configuration
  218. (lambda (x)
  219. (syntax-case x ()
  220. ((_ name1 name2 (common-option ...) (first-option ...) (second-option ...))
  221. #`(begin
  222. (define-configuration #,#'name1
  223. common-option ...
  224. first-option ...)
  225. (define-configuration #,#'name2
  226. common-option ...
  227. second-option ...))))))
  228. (define-split-configuration openvpn-client-configuration
  229. openvpn-server-configuration
  230. ((openvpn
  231. (package openvpn)
  232. "The OpenVPN package.")
  233. (pid-file
  234. (string "/var/run/openvpn/openvpn.pid")
  235. "The OpenVPN pid file.")
  236. (proto
  237. (proto 'udp)
  238. "The protocol (UDP or TCP) used to open a channel between clients and
  239. servers.")
  240. (dev
  241. (dev 'tun)
  242. "The device type used to represent the VPN connection.")
  243. (ca
  244. (maybe-string "/etc/openvpn/ca.crt")
  245. "The certificate authority to check connections against.")
  246. (cert
  247. (maybe-string "/etc/openvpn/client.crt")
  248. "The certificate of the machine the daemon is running on. It should be signed
  249. by the authority given in @code{ca}.")
  250. (key
  251. (maybe-string "/etc/openvpn/client.key")
  252. "The key of the machine the daemon is running on. It must be the key whose
  253. certificate is @code{cert}.")
  254. (comp-lzo?
  255. (boolean #t)
  256. "Whether to use the lzo compression algorithm.")
  257. (persist-key?
  258. (boolean #t)
  259. "Don't re-read key files across SIGUSR1 or --ping-restart.")
  260. (persist-tun?
  261. (boolean #t)
  262. "Don't close and reopen TUN/TAP device or run up/down scripts across
  263. SIGUSR1 or --ping-restart restarts.")
  264. (fast-io?
  265. (boolean #f)
  266. "(Experimental) Optimize TUN/TAP/UDP I/O writes by avoiding a call to
  267. poll/epoll/select prior to the write operation.")
  268. (verbosity
  269. (number 3)
  270. "Verbosity level."))
  271. ;; client-specific configuration
  272. ((tls-auth
  273. (tls-auth-client #f)
  274. "Add an additional layer of HMAC authentication on top of the TLS control
  275. channel to protect against DoS attacks.")
  276. (auth-user-pass
  277. (maybe-string 'disabled)
  278. "Authenticate with server using username/password. The option is a file
  279. containing username/password on 2 lines. Do not use a file-like object as it
  280. would be added to the store and readable by any user.")
  281. (verify-key-usage?
  282. (key-usage #t)
  283. "Whether to check the server certificate has server usage extension.")
  284. (bind?
  285. (bind #f)
  286. "Bind to a specific local port number.")
  287. (resolv-retry?
  288. (resolv-retry #t)
  289. "Retry resolving server address.")
  290. (remote
  291. (openvpn-remote-list '())
  292. "A list of remote servers to connect to."))
  293. ;; server-specific configuration
  294. ((tls-auth
  295. (tls-auth-server #f)
  296. "Add an additional layer of HMAC authentication on top of the TLS control
  297. channel to protect against DoS attacks.")
  298. (port
  299. (number 1194)
  300. "Specifies the port number on which the server listens.")
  301. (server
  302. (ip-mask "10.8.0.0 255.255.255.0")
  303. "An ip and mask specifying the subnet inside the virtual network.")
  304. (server-ipv6
  305. (cidr6 #f)
  306. "A CIDR notation specifying the IPv6 subnet inside the virtual network.")
  307. (dh
  308. (string "/etc/openvpn/dh2048.pem")
  309. "The Diffie-Hellman parameters file.")
  310. (ifconfig-pool-persist
  311. (string "/etc/openvpn/ipp.txt")
  312. "The file that records client IPs.")
  313. (redirect-gateway?
  314. (gateway #f)
  315. "When true, the server will act as a gateway for its clients.")
  316. (client-to-client?
  317. (boolean #f)
  318. "When true, clients are allowed to talk to each other inside the VPN.")
  319. (keepalive
  320. (keepalive '(10 120))
  321. "Causes ping-like messages to be sent back and forth over the link so that
  322. each side knows when the other side has gone down. @code{keepalive} requires
  323. a pair. The first element is the period of the ping sending, and the second
  324. element is the timeout before considering the other side down.")
  325. (max-clients
  326. (number 100)
  327. "The maximum number of clients.")
  328. (status
  329. (string "/var/run/openvpn/status")
  330. "The status file. This file shows a small report on current connection. It
  331. is truncated and rewritten every minute.")
  332. (client-config-dir
  333. (openvpn-ccd-list '())
  334. "The list of configuration for some clients.")))
  335. (define (openvpn-config-file role config)
  336. (let ((config-str
  337. (with-output-to-string
  338. (lambda ()
  339. (serialize-configuration config
  340. (match role
  341. ('server
  342. openvpn-server-configuration-fields)
  343. ('client
  344. openvpn-client-configuration-fields))))))
  345. (ccd-dir (match role
  346. ('server (create-ccd-directory
  347. (openvpn-server-configuration-client-config-dir
  348. config)))
  349. ('client #f))))
  350. (computed-file "openvpn.conf"
  351. #~(begin
  352. (use-modules (ice-9 match))
  353. (call-with-output-file #$output
  354. (lambda (port)
  355. (match '#$role
  356. ('server (display "" port))
  357. ('client (display "client\n" port)))
  358. (display #$config-str port)
  359. (match '#$role
  360. ('server (display
  361. (string-append "client-config-dir "
  362. #$ccd-dir "\n") port))
  363. ('client (display "" port)))))))))
  364. (define (openvpn-shepherd-service role)
  365. (lambda (config)
  366. (let* ((config-file (openvpn-config-file role config))
  367. (pid-file ((match role
  368. ('server openvpn-server-configuration-pid-file)
  369. ('client openvpn-client-configuration-pid-file))
  370. config))
  371. (openvpn ((match role
  372. ('server openvpn-server-configuration-openvpn)
  373. ('client openvpn-client-configuration-openvpn))
  374. config))
  375. (log-file (match role
  376. ('server "/var/log/openvpn-server.log")
  377. ('client "/var/log/openvpn-client.log"))))
  378. (list (shepherd-service
  379. (documentation (string-append "Run the OpenVPN "
  380. (match role
  381. ('server "server")
  382. ('client "client"))
  383. " daemon."))
  384. (provision (match role
  385. ('server '(vpn-server))
  386. ('client '(vpn-client))))
  387. (requirement '(networking))
  388. (start #~(make-forkexec-constructor
  389. (list (string-append #$openvpn "/sbin/openvpn")
  390. "--writepid" #$pid-file "--config" #$config-file
  391. "--daemon")
  392. #:pid-file #$pid-file))
  393. (stop #~(make-kill-destructor)))))))
  394. (define %openvpn-accounts
  395. (list (user-group (name "openvpn") (system? #t))
  396. (user-account
  397. (name "openvpn")
  398. (group "openvpn")
  399. (system? #t)
  400. (comment "Openvpn daemon user")
  401. (home-directory "/var/empty")
  402. (shell (file-append shadow "/sbin/nologin")))))
  403. (define %openvpn-activation
  404. #~(begin
  405. (use-modules (guix build utils))
  406. (mkdir-p "/var/run/openvpn")))
  407. (define openvpn-server-service-type
  408. (service-type (name 'openvpn-server)
  409. (extensions
  410. (list (service-extension shepherd-root-service-type
  411. (openvpn-shepherd-service 'server))
  412. (service-extension account-service-type
  413. (const %openvpn-accounts))
  414. (service-extension activation-service-type
  415. (const %openvpn-activation))))))
  416. (define openvpn-client-service-type
  417. (service-type (name 'openvpn-client)
  418. (extensions
  419. (list (service-extension shepherd-root-service-type
  420. (openvpn-shepherd-service 'client))
  421. (service-extension account-service-type
  422. (const %openvpn-accounts))
  423. (service-extension activation-service-type
  424. (const %openvpn-activation))))))
  425. (define* (openvpn-client-service #:key (config (openvpn-client-configuration)))
  426. (validate-configuration config openvpn-client-configuration-fields)
  427. (service openvpn-client-service-type config))
  428. (define* (openvpn-server-service #:key (config (openvpn-server-configuration)))
  429. (validate-configuration config openvpn-server-configuration-fields)
  430. (service openvpn-server-service-type config))
  431. (define (generate-openvpn-server-documentation)
  432. (generate-documentation
  433. `((openvpn-server-configuration
  434. ,openvpn-server-configuration-fields
  435. (ccd openvpn-ccd-configuration))
  436. (openvpn-ccd-configuration ,openvpn-ccd-configuration-fields))
  437. 'openvpn-server-configuration))
  438. (define (generate-openvpn-client-documentation)
  439. (generate-documentation
  440. `((openvpn-client-configuration
  441. ,openvpn-client-configuration-fields
  442. (remote openvpn-remote-configuration))
  443. (openvpn-remote-configuration ,openvpn-remote-configuration-fields))
  444. 'openvpn-client-configuration))