vpn.scm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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 (serialize-boolean field-name val)
  59. (if val
  60. (serialize-field field-name val)
  61. (format #t "")))
  62. (define (ip-mask? val)
  63. (and (string? val)
  64. (if (string-match "^([0-9]+\\.){3}[0-9]+ ([0-9]+\\.){3}[0-9]+$" val)
  65. (let ((numbers (string-tokenize val char-set:digit)))
  66. (all-lte numbers (list 255 255 255 255 255 255 255 255)))
  67. #f)))
  68. (define serialize-ip-mask serialize-string)
  69. (define-syntax define-enumerated-field-type
  70. (lambda (x)
  71. (define (id-append ctx . parts)
  72. (datum->syntax ctx (apply symbol-append (map syntax->datum parts))))
  73. (syntax-case x ()
  74. ((_ name (option ...))
  75. #`(begin
  76. (define (#,(id-append #'name #'name #'?) x)
  77. (memq x '(option ...)))
  78. (define (#,(id-append #'name #'serialize- #'name) field-name val)
  79. (serialize-field field-name val)))))))
  80. (define-enumerated-field-type proto
  81. (udp tcp udp6 tcp6))
  82. (define-enumerated-field-type dev
  83. (tun tap))
  84. (define key-usage? boolean?)
  85. (define (serialize-key-usage field-name value)
  86. (if value
  87. (format #t "remote-cert-tls server\n")
  88. #f))
  89. (define bind? boolean?)
  90. (define (serialize-bind field-name value)
  91. (if value
  92. #f
  93. (format #t "nobind\n")))
  94. (define resolv-retry? boolean?)
  95. (define (serialize-resolv-retry field-name value)
  96. (if value
  97. (format #t "resolv-retry infinite\n")
  98. #f))
  99. (define (serialize-tls-auth role location)
  100. (serialize-field 'tls-auth
  101. (string-append location " " (match role
  102. ('server "0")
  103. ('client "1")))))
  104. (define (tls-auth? val)
  105. (or (eq? val #f)
  106. (string? val)))
  107. (define (serialize-tls-auth-server field-name val)
  108. (serialize-tls-auth 'server val))
  109. (define (serialize-tls-auth-client field-name val)
  110. (serialize-tls-auth 'client val))
  111. (define tls-auth-server? tls-auth?)
  112. (define tls-auth-client? tls-auth?)
  113. (define (serialize-number field-name val)
  114. (serialize-field field-name (number->string val)))
  115. (define (all-lte left right)
  116. (if (eq? left '())
  117. (eq? right '())
  118. (and (<= (string->number (car left)) (car right))
  119. (all-lte (cdr left) (cdr right)))))
  120. (define (cidr4? val)
  121. (if (string? val)
  122. (if (string-match "^([0-9]+\\.){3}[0-9]+/[0-9]+$" val)
  123. (let ((numbers (string-tokenize val char-set:digit)))
  124. (all-lte numbers (list 255 255 255 255 32)))
  125. #f)
  126. (eq? val #f)))
  127. (define (cidr6? val)
  128. (if (string? val)
  129. (string-match "^([0-9a-f]{0,4}:){0,8}/[0-9]{1,3}$" val)
  130. (eq? val #f)))
  131. (define (serialize-cidr4 field-name val)
  132. (if (eq? val #f) #f (serialize-field field-name val)))
  133. (define (serialize-cidr6 field-name val)
  134. (if (eq? val #f) #f (serialize-field field-name val)))
  135. (define (ip? val)
  136. (if (string? val)
  137. (if (string-match "^([0-9]+\\.){3}[0-9]+$" val)
  138. (let ((numbers (string-tokenize val char-set:digit)))
  139. (all-lte numbers (list 255 255 255 255)))
  140. #f)
  141. (eq? val #f)))
  142. (define (serialize-ip field-name val)
  143. (if (eq? val #f) #f (serialize-field field-name val)))
  144. (define (keepalive? val)
  145. (and (list? val)
  146. (and (number? (car val))
  147. (number? (car (cdr val))))))
  148. (define (serialize-keepalive field-name val)
  149. (format #t "~a ~a ~a\n" (uglify-field-name field-name)
  150. (number->string (car val)) (number->string (car (cdr val)))))
  151. (define gateway? boolean?)
  152. (define (serialize-gateway field-name val)
  153. (and val
  154. (format #t "push \"redirect-gateway\"\n")))
  155. (define-configuration openvpn-remote-configuration
  156. (name
  157. (string "my-server")
  158. "Server name.")
  159. (port
  160. (number 1194)
  161. "Port number the server listens to."))
  162. (define-configuration openvpn-ccd-configuration
  163. (name
  164. (string "client")
  165. "Client name.")
  166. (iroute
  167. (ip-mask #f)
  168. "Client own network")
  169. (ifconfig-push
  170. (ip-mask #f)
  171. "Client VPN IP."))
  172. (define (openvpn-remote-list? val)
  173. (and (list? val)
  174. (or (eq? val '())
  175. (and (openvpn-remote-configuration? (car val))
  176. (openvpn-remote-list? (cdr val))))))
  177. (define (serialize-openvpn-remote-list field-name val)
  178. (for-each (lambda (remote)
  179. (format #t "remote ~a ~a\n" (openvpn-remote-configuration-name remote)
  180. (number->string (openvpn-remote-configuration-port remote))))
  181. val))
  182. (define (openvpn-ccd-list? val)
  183. (and (list? val)
  184. (or (eq? val '())
  185. (and (openvpn-ccd-configuration? (car val))
  186. (openvpn-ccd-list? (cdr val))))))
  187. (define (serialize-openvpn-ccd-list field-name val)
  188. #f)
  189. (define (create-ccd-directory val)
  190. "Create a ccd directory containing files for the ccd configuration option
  191. of OpenVPN. Each file in this directory represents particular settings for a
  192. client. Each file is named after the name of the client."
  193. (let ((files (map (lambda (ccd)
  194. (list (openvpn-ccd-configuration-name ccd)
  195. (with-output-to-string
  196. (lambda ()
  197. (serialize-configuration
  198. ccd openvpn-ccd-configuration-fields)))))
  199. val)))
  200. (computed-file "ccd"
  201. (with-imported-modules '((guix build utils))
  202. #~(begin
  203. (use-modules (guix build utils))
  204. (use-modules (ice-9 match))
  205. (mkdir-p #$output)
  206. (for-each
  207. (lambda (ccd)
  208. (match ccd
  209. ((name config-string)
  210. (call-with-output-file
  211. (string-append #$output "/" name)
  212. (lambda (port) (display config-string port))))))
  213. '#$files))))))
  214. (define-syntax define-split-configuration
  215. (lambda (x)
  216. (syntax-case x ()
  217. ((_ name1 name2 (common-option ...) (first-option ...) (second-option ...))
  218. #`(begin
  219. (define-configuration #,#'name1
  220. common-option ...
  221. first-option ...)
  222. (define-configuration #,#'name2
  223. common-option ...
  224. second-option ...))))))
  225. (define-split-configuration openvpn-client-configuration
  226. openvpn-server-configuration
  227. ((openvpn
  228. (package openvpn)
  229. "The OpenVPN package.")
  230. (pid-file
  231. (string "/var/run/openvpn/openvpn.pid")
  232. "The OpenVPN pid file.")
  233. (proto
  234. (proto 'udp)
  235. "The protocol (UDP or TCP) used to open a channel between clients and
  236. servers.")
  237. (dev
  238. (dev 'tun)
  239. "The device type used to represent the VPN connection.")
  240. (ca
  241. (string "/etc/openvpn/ca.crt")
  242. "The certificate authority to check connections against.")
  243. (cert
  244. (string "/etc/openvpn/client.crt")
  245. "The certificate of the machine the daemon is running on. It should be signed
  246. by the authority given in @code{ca}.")
  247. (key
  248. (string "/etc/openvpn/client.key")
  249. "The key of the machine the daemon is running on. It must be the key whose
  250. certificate is @code{cert}.")
  251. (comp-lzo?
  252. (boolean #t)
  253. "Whether to use the lzo compression algorithm.")
  254. (persist-key?
  255. (boolean #t)
  256. "Don't re-read key files across SIGUSR1 or --ping-restart.")
  257. (persist-tun?
  258. (boolean #t)
  259. "Don't close and reopen TUN/TAP device or run up/down scripts across
  260. SIGUSR1 or --ping-restart restarts.")
  261. (verbosity
  262. (number 3)
  263. "Verbosity level."))
  264. ;; client-specific configuration
  265. ((tls-auth
  266. (tls-auth-client #f)
  267. "Add an additional layer of HMAC authentication on top of the TLS control
  268. channel to protect against DoS attacks.")
  269. (verify-key-usage?
  270. (key-usage #t)
  271. "Whether to check the server certificate has server usage extension.")
  272. (bind?
  273. (bind #f)
  274. "Bind to a specific local port number.")
  275. (resolv-retry?
  276. (resolv-retry #t)
  277. "Retry resolving server address.")
  278. (remote
  279. (openvpn-remote-list '())
  280. "A list of remote servers to connect to."))
  281. ;; server-specific configuration
  282. ((tls-auth
  283. (tls-auth-server #f)
  284. "Add an additional layer of HMAC authentication on top of the TLS control
  285. channel to protect against DoS attacks.")
  286. (port
  287. (number 1194)
  288. "Specifies the port number on which the server listens.")
  289. (server
  290. (ip-mask "10.8.0.0 255.255.255.0")
  291. "An ip and mask specifying the subnet inside the virtual network.")
  292. (server-ipv6
  293. (cidr6 #f)
  294. "A CIDR notation specifying the IPv6 subnet inside the virtual network.")
  295. (dh
  296. (string "/etc/openvpn/dh2048.pem")
  297. "The Diffie-Hellman parameters file.")
  298. (ifconfig-pool-persist
  299. (string "/etc/openvpn/ipp.txt")
  300. "The file that records client IPs.")
  301. (redirect-gateway?
  302. (gateway #f)
  303. "When true, the server will act as a gateway for its clients.")
  304. (client-to-client?
  305. (boolean #f)
  306. "When true, clients are allowed to talk to each other inside the VPN.")
  307. (keepalive
  308. (keepalive '(10 120))
  309. "Causes ping-like messages to be sent back and forth over the link so that
  310. each side knows when the other side has gone down. @code{keepalive} requires
  311. a pair. The first element is the period of the ping sending, and the second
  312. element is the timeout before considering the other side down.")
  313. (max-clients
  314. (number 100)
  315. "The maximum number of clients.")
  316. (status
  317. (string "/var/run/openvpn/status")
  318. "The status file. This file shows a small report on current connection. It
  319. is truncated and rewritten every minute.")
  320. (client-config-dir
  321. (openvpn-ccd-list '())
  322. "The list of configuration for some clients.")))
  323. (define (openvpn-config-file role config)
  324. (let ((config-str
  325. (with-output-to-string
  326. (lambda ()
  327. (serialize-configuration config
  328. (match role
  329. ('server
  330. openvpn-server-configuration-fields)
  331. ('client
  332. openvpn-client-configuration-fields))))))
  333. (ccd-dir (match role
  334. ('server (create-ccd-directory
  335. (openvpn-server-configuration-client-config-dir
  336. config)))
  337. ('client #f))))
  338. (computed-file "openvpn.conf"
  339. #~(begin
  340. (use-modules (ice-9 match))
  341. (call-with-output-file #$output
  342. (lambda (port)
  343. (match '#$role
  344. ('server (display "" port))
  345. ('client (display "client\n" port)))
  346. (display #$config-str port)
  347. (match '#$role
  348. ('server (display
  349. (string-append "client-config-dir "
  350. #$ccd-dir "\n") port))
  351. ('client (display "" port)))))))))
  352. (define (openvpn-shepherd-service role)
  353. (lambda (config)
  354. (let* ((config-file (openvpn-config-file role config))
  355. (pid-file ((match role
  356. ('server openvpn-server-configuration-pid-file)
  357. ('client openvpn-client-configuration-pid-file))
  358. config))
  359. (openvpn ((match role
  360. ('server openvpn-server-configuration-openvpn)
  361. ('client openvpn-client-configuration-openvpn))
  362. config))
  363. (log-file (match role
  364. ('server "/var/log/openvpn-server.log")
  365. ('client "/var/log/openvpn-client.log"))))
  366. (list (shepherd-service
  367. (documentation (string-append "Run the OpenVPN "
  368. (match role
  369. ('server "server")
  370. ('client "client"))
  371. " daemon."))
  372. (provision (match role
  373. ('server '(vpn-server))
  374. ('client '(vpn-client))))
  375. (requirement '(networking))
  376. (start #~(make-forkexec-constructor
  377. (list (string-append #$openvpn "/sbin/openvpn")
  378. "--writepid" #$pid-file "--config" #$config-file
  379. "--daemon")
  380. #:pid-file #$pid-file))
  381. (stop #~(make-kill-destructor)))))))
  382. (define %openvpn-accounts
  383. (list (user-group (name "openvpn") (system? #t))
  384. (user-account
  385. (name "openvpn")
  386. (group "openvpn")
  387. (system? #t)
  388. (comment "Openvpn daemon user")
  389. (home-directory "/var/empty")
  390. (shell (file-append shadow "/sbin/nologin")))))
  391. (define %openvpn-activation
  392. #~(begin
  393. (use-modules (guix build utils))
  394. (mkdir-p "/var/run/openvpn")))
  395. (define openvpn-server-service-type
  396. (service-type (name 'openvpn-server)
  397. (extensions
  398. (list (service-extension shepherd-root-service-type
  399. (openvpn-shepherd-service 'server))
  400. (service-extension account-service-type
  401. (const %openvpn-accounts))
  402. (service-extension activation-service-type
  403. (const %openvpn-activation))))))
  404. (define openvpn-client-service-type
  405. (service-type (name 'openvpn-client)
  406. (extensions
  407. (list (service-extension shepherd-root-service-type
  408. (openvpn-shepherd-service 'client))
  409. (service-extension account-service-type
  410. (const %openvpn-accounts))
  411. (service-extension activation-service-type
  412. (const %openvpn-activation))))))
  413. (define* (openvpn-client-service #:key (config (openvpn-client-configuration)))
  414. (validate-configuration config openvpn-client-configuration-fields)
  415. (service openvpn-client-service-type config))
  416. (define* (openvpn-server-service #:key (config (openvpn-server-configuration)))
  417. (validate-configuration config openvpn-server-configuration-fields)
  418. (service openvpn-server-service-type config))
  419. (define (generate-openvpn-server-documentation)
  420. (generate-documentation
  421. `((openvpn-server-configuration
  422. ,openvpn-server-configuration-fields
  423. (ccd openvpn-ccd-configuration))
  424. (openvpn-ccd-configuration ,openvpn-ccd-configuration-fields))
  425. 'openvpn-server-configuration))
  426. (define (generate-openvpn-client-documentation)
  427. (generate-documentation
  428. `((openvpn-client-configuration
  429. ,openvpn-client-configuration-fields
  430. (remote openvpn-remote-configuration))
  431. (openvpn-remote-configuration ,openvpn-remote-configuration-fields))
  432. 'openvpn-client-configuration))