vpn.scm 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  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. ;;; Copyright © 2021 Guillaume Le Vaillant <glv@posteo.net>
  6. ;;; Copyright © 2021 Solene Rapenne <solene@perso.pw>
  7. ;;; Copyright © 2021 Domagoj Stolfa <ds815@gmx.com>
  8. ;;; Copyright © 2021 Tobias Geerinckx-Rice <me@tobias.gr>
  9. ;;; Copyright © 2021 Raghav Gururajan <rg@raghavgururajan.name>
  10. ;;; Copyright © 2021 jgart <jgart@dismail.de>
  11. ;;; Copyright © 2021 Nathan Dehnel <ncdehnel@gmail.com>
  12. ;;; Copyright © 2022 Cameron V Chaparro <cameron@cameronchaparro.com>
  13. ;;; Copyright © 2022 Timo Wilken <guix@twilken.net>
  14. ;;; Copyright © 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
  15. ;;;
  16. ;;; This file is part of GNU Guix.
  17. ;;;
  18. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  19. ;;; under the terms of the GNU General Public License as published by
  20. ;;; the Free Software Foundation; either version 3 of the License, or (at
  21. ;;; your option) any later version.
  22. ;;;
  23. ;;; GNU Guix is distributed in the hope that it will be useful, but
  24. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  25. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. ;;; GNU General Public License for more details.
  27. ;;;
  28. ;;; You should have received a copy of the GNU General Public License
  29. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  30. (define-module (gnu services vpn)
  31. #:use-module (gnu services)
  32. #:use-module (gnu services configuration)
  33. #:use-module (gnu services dbus)
  34. #:use-module (gnu services mcron)
  35. #:use-module (gnu services shepherd)
  36. #:use-module (gnu system shadow)
  37. #:use-module (gnu packages admin)
  38. #:use-module (gnu packages vpn)
  39. #:use-module (guix modules)
  40. #:use-module (guix packages)
  41. #:use-module (guix records)
  42. #:use-module (guix gexp)
  43. #:use-module (guix i18n)
  44. #:use-module (guix deprecation)
  45. #:use-module (srfi srfi-1)
  46. #:use-module (ice-9 format)
  47. #:use-module (ice-9 match)
  48. #:use-module (ice-9 regex)
  49. #:export (openvpn-client-service ; deprecated
  50. openvpn-server-service ; deprecated
  51. openvpn-client-service-type
  52. openvpn-server-service-type
  53. openvpn-client-configuration
  54. openvpn-server-configuration
  55. openvpn-remote-configuration
  56. openvpn-ccd-configuration
  57. generate-openvpn-client-documentation
  58. generate-openvpn-server-documentation
  59. strongswan-configuration
  60. strongswan-service-type
  61. wireguard-peer
  62. wireguard-peer?
  63. wireguard-peer-name
  64. wireguard-peer-endpoint
  65. wireguard-peer-allowed-ips
  66. wireguard-peer-public-key
  67. wireguard-peer-preshared-key
  68. wireguard-peer-keep-alive
  69. wireguard-configuration
  70. wireguard-configuration?
  71. wireguard-configuration-wireguard
  72. wireguard-configuration-interface
  73. wireguard-configuration-addresses
  74. wireguard-configuration-port
  75. wireguard-configuration-dns
  76. wireguard-configuration-monitor-ips?
  77. wireguard-configuration-monitor-ips-interval
  78. wireguard-configuration-private-key
  79. wireguard-configuration-peers
  80. wireguard-configuration-pre-up
  81. wireguard-configuration-post-up
  82. wireguard-configuration-pre-down
  83. wireguard-configuration-post-down
  84. wireguard-configuration-table
  85. wireguard-service-type))
  86. ;;;
  87. ;;; Bitmask.
  88. ;;;
  89. (define-public bitmask-service-type
  90. (service-type
  91. (name 'bitmask)
  92. (description "Setup the @uref{https://bitmask.net, Bitmask} VPN application.")
  93. (default-value bitmask)
  94. (extensions
  95. (list
  96. ;; Add bitmask to the system profile.
  97. (service-extension profile-service-type list)
  98. ;; Configure polkit policy of bitmask.
  99. (service-extension polkit-service-type list)))))
  100. ;;;
  101. ;;; OpenVPN.
  102. ;;;
  103. (define (uglify-field-name name)
  104. (match name
  105. ('verbosity "verb")
  106. (_ (let ((str (symbol->string name)))
  107. (if (string-suffix? "?" str)
  108. (substring str 0 (1- (string-length str)))
  109. str)))))
  110. (define (serialize-field field-name val)
  111. (if (eq? field-name 'pid-file)
  112. (format #t "")
  113. (format #t "~a ~a\n" (uglify-field-name field-name) val)))
  114. (define serialize-string serialize-field)
  115. (define-maybe string)
  116. (define (serialize-boolean field-name val)
  117. (if val
  118. (serialize-field field-name "")
  119. (format #t "")))
  120. (define (ip-mask? val)
  121. (and (string? val)
  122. (if (string-match "^([0-9]+\\.){3}[0-9]+ ([0-9]+\\.){3}[0-9]+$" val)
  123. (let ((numbers (string-tokenize val char-set:digit)))
  124. (all-lte numbers (list 255 255 255 255 255 255 255 255)))
  125. #f)))
  126. (define serialize-ip-mask serialize-string)
  127. (define-syntax define-enumerated-field-type
  128. (lambda (x)
  129. (define (id-append ctx . parts)
  130. (datum->syntax ctx (apply symbol-append (map syntax->datum parts))))
  131. (syntax-case x ()
  132. ((_ name (option ...))
  133. #`(begin
  134. (define (#,(id-append #'name #'name #'?) x)
  135. (memq x '(option ...)))
  136. (define (#,(id-append #'name #'serialize- #'name) field-name val)
  137. (serialize-field field-name val)))))))
  138. (define-enumerated-field-type proto
  139. (udp tcp udp6 tcp6))
  140. (define-enumerated-field-type dev
  141. (tun tap))
  142. (define key-usage? boolean?)
  143. (define (serialize-key-usage field-name value)
  144. (if value
  145. (format #t "remote-cert-tls server\n")
  146. #f))
  147. (define bind? boolean?)
  148. (define (serialize-bind field-name value)
  149. (if value
  150. #f
  151. (format #t "nobind\n")))
  152. (define resolv-retry? boolean?)
  153. (define (serialize-resolv-retry field-name value)
  154. (if value
  155. (format #t "resolv-retry infinite\n")
  156. #f))
  157. (define (serialize-tls-auth role location)
  158. (if location
  159. (serialize-field 'tls-auth
  160. (string-append location " " (match role
  161. ('server "0")
  162. ('client "1"))))
  163. #f))
  164. (define (tls-auth? val)
  165. (or (eq? val #f)
  166. (string? val)))
  167. (define (serialize-tls-auth-server field-name val)
  168. (serialize-tls-auth 'server val))
  169. (define (serialize-tls-auth-client field-name val)
  170. (serialize-tls-auth 'client val))
  171. (define tls-auth-server? tls-auth?)
  172. (define tls-auth-client? tls-auth?)
  173. (define (serialize-number field-name val)
  174. (serialize-field field-name (number->string val)))
  175. (define (all-lte left right)
  176. (if (eq? left '())
  177. (eq? right '())
  178. (and (<= (string->number (car left)) (car right))
  179. (all-lte (cdr left) (cdr right)))))
  180. (define (cidr4? val)
  181. (if (string? val)
  182. (if (string-match "^([0-9]+\\.){3}[0-9]+/[0-9]+$" val)
  183. (let ((numbers (string-tokenize val char-set:digit)))
  184. (all-lte numbers (list 255 255 255 255 32)))
  185. #f)
  186. (eq? val #f)))
  187. (define (cidr6? val)
  188. (if (string? val)
  189. (string-match "^([0-9a-f]{0,4}:){0,8}/[0-9]{1,3}$" val)
  190. (eq? val #f)))
  191. (define (serialize-cidr4 field-name val)
  192. (if (eq? val #f) #f (serialize-field field-name val)))
  193. (define (serialize-cidr6 field-name val)
  194. (if (eq? val #f) #f (serialize-field field-name val)))
  195. (define (ip? val)
  196. (if (string? val)
  197. (if (string-match "^([0-9]+\\.){3}[0-9]+$" val)
  198. (let ((numbers (string-tokenize val char-set:digit)))
  199. (all-lte numbers (list 255 255 255 255)))
  200. #f)
  201. (eq? val #f)))
  202. (define (serialize-ip field-name val)
  203. (if (eq? val #f) #f (serialize-field field-name val)))
  204. (define (keepalive? val)
  205. (and (list? val)
  206. (and (number? (car val))
  207. (number? (car (cdr val))))))
  208. (define (serialize-keepalive field-name val)
  209. (format #t "~a ~a ~a\n" (uglify-field-name field-name)
  210. (number->string (car val)) (number->string (car (cdr val)))))
  211. (define gateway? boolean?)
  212. (define (serialize-gateway field-name val)
  213. (and val
  214. (format #t "push \"redirect-gateway\"\n")))
  215. (define-configuration openvpn-remote-configuration
  216. (name
  217. (string "my-server")
  218. "Server name.")
  219. (port
  220. (number 1194)
  221. "Port number the server listens to."))
  222. (define-configuration openvpn-ccd-configuration
  223. (name
  224. (string "client")
  225. "Client name.")
  226. (iroute
  227. (ip-mask #f)
  228. "Client own network")
  229. (ifconfig-push
  230. (ip-mask #f)
  231. "Client VPN IP."))
  232. (define (openvpn-remote-list? val)
  233. (and (list? val)
  234. (or (eq? val '())
  235. (and (openvpn-remote-configuration? (car val))
  236. (openvpn-remote-list? (cdr val))))))
  237. (define (serialize-openvpn-remote-list field-name val)
  238. (for-each (lambda (remote)
  239. (format #t "remote ~a ~a\n" (openvpn-remote-configuration-name remote)
  240. (number->string (openvpn-remote-configuration-port remote))))
  241. val))
  242. (define (openvpn-ccd-list? val)
  243. (and (list? val)
  244. (or (eq? val '())
  245. (and (openvpn-ccd-configuration? (car val))
  246. (openvpn-ccd-list? (cdr val))))))
  247. (define (serialize-openvpn-ccd-list field-name val)
  248. #f)
  249. (define (create-ccd-directory val)
  250. "Create a ccd directory containing files for the ccd configuration option
  251. of OpenVPN. Each file in this directory represents particular settings for a
  252. client. Each file is named after the name of the client."
  253. (let ((files (map (lambda (ccd)
  254. (list (openvpn-ccd-configuration-name ccd)
  255. (with-output-to-string
  256. (lambda ()
  257. (serialize-configuration
  258. ccd openvpn-ccd-configuration-fields)))))
  259. val)))
  260. (computed-file "ccd"
  261. (with-imported-modules '((guix build utils))
  262. #~(begin
  263. (use-modules (guix build utils))
  264. (use-modules (ice-9 match))
  265. (mkdir-p #$output)
  266. (for-each
  267. (lambda (ccd)
  268. (match ccd
  269. ((name config-string)
  270. (call-with-output-file
  271. (string-append #$output "/" name)
  272. (lambda (port) (display config-string port))))))
  273. '#$files))))))
  274. (define-syntax define-split-configuration
  275. (lambda (x)
  276. (syntax-case x ()
  277. ((_ name1 name2 (common-option ...) (first-option ...) (second-option ...))
  278. #`(begin
  279. (define-configuration #,#'name1
  280. common-option ...
  281. first-option ...)
  282. (define-configuration #,#'name2
  283. common-option ...
  284. second-option ...))))))
  285. (define-split-configuration openvpn-client-configuration
  286. openvpn-server-configuration
  287. ((openvpn
  288. (file-like openvpn)
  289. "The OpenVPN package.")
  290. (pid-file
  291. (string "/var/run/openvpn/openvpn.pid")
  292. "The OpenVPN pid file.")
  293. (proto
  294. (proto 'udp)
  295. "The protocol (UDP or TCP) used to open a channel between clients and
  296. servers.")
  297. (dev
  298. (dev 'tun)
  299. "The device type used to represent the VPN connection.")
  300. (ca
  301. (maybe-string "/etc/openvpn/ca.crt")
  302. "The certificate authority to check connections against.")
  303. (cert
  304. (maybe-string "/etc/openvpn/client.crt")
  305. "The certificate of the machine the daemon is running on. It should be signed
  306. by the authority given in @code{ca}.")
  307. (key
  308. (maybe-string "/etc/openvpn/client.key")
  309. "The key of the machine the daemon is running on. It must be the key whose
  310. certificate is @code{cert}.")
  311. (comp-lzo?
  312. (boolean #t)
  313. "Whether to use the lzo compression algorithm.")
  314. (persist-key?
  315. (boolean #t)
  316. "Don't re-read key files across SIGUSR1 or --ping-restart.")
  317. (persist-tun?
  318. (boolean #t)
  319. "Don't close and reopen TUN/TAP device or run up/down scripts across
  320. SIGUSR1 or --ping-restart restarts.")
  321. (fast-io?
  322. (boolean #f)
  323. "(Experimental) Optimize TUN/TAP/UDP I/O writes by avoiding a call to
  324. poll/epoll/select prior to the write operation.")
  325. (verbosity
  326. (number 3)
  327. "Verbosity level."))
  328. ;; client-specific configuration
  329. ((tls-auth
  330. (tls-auth-client #f)
  331. "Add an additional layer of HMAC authentication on top of the TLS control
  332. channel to protect against DoS attacks.")
  333. (auth-user-pass
  334. maybe-string
  335. "Authenticate with server using username/password. The option is a file
  336. containing username/password on 2 lines. Do not use a file-like object as it
  337. would be added to the store and readable by any user.")
  338. (verify-key-usage?
  339. (key-usage #t)
  340. "Whether to check the server certificate has server usage extension.")
  341. (bind?
  342. (bind #f)
  343. "Bind to a specific local port number.")
  344. (resolv-retry?
  345. (resolv-retry #t)
  346. "Retry resolving server address.")
  347. (remote
  348. (openvpn-remote-list '())
  349. "A list of remote servers to connect to."))
  350. ;; server-specific configuration
  351. ((tls-auth
  352. (tls-auth-server #f)
  353. "Add an additional layer of HMAC authentication on top of the TLS control
  354. channel to protect against DoS attacks.")
  355. (port
  356. (number 1194)
  357. "Specifies the port number on which the server listens.")
  358. (server
  359. (ip-mask "10.8.0.0 255.255.255.0")
  360. "An ip and mask specifying the subnet inside the virtual network.")
  361. (server-ipv6
  362. (cidr6 #f)
  363. "A CIDR notation specifying the IPv6 subnet inside the virtual network.")
  364. (dh
  365. (string "/etc/openvpn/dh2048.pem")
  366. "The Diffie-Hellman parameters file.")
  367. (ifconfig-pool-persist
  368. (string "/etc/openvpn/ipp.txt")
  369. "The file that records client IPs.")
  370. (redirect-gateway?
  371. (gateway #f)
  372. "When true, the server will act as a gateway for its clients.")
  373. (client-to-client?
  374. (boolean #f)
  375. "When true, clients are allowed to talk to each other inside the VPN.")
  376. (keepalive
  377. (keepalive '(10 120))
  378. "Causes ping-like messages to be sent back and forth over the link so that
  379. each side knows when the other side has gone down. @code{keepalive} requires
  380. a pair. The first element is the period of the ping sending, and the second
  381. element is the timeout before considering the other side down.")
  382. (max-clients
  383. (number 100)
  384. "The maximum number of clients.")
  385. (status
  386. (string "/var/run/openvpn/status")
  387. "The status file. This file shows a small report on current connection. It
  388. is truncated and rewritten every minute.")
  389. (client-config-dir
  390. (openvpn-ccd-list '())
  391. "The list of configuration for some clients.")))
  392. (define (openvpn-config-file role config)
  393. (let ((config-str
  394. (with-output-to-string
  395. (lambda ()
  396. (serialize-configuration config
  397. (match role
  398. ('server
  399. openvpn-server-configuration-fields)
  400. ('client
  401. openvpn-client-configuration-fields))))))
  402. (ccd-dir (match role
  403. ('server (create-ccd-directory
  404. (openvpn-server-configuration-client-config-dir
  405. config)))
  406. ('client #f))))
  407. (computed-file "openvpn.conf"
  408. #~(begin
  409. (use-modules (ice-9 match))
  410. (call-with-output-file #$output
  411. (lambda (port)
  412. (match '#$role
  413. ('server (display "" port))
  414. ('client (display "client\n" port)))
  415. (display #$config-str port)
  416. (match '#$role
  417. ('server (display
  418. (string-append "client-config-dir "
  419. #$ccd-dir "\n") port))
  420. ('client (display "" port)))))))))
  421. (define (openvpn-shepherd-service role)
  422. (lambda (config)
  423. (let* ((config-file (openvpn-config-file role config))
  424. (pid-file ((match role
  425. ('server openvpn-server-configuration-pid-file)
  426. ('client openvpn-client-configuration-pid-file))
  427. config))
  428. (openvpn ((match role
  429. ('server openvpn-server-configuration-openvpn)
  430. ('client openvpn-client-configuration-openvpn))
  431. config))
  432. (log-file (match role
  433. ('server "/var/log/openvpn-server.log")
  434. ('client "/var/log/openvpn-client.log"))))
  435. (list (shepherd-service
  436. (documentation (string-append "Run the OpenVPN "
  437. (match role
  438. ('server "server")
  439. ('client "client"))
  440. " daemon."))
  441. (provision (match role
  442. ('server '(vpn-server))
  443. ('client '(vpn-client))))
  444. (requirement '(networking))
  445. (start #~(make-forkexec-constructor
  446. (list (string-append #$openvpn "/sbin/openvpn")
  447. "--writepid" #$pid-file "--config" #$config-file
  448. "--daemon")
  449. #:pid-file #$pid-file
  450. #:log-file #$log-file))
  451. (stop #~(make-kill-destructor)))))))
  452. (define %openvpn-accounts
  453. (list (user-group (name "openvpn") (system? #t))
  454. (user-account
  455. (name "openvpn")
  456. (group "openvpn")
  457. (system? #t)
  458. (comment "Openvpn daemon user")
  459. (home-directory "/var/empty")
  460. (shell (file-append shadow "/sbin/nologin")))))
  461. (define %openvpn-activation
  462. #~(begin
  463. (use-modules (guix build utils))
  464. (mkdir-p "/var/run/openvpn")))
  465. (define openvpn-server-service-type
  466. (service-type (name 'openvpn-server)
  467. (extensions
  468. (list (service-extension shepherd-root-service-type
  469. (openvpn-shepherd-service 'server))
  470. (service-extension account-service-type
  471. (const %openvpn-accounts))
  472. (service-extension activation-service-type
  473. (const %openvpn-activation))))
  474. (description "Run the OpenVPN server, which allows you to
  475. @emph{host} a @acronym{VPN, virtual private network}.")
  476. (default-value (openvpn-server-configuration))))
  477. (define openvpn-client-service-type
  478. (service-type (name 'openvpn-client)
  479. (extensions
  480. (list (service-extension shepherd-root-service-type
  481. (openvpn-shepherd-service 'client))
  482. (service-extension account-service-type
  483. (const %openvpn-accounts))
  484. (service-extension activation-service-type
  485. (const %openvpn-activation))))
  486. (description
  487. "Run the OpenVPN client service, which allows you to connect
  488. to an existing @acronym{VPN, virtual private network}.")
  489. (default-value (openvpn-client-configuration))))
  490. (define-deprecated
  491. (openvpn-client-service #:key (config (openvpn-client-configuration)))
  492. openvpn-client-service-type
  493. (service openvpn-client-service-type config))
  494. (define-deprecated
  495. (openvpn-server-service #:key (config (openvpn-server-configuration)))
  496. openvpn-server-service-type
  497. (service openvpn-server-service-type config))
  498. (define (generate-openvpn-server-documentation)
  499. (generate-documentation
  500. `((openvpn-server-configuration
  501. ,openvpn-server-configuration-fields
  502. (ccd openvpn-ccd-configuration))
  503. (openvpn-ccd-configuration ,openvpn-ccd-configuration-fields))
  504. 'openvpn-server-configuration))
  505. (define (generate-openvpn-client-documentation)
  506. (generate-documentation
  507. `((openvpn-client-configuration
  508. ,openvpn-client-configuration-fields
  509. (remote openvpn-remote-configuration))
  510. (openvpn-remote-configuration ,openvpn-remote-configuration-fields))
  511. 'openvpn-client-configuration))
  512. ;;;
  513. ;;; Strongswan.
  514. ;;;
  515. (define-record-type* <strongswan-configuration>
  516. strongswan-configuration make-strongswan-configuration
  517. strongswan-configuration?
  518. (strongswan strongswan-configuration-strongswan ;file-like
  519. (default strongswan))
  520. (ipsec-conf strongswan-configuration-ipsec-conf ;string|#f
  521. (default #f))
  522. (ipsec-secrets strongswan-configuration-ipsec-secrets ;string|#f
  523. (default #f)))
  524. ;; In the future, it might be worth implementing a record type to configure
  525. ;; all of the plugins, but for *most* basic use cases, simply creating the
  526. ;; files will be sufficient. Same is true of charon-plugins.
  527. (define strongswand-configuration-files
  528. (list "charon" "charon-logging" "pki" "pool" "scepclient"
  529. "swanctl" "tnc"))
  530. ;; Plugins to load. All of these plugins end up as configuration files in
  531. ;; strongswan.d/charon/.
  532. (define charon-plugins
  533. (list "aes" "aesni" "attr" "attr-sql" "chapoly" "cmac" "constraints"
  534. "counters" "curl" "curve25519" "dhcp" "dnskey" "drbg" "eap-aka-3gpp"
  535. "eap-aka" "eap-dynamic" "eap-identity" "eap-md5" "eap-mschapv2"
  536. "eap-peap" "eap-radius" "eap-simaka-pseudonym" "eap-simaka-reauth"
  537. "eap-simaka-sql" "eap-sim" "eap-sim-file" "eap-tls" "eap-tnc"
  538. "eap-ttls" "ext-auth" "farp" "fips-prf" "gmp" "ha" "hmac"
  539. "kernel-netlink" "led" "md4" "md5" "mgf1" "nonce" "openssl" "pem"
  540. "pgp" "pkcs12" "pkcs1" "pkcs7" "pkcs8" "pubkey" "random" "rc2"
  541. "resolve" "revocation" "sha1" "sha2" "socket-default" "soup" "sql"
  542. "sqlite" "sshkey" "tnc-tnccs" "vici" "x509" "xauth-eap" "xauth-generic"
  543. "xauth-noauth" "xauth-pam" "xcbc"))
  544. (define (strongswan-configuration-file config)
  545. (match-record config <strongswan-configuration>
  546. (strongswan ipsec-conf ipsec-secrets)
  547. (if (eq? (string? ipsec-conf) (string? ipsec-secrets))
  548. (let* ((strongswan-dir
  549. (computed-file
  550. "strongswan.d"
  551. #~(begin
  552. (mkdir #$output)
  553. ;; Create all of the configuration files strongswan.d/.
  554. (map (lambda (conf-file)
  555. (let* ((filename (string-append
  556. #$output "/"
  557. conf-file ".conf")))
  558. (call-with-output-file filename
  559. (lambda (port)
  560. (display
  561. "# Created by 'strongswan-service'\n"
  562. port)))))
  563. (list #$@strongswand-configuration-files))
  564. (mkdir (string-append #$output "/charon"))
  565. ;; Create all of the plugin configuration files.
  566. (map (lambda (plugin)
  567. (let* ((filename (string-append
  568. #$output "/charon/"
  569. plugin ".conf")))
  570. (call-with-output-file filename
  571. (lambda (port)
  572. (format port "~a {
  573. load = yes
  574. }"
  575. plugin)))))
  576. (list #$@charon-plugins))))))
  577. ;; Generate our strongswan.conf to reflect the user configuration.
  578. (computed-file
  579. "strongswan.conf"
  580. #~(begin
  581. (call-with-output-file #$output
  582. (lambda (port)
  583. (display "# Generated by 'strongswan-service'.\n" port)
  584. (format port "charon {
  585. load_modular = yes
  586. plugins {
  587. include ~a/charon/*.conf"
  588. #$strongswan-dir)
  589. (if #$ipsec-conf
  590. (format port "
  591. stroke {
  592. load = yes
  593. secrets_file = ~a
  594. }
  595. }
  596. }
  597. starter {
  598. config_file = ~a
  599. }
  600. include ~a/*.conf"
  601. #$ipsec-secrets
  602. #$ipsec-conf
  603. #$strongswan-dir)
  604. (format port "
  605. }
  606. }
  607. include ~a/*.conf"
  608. #$strongswan-dir)))))))
  609. (throw 'error
  610. (G_ "strongSwan ipsec-conf and ipsec-secrets must both be (un)set")))))
  611. (define (strongswan-shepherd-service config)
  612. (let* ((ipsec (file-append strongswan "/sbin/ipsec"))
  613. (strongswan-conf-path (strongswan-configuration-file config)))
  614. (list (shepherd-service
  615. (requirement '(networking))
  616. (provision '(ipsec))
  617. (start #~(make-forkexec-constructor
  618. (list #$ipsec "start" "--nofork")
  619. #:environment-variables
  620. (list (string-append "STRONGSWAN_CONF="
  621. #$strongswan-conf-path))))
  622. (stop #~(make-kill-destructor))
  623. (documentation
  624. "strongSwan's charon IKE keying daemon for IPsec VPN.")))))
  625. (define strongswan-service-type
  626. (service-type
  627. (name 'strongswan)
  628. (extensions
  629. (list (service-extension shepherd-root-service-type
  630. strongswan-shepherd-service)))
  631. (default-value (strongswan-configuration))
  632. (description
  633. "Connect to an IPsec @acronym{VPN, Virtual Private Network} with
  634. strongSwan.")))
  635. ;;;
  636. ;;; Wireguard.
  637. ;;;
  638. (define-record-type* <wireguard-peer>
  639. wireguard-peer make-wireguard-peer
  640. wireguard-peer?
  641. (name wireguard-peer-name)
  642. (endpoint wireguard-peer-endpoint
  643. (default #f)) ;string
  644. (public-key wireguard-peer-public-key) ;string
  645. (preshared-key wireguard-peer-preshared-key
  646. (default #f)) ;string
  647. (allowed-ips wireguard-peer-allowed-ips) ;list of strings
  648. (keep-alive wireguard-peer-keep-alive
  649. (default #f))) ;integer
  650. (define-record-type* <wireguard-configuration>
  651. wireguard-configuration make-wireguard-configuration
  652. wireguard-configuration?
  653. (wireguard wireguard-configuration-wireguard ;file-like
  654. (default wireguard-tools))
  655. (interface wireguard-configuration-interface ;string
  656. (default "wg0"))
  657. (addresses wireguard-configuration-addresses ;string
  658. (default '("10.0.0.1/32")))
  659. (port wireguard-configuration-port ;integer
  660. (default 51820))
  661. (private-key wireguard-configuration-private-key ;string
  662. (default "/etc/wireguard/private.key"))
  663. (peers wireguard-configuration-peers ;list of <wiregard-peer>
  664. (default '()))
  665. (dns wireguard-configuration-dns ;list of strings
  666. (default '()))
  667. (monitor-ips? wireguard-configuration-monitor-ips? ;boolean
  668. (default #f))
  669. (monitor-ips-interval wireguard-configuration-monitor-ips-interval
  670. (default '(next-minute (range 0 60 5)))) ;string | list
  671. (pre-up wireguard-configuration-pre-up ;list of strings
  672. (default '()))
  673. (post-up wireguard-configuration-post-up ;list of strings
  674. (default '()))
  675. (pre-down wireguard-configuration-pre-down ;list of strings
  676. (default '()))
  677. (post-down wireguard-configuration-post-down ;list of strings
  678. (default '()))
  679. (table wireguard-configuration-table ;string
  680. (default "auto")))
  681. (define (wireguard-configuration-file config)
  682. (define (peer->config peer)
  683. (match-record peer <wireguard-peer>
  684. (name public-key endpoint allowed-ips keep-alive)
  685. (let ((lines (list
  686. (format #f "[Peer] #~a" name)
  687. (format #f "PublicKey = ~a" public-key)
  688. (format #f "AllowedIPs = ~{~a~^, ~}" allowed-ips)
  689. (format #f "~@[Endpoint = ~a~]" endpoint)
  690. (format #f "~@[PersistentKeepalive = ~a~]" keep-alive))))
  691. (string-join (remove string-null? lines) "\n"))))
  692. (define (peers->preshared-keys peer keys)
  693. (let ((public-key (wireguard-peer-public-key peer))
  694. (preshared-key (wireguard-peer-preshared-key peer)))
  695. (if preshared-key
  696. (cons* public-key preshared-key keys)
  697. keys)))
  698. (match-record config <wireguard-configuration>
  699. (wireguard interface addresses port private-key peers dns
  700. pre-up post-up pre-down post-down table)
  701. (let* ((config-file (string-append interface ".conf"))
  702. (peer-keys (fold peers->preshared-keys (list) peers))
  703. (peers (map peer->config peers))
  704. (config
  705. (computed-file
  706. "wireguard-config"
  707. #~(begin
  708. (use-modules (ice-9 format)
  709. (srfi srfi-1))
  710. (define lines
  711. (list
  712. "[Interface]"
  713. #$@(if (null? addresses)
  714. '()
  715. (list (format #f "Address = ~{~a~^, ~}"
  716. addresses)))
  717. (format #f "~@[Table = ~a~]" #$table)
  718. #$@(if (null? pre-up)
  719. '()
  720. (list (format #f "~{PreUp = ~a~%~}" pre-up)))
  721. (format #f "PostUp = ~a set %i private-key ~a\
  722. ~{ peer ~a preshared-key ~a~}" #$(file-append wireguard "/bin/wg")
  723. #$private-key '#$peer-keys)
  724. #$@(if (null? post-up)
  725. '()
  726. (list (format #f "~{PostUp = ~a~%~}" post-up)))
  727. #$@(if (null? pre-down)
  728. '()
  729. (list (format #f "~{PreDown = ~a~%~}" pre-down)))
  730. #$@(if (null? post-down)
  731. '()
  732. (list (format #f "~{PostDown = ~a~%~}" post-down)))
  733. (format #f "~@[ListenPort = ~a~]" #$port)
  734. #$@(if (null? dns)
  735. '()
  736. (list (format #f "DNS = ~{~a~^, ~}" dns)))))
  737. (mkdir #$output)
  738. (chdir #$output)
  739. (call-with-output-file #$config-file
  740. (lambda (port)
  741. (format port "~a~%~%~{~a~%~^~%~}"
  742. (string-join (remove string-null? lines) "\n")
  743. '#$peers)))))))
  744. (file-append config "/" config-file))))
  745. (define (wireguard-activation config)
  746. (match-record config <wireguard-configuration>
  747. (private-key wireguard)
  748. #~(begin
  749. (use-modules (guix build utils)
  750. (ice-9 popen)
  751. (ice-9 rdelim))
  752. (mkdir-p (dirname #$private-key))
  753. (unless (file-exists? #$private-key)
  754. (let* ((pipe
  755. (open-input-pipe (string-append
  756. #$(file-append wireguard "/bin/wg")
  757. " genkey")))
  758. (key (read-line pipe)))
  759. (call-with-output-file #$private-key
  760. (lambda (port)
  761. (display key port)))
  762. (chmod #$private-key #o400)
  763. (close-pipe pipe))))))
  764. ;;; XXX: Copied from (guix scripts pack), changing define to define*.
  765. (define-syntax-rule (define-with-source (variable args ...) body body* ...)
  766. "Bind VARIABLE to a procedure accepting ARGS defined as BODY, also setting
  767. its source property."
  768. (begin
  769. (define* (variable args ...)
  770. body body* ...)
  771. (eval-when (load eval)
  772. (set-procedure-property! variable 'source
  773. '(define* (variable args ...) body body* ...)))))
  774. (define (wireguard-service-name interface)
  775. "Return the WireGuard service name (a symbol) configured to use INTERFACE."
  776. (symbol-append 'wireguard- (string->symbol interface)))
  777. (define-with-source (strip-port/maybe endpoint #:key ipv6?)
  778. "Strip the colon and port, if present in ENDPOINT, a string."
  779. (if ipv6?
  780. (if (string-prefix? "[" endpoint)
  781. (first (string-split (string-drop endpoint 1) #\])) ;ipv6
  782. endpoint)
  783. (first (string-split endpoint #\:)))) ;ipv4
  784. (define* (ipv4-address? address)
  785. "Predicate to check whether ADDRESS is a valid IPv4 address."
  786. (let ((address (strip-port/maybe address)))
  787. (false-if-exception
  788. (->bool (getaddrinfo address #f AI_NUMERICHOST AF_INET)))))
  789. (define* (ipv6-address? address)
  790. "Predicate to check whether ADDRESS is a valid IPv6 address."
  791. (let ((address (strip-port/maybe address #:ipv6? #t)))
  792. (false-if-exception
  793. (->bool (getaddrinfo address #f AI_NUMERICHOST AF_INET6)))))
  794. (define (host-name? name)
  795. "Predicate to check whether NAME is a host name, i.e. not an IP address."
  796. (not (or (ipv6-address? name) (ipv4-address? name))))
  797. (define (endpoint-host-names peers)
  798. "Return an association list of endpoint host names keyed by their peer
  799. public key, if any."
  800. (reverse
  801. (fold (lambda (peer host-names)
  802. (let ((public-key (wireguard-peer-public-key peer))
  803. (endpoint (wireguard-peer-endpoint peer)))
  804. (if (and endpoint (host-name? endpoint))
  805. (cons (cons public-key endpoint) host-names)
  806. host-names)))
  807. '()
  808. peers)))
  809. (define (wireguard-shepherd-service config)
  810. (match-record config <wireguard-configuration>
  811. (wireguard interface)
  812. (let ((wg-quick (file-append wireguard "/bin/wg-quick"))
  813. (config (wireguard-configuration-file config)))
  814. (list (shepherd-service
  815. (requirement '(networking))
  816. (provision (list (wireguard-service-name interface)))
  817. (start #~(lambda _
  818. (invoke #$wg-quick "up" #$config)))
  819. (stop #~(lambda _
  820. (invoke #$wg-quick "down" #$config)
  821. #f)) ;stopped!
  822. (actions (list (shepherd-configuration-action config)))
  823. (documentation "Run the Wireguard VPN tunnel"))))))
  824. (define (wireguard-monitoring-jobs config)
  825. ;; Loosely based on WireGuard's own 'reresolve-dns.sh' shell script (see:
  826. ;; https://raw.githubusercontent.com/WireGuard/wireguard-tools/
  827. ;; master/contrib/reresolve-dns/reresolve-dns.sh).
  828. (match-record config <wireguard-configuration>
  829. (interface monitor-ips? monitor-ips-interval peers)
  830. (let ((host-names (endpoint-host-names peers)))
  831. (if monitor-ips?
  832. (if (null? host-names)
  833. (begin
  834. (warn "monitor-ips? is #t but no host name to monitor")
  835. '())
  836. ;; The mcron monitor job may be a string or a list; ungexp strips
  837. ;; one quote level, which must be added back when a list is
  838. ;; provided.
  839. (list
  840. #~(job
  841. (if (string? #$monitor-ips-interval)
  842. #$monitor-ips-interval
  843. '#$monitor-ips-interval)
  844. #$(program-file
  845. (format #f "wireguard-~a-monitoring" interface)
  846. (with-imported-modules (source-module-closure
  847. '((gnu services herd)
  848. (guix build utils)))
  849. #~(begin
  850. (use-modules (gnu services herd)
  851. (guix build utils)
  852. (ice-9 popen)
  853. (ice-9 match)
  854. (ice-9 textual-ports)
  855. (srfi srfi-1)
  856. (srfi srfi-26))
  857. (define (resolve-host name)
  858. "Return the IP address resolved from NAME."
  859. (let* ((ai (car (getaddrinfo name)))
  860. (sa (addrinfo:addr ai)))
  861. (inet-ntop (sockaddr:fam sa)
  862. (sockaddr:addr sa))))
  863. (define wg #$(file-append wireguard-tools "/bin/wg"))
  864. #$(procedure-source strip-port/maybe)
  865. (define service-name '#$(wireguard-service-name
  866. interface))
  867. (when (live-service-running
  868. (current-service service-name))
  869. (let* ((pipe (open-pipe* OPEN_READ wg "show"
  870. #$interface "endpoints"))
  871. (lines (string-split (get-string-all pipe)
  872. #\newline))
  873. ;; IPS is an association list mapping
  874. ;; public keys to IP addresses.
  875. (ips (map (match-lambda
  876. ((public-key ip)
  877. (cons public-key
  878. (strip-port/maybe ip))))
  879. (map (cut string-split <> #\tab)
  880. (remove string-null?
  881. lines)))))
  882. (close-pipe pipe)
  883. (for-each
  884. (match-lambda
  885. ((key . host-name)
  886. (let ((resolved-ip (resolve-host
  887. (strip-port/maybe
  888. host-name)))
  889. (current-ip (assoc-ref ips key)))
  890. (unless (string=? resolved-ip current-ip)
  891. (format #t "resetting `~a' peer \
  892. endpoint to `~a' due to stale IP (`~a' instead of `~a')~%"
  893. key host-name
  894. current-ip resolved-ip)
  895. (invoke wg "set" #$interface "peer" key
  896. "endpoint" host-name)))))
  897. '#$host-names)))))))))
  898. '())))) ;monitor-ips? is #f
  899. (define wireguard-service-type
  900. (service-type
  901. (name 'wireguard)
  902. (extensions
  903. (list (service-extension shepherd-root-service-type
  904. wireguard-shepherd-service)
  905. (service-extension activation-service-type
  906. wireguard-activation)
  907. (service-extension profile-service-type
  908. (compose list
  909. wireguard-configuration-wireguard))
  910. (service-extension mcron-service-type
  911. wireguard-monitoring-jobs)))
  912. (description "Set up Wireguard @acronym{VPN, Virtual Private Network}
  913. tunnels.")))