vpn.scm 17 KB

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