vpn.scm 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  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. wireguard-peer
  44. wireguard-peer?
  45. wireguard-peer-name
  46. wireguard-peer-endpoint
  47. wireguard-peer-allowed-ips
  48. wireguard-configuration
  49. wireguard-configuration?
  50. wireguard-configuration-wireguard
  51. wireguard-configuration-interface
  52. wireguard-configuration-addresses
  53. wireguard-configuration-port
  54. wireguard-configuration-private-key
  55. wireguard-configuration-peers
  56. wireguard-service-type))
  57. ;;;
  58. ;;; OpenVPN.
  59. ;;;
  60. (define (uglify-field-name name)
  61. (match name
  62. ('verbosity "verb")
  63. (_ (let ((str (symbol->string name)))
  64. (if (string-suffix? "?" str)
  65. (substring str 0 (1- (string-length str)))
  66. str)))))
  67. (define (serialize-field field-name val)
  68. (if (eq? field-name 'pid-file)
  69. (format #t "")
  70. (format #t "~a ~a\n" (uglify-field-name field-name) val)))
  71. (define serialize-string serialize-field)
  72. (define-maybe string)
  73. (define (serialize-boolean field-name val)
  74. (if val
  75. (serialize-field field-name "")
  76. (format #t "")))
  77. (define (ip-mask? val)
  78. (and (string? val)
  79. (if (string-match "^([0-9]+\\.){3}[0-9]+ ([0-9]+\\.){3}[0-9]+$" val)
  80. (let ((numbers (string-tokenize val char-set:digit)))
  81. (all-lte numbers (list 255 255 255 255 255 255 255 255)))
  82. #f)))
  83. (define serialize-ip-mask serialize-string)
  84. (define-syntax define-enumerated-field-type
  85. (lambda (x)
  86. (define (id-append ctx . parts)
  87. (datum->syntax ctx (apply symbol-append (map syntax->datum parts))))
  88. (syntax-case x ()
  89. ((_ name (option ...))
  90. #`(begin
  91. (define (#,(id-append #'name #'name #'?) x)
  92. (memq x '(option ...)))
  93. (define (#,(id-append #'name #'serialize- #'name) field-name val)
  94. (serialize-field field-name val)))))))
  95. (define-enumerated-field-type proto
  96. (udp tcp udp6 tcp6))
  97. (define-enumerated-field-type dev
  98. (tun tap))
  99. (define key-usage? boolean?)
  100. (define (serialize-key-usage field-name value)
  101. (if value
  102. (format #t "remote-cert-tls server\n")
  103. #f))
  104. (define bind? boolean?)
  105. (define (serialize-bind field-name value)
  106. (if value
  107. #f
  108. (format #t "nobind\n")))
  109. (define resolv-retry? boolean?)
  110. (define (serialize-resolv-retry field-name value)
  111. (if value
  112. (format #t "resolv-retry infinite\n")
  113. #f))
  114. (define (serialize-tls-auth role location)
  115. (if location
  116. (serialize-field 'tls-auth
  117. (string-append location " " (match role
  118. ('server "0")
  119. ('client "1"))))
  120. #f))
  121. (define (tls-auth? val)
  122. (or (eq? val #f)
  123. (string? val)))
  124. (define (serialize-tls-auth-server field-name val)
  125. (serialize-tls-auth 'server val))
  126. (define (serialize-tls-auth-client field-name val)
  127. (serialize-tls-auth 'client val))
  128. (define tls-auth-server? tls-auth?)
  129. (define tls-auth-client? tls-auth?)
  130. (define (serialize-number field-name val)
  131. (serialize-field field-name (number->string val)))
  132. (define (all-lte left right)
  133. (if (eq? left '())
  134. (eq? right '())
  135. (and (<= (string->number (car left)) (car right))
  136. (all-lte (cdr left) (cdr right)))))
  137. (define (cidr4? val)
  138. (if (string? val)
  139. (if (string-match "^([0-9]+\\.){3}[0-9]+/[0-9]+$" val)
  140. (let ((numbers (string-tokenize val char-set:digit)))
  141. (all-lte numbers (list 255 255 255 255 32)))
  142. #f)
  143. (eq? val #f)))
  144. (define (cidr6? val)
  145. (if (string? val)
  146. (string-match "^([0-9a-f]{0,4}:){0,8}/[0-9]{1,3}$" val)
  147. (eq? val #f)))
  148. (define (serialize-cidr4 field-name val)
  149. (if (eq? val #f) #f (serialize-field field-name val)))
  150. (define (serialize-cidr6 field-name val)
  151. (if (eq? val #f) #f (serialize-field field-name val)))
  152. (define (ip? val)
  153. (if (string? val)
  154. (if (string-match "^([0-9]+\\.){3}[0-9]+$" val)
  155. (let ((numbers (string-tokenize val char-set:digit)))
  156. (all-lte numbers (list 255 255 255 255)))
  157. #f)
  158. (eq? val #f)))
  159. (define (serialize-ip field-name val)
  160. (if (eq? val #f) #f (serialize-field field-name val)))
  161. (define (keepalive? val)
  162. (and (list? val)
  163. (and (number? (car val))
  164. (number? (car (cdr val))))))
  165. (define (serialize-keepalive field-name val)
  166. (format #t "~a ~a ~a\n" (uglify-field-name field-name)
  167. (number->string (car val)) (number->string (car (cdr val)))))
  168. (define gateway? boolean?)
  169. (define (serialize-gateway field-name val)
  170. (and val
  171. (format #t "push \"redirect-gateway\"\n")))
  172. (define-configuration openvpn-remote-configuration
  173. (name
  174. (string "my-server")
  175. "Server name.")
  176. (port
  177. (number 1194)
  178. "Port number the server listens to."))
  179. (define-configuration openvpn-ccd-configuration
  180. (name
  181. (string "client")
  182. "Client name.")
  183. (iroute
  184. (ip-mask #f)
  185. "Client own network")
  186. (ifconfig-push
  187. (ip-mask #f)
  188. "Client VPN IP."))
  189. (define (openvpn-remote-list? val)
  190. (and (list? val)
  191. (or (eq? val '())
  192. (and (openvpn-remote-configuration? (car val))
  193. (openvpn-remote-list? (cdr val))))))
  194. (define (serialize-openvpn-remote-list field-name val)
  195. (for-each (lambda (remote)
  196. (format #t "remote ~a ~a\n" (openvpn-remote-configuration-name remote)
  197. (number->string (openvpn-remote-configuration-port remote))))
  198. val))
  199. (define (openvpn-ccd-list? val)
  200. (and (list? val)
  201. (or (eq? val '())
  202. (and (openvpn-ccd-configuration? (car val))
  203. (openvpn-ccd-list? (cdr val))))))
  204. (define (serialize-openvpn-ccd-list field-name val)
  205. #f)
  206. (define (create-ccd-directory val)
  207. "Create a ccd directory containing files for the ccd configuration option
  208. of OpenVPN. Each file in this directory represents particular settings for a
  209. client. Each file is named after the name of the client."
  210. (let ((files (map (lambda (ccd)
  211. (list (openvpn-ccd-configuration-name ccd)
  212. (with-output-to-string
  213. (lambda ()
  214. (serialize-configuration
  215. ccd openvpn-ccd-configuration-fields)))))
  216. val)))
  217. (computed-file "ccd"
  218. (with-imported-modules '((guix build utils))
  219. #~(begin
  220. (use-modules (guix build utils))
  221. (use-modules (ice-9 match))
  222. (mkdir-p #$output)
  223. (for-each
  224. (lambda (ccd)
  225. (match ccd
  226. ((name config-string)
  227. (call-with-output-file
  228. (string-append #$output "/" name)
  229. (lambda (port) (display config-string port))))))
  230. '#$files))))))
  231. (define-syntax define-split-configuration
  232. (lambda (x)
  233. (syntax-case x ()
  234. ((_ name1 name2 (common-option ...) (first-option ...) (second-option ...))
  235. #`(begin
  236. (define-configuration #,#'name1
  237. common-option ...
  238. first-option ...)
  239. (define-configuration #,#'name2
  240. common-option ...
  241. second-option ...))))))
  242. (define-split-configuration openvpn-client-configuration
  243. openvpn-server-configuration
  244. ((openvpn
  245. (package openvpn)
  246. "The OpenVPN package.")
  247. (pid-file
  248. (string "/var/run/openvpn/openvpn.pid")
  249. "The OpenVPN pid file.")
  250. (proto
  251. (proto 'udp)
  252. "The protocol (UDP or TCP) used to open a channel between clients and
  253. servers.")
  254. (dev
  255. (dev 'tun)
  256. "The device type used to represent the VPN connection.")
  257. (ca
  258. (maybe-string "/etc/openvpn/ca.crt")
  259. "The certificate authority to check connections against.")
  260. (cert
  261. (maybe-string "/etc/openvpn/client.crt")
  262. "The certificate of the machine the daemon is running on. It should be signed
  263. by the authority given in @code{ca}.")
  264. (key
  265. (maybe-string "/etc/openvpn/client.key")
  266. "The key of the machine the daemon is running on. It must be the key whose
  267. certificate is @code{cert}.")
  268. (comp-lzo?
  269. (boolean #t)
  270. "Whether to use the lzo compression algorithm.")
  271. (persist-key?
  272. (boolean #t)
  273. "Don't re-read key files across SIGUSR1 or --ping-restart.")
  274. (persist-tun?
  275. (boolean #t)
  276. "Don't close and reopen TUN/TAP device or run up/down scripts across
  277. SIGUSR1 or --ping-restart restarts.")
  278. (fast-io?
  279. (boolean #f)
  280. "(Experimental) Optimize TUN/TAP/UDP I/O writes by avoiding a call to
  281. poll/epoll/select prior to the write operation.")
  282. (verbosity
  283. (number 3)
  284. "Verbosity level."))
  285. ;; client-specific configuration
  286. ((tls-auth
  287. (tls-auth-client #f)
  288. "Add an additional layer of HMAC authentication on top of the TLS control
  289. channel to protect against DoS attacks.")
  290. (auth-user-pass
  291. (maybe-string 'disabled)
  292. "Authenticate with server using username/password. The option is a file
  293. containing username/password on 2 lines. Do not use a file-like object as it
  294. would be added to the store and readable by any user.")
  295. (verify-key-usage?
  296. (key-usage #t)
  297. "Whether to check the server certificate has server usage extension.")
  298. (bind?
  299. (bind #f)
  300. "Bind to a specific local port number.")
  301. (resolv-retry?
  302. (resolv-retry #t)
  303. "Retry resolving server address.")
  304. (remote
  305. (openvpn-remote-list '())
  306. "A list of remote servers to connect to."))
  307. ;; server-specific configuration
  308. ((tls-auth
  309. (tls-auth-server #f)
  310. "Add an additional layer of HMAC authentication on top of the TLS control
  311. channel to protect against DoS attacks.")
  312. (port
  313. (number 1194)
  314. "Specifies the port number on which the server listens.")
  315. (server
  316. (ip-mask "10.8.0.0 255.255.255.0")
  317. "An ip and mask specifying the subnet inside the virtual network.")
  318. (server-ipv6
  319. (cidr6 #f)
  320. "A CIDR notation specifying the IPv6 subnet inside the virtual network.")
  321. (dh
  322. (string "/etc/openvpn/dh2048.pem")
  323. "The Diffie-Hellman parameters file.")
  324. (ifconfig-pool-persist
  325. (string "/etc/openvpn/ipp.txt")
  326. "The file that records client IPs.")
  327. (redirect-gateway?
  328. (gateway #f)
  329. "When true, the server will act as a gateway for its clients.")
  330. (client-to-client?
  331. (boolean #f)
  332. "When true, clients are allowed to talk to each other inside the VPN.")
  333. (keepalive
  334. (keepalive '(10 120))
  335. "Causes ping-like messages to be sent back and forth over the link so that
  336. each side knows when the other side has gone down. @code{keepalive} requires
  337. a pair. The first element is the period of the ping sending, and the second
  338. element is the timeout before considering the other side down.")
  339. (max-clients
  340. (number 100)
  341. "The maximum number of clients.")
  342. (status
  343. (string "/var/run/openvpn/status")
  344. "The status file. This file shows a small report on current connection. It
  345. is truncated and rewritten every minute.")
  346. (client-config-dir
  347. (openvpn-ccd-list '())
  348. "The list of configuration for some clients.")))
  349. (define (openvpn-config-file role config)
  350. (let ((config-str
  351. (with-output-to-string
  352. (lambda ()
  353. (serialize-configuration config
  354. (match role
  355. ('server
  356. openvpn-server-configuration-fields)
  357. ('client
  358. openvpn-client-configuration-fields))))))
  359. (ccd-dir (match role
  360. ('server (create-ccd-directory
  361. (openvpn-server-configuration-client-config-dir
  362. config)))
  363. ('client #f))))
  364. (computed-file "openvpn.conf"
  365. #~(begin
  366. (use-modules (ice-9 match))
  367. (call-with-output-file #$output
  368. (lambda (port)
  369. (match '#$role
  370. ('server (display "" port))
  371. ('client (display "client\n" port)))
  372. (display #$config-str port)
  373. (match '#$role
  374. ('server (display
  375. (string-append "client-config-dir "
  376. #$ccd-dir "\n") port))
  377. ('client (display "" port)))))))))
  378. (define (openvpn-shepherd-service role)
  379. (lambda (config)
  380. (let* ((config-file (openvpn-config-file role config))
  381. (pid-file ((match role
  382. ('server openvpn-server-configuration-pid-file)
  383. ('client openvpn-client-configuration-pid-file))
  384. config))
  385. (openvpn ((match role
  386. ('server openvpn-server-configuration-openvpn)
  387. ('client openvpn-client-configuration-openvpn))
  388. config))
  389. (log-file (match role
  390. ('server "/var/log/openvpn-server.log")
  391. ('client "/var/log/openvpn-client.log"))))
  392. (list (shepherd-service
  393. (documentation (string-append "Run the OpenVPN "
  394. (match role
  395. ('server "server")
  396. ('client "client"))
  397. " daemon."))
  398. (provision (match role
  399. ('server '(vpn-server))
  400. ('client '(vpn-client))))
  401. (requirement '(networking))
  402. (start #~(make-forkexec-constructor
  403. (list (string-append #$openvpn "/sbin/openvpn")
  404. "--writepid" #$pid-file "--config" #$config-file
  405. "--daemon")
  406. #:pid-file #$pid-file))
  407. (stop #~(make-kill-destructor)))))))
  408. (define %openvpn-accounts
  409. (list (user-group (name "openvpn") (system? #t))
  410. (user-account
  411. (name "openvpn")
  412. (group "openvpn")
  413. (system? #t)
  414. (comment "Openvpn daemon user")
  415. (home-directory "/var/empty")
  416. (shell (file-append shadow "/sbin/nologin")))))
  417. (define %openvpn-activation
  418. #~(begin
  419. (use-modules (guix build utils))
  420. (mkdir-p "/var/run/openvpn")))
  421. (define openvpn-server-service-type
  422. (service-type (name 'openvpn-server)
  423. (extensions
  424. (list (service-extension shepherd-root-service-type
  425. (openvpn-shepherd-service 'server))
  426. (service-extension account-service-type
  427. (const %openvpn-accounts))
  428. (service-extension activation-service-type
  429. (const %openvpn-activation))))))
  430. (define openvpn-client-service-type
  431. (service-type (name 'openvpn-client)
  432. (extensions
  433. (list (service-extension shepherd-root-service-type
  434. (openvpn-shepherd-service 'client))
  435. (service-extension account-service-type
  436. (const %openvpn-accounts))
  437. (service-extension activation-service-type
  438. (const %openvpn-activation))))))
  439. (define* (openvpn-client-service #:key (config (openvpn-client-configuration)))
  440. (validate-configuration config openvpn-client-configuration-fields)
  441. (service openvpn-client-service-type config))
  442. (define* (openvpn-server-service #:key (config (openvpn-server-configuration)))
  443. (validate-configuration config openvpn-server-configuration-fields)
  444. (service openvpn-server-service-type config))
  445. (define (generate-openvpn-server-documentation)
  446. (generate-documentation
  447. `((openvpn-server-configuration
  448. ,openvpn-server-configuration-fields
  449. (ccd openvpn-ccd-configuration))
  450. (openvpn-ccd-configuration ,openvpn-ccd-configuration-fields))
  451. 'openvpn-server-configuration))
  452. (define (generate-openvpn-client-documentation)
  453. (generate-documentation
  454. `((openvpn-client-configuration
  455. ,openvpn-client-configuration-fields
  456. (remote openvpn-remote-configuration))
  457. (openvpn-remote-configuration ,openvpn-remote-configuration-fields))
  458. 'openvpn-client-configuration))
  459. ;;;
  460. ;;; Wireguard.
  461. ;;;
  462. (define-record-type* <wireguard-peer>
  463. wireguard-peer make-wireguard-peer
  464. wireguard-peer?
  465. (name wireguard-peer-name)
  466. (endpoint wireguard-peer-endpoint
  467. (default #f)) ;string
  468. (public-key wireguard-peer-public-key) ;string
  469. (allowed-ips wireguard-peer-allowed-ips)) ;list of strings
  470. (define-record-type* <wireguard-configuration>
  471. wireguard-configuration make-wireguard-configuration
  472. wireguard-configuration?
  473. (wireguard wireguard-configuration-wireguard ;<package>
  474. (default wireguard-tools))
  475. (interface wireguard-configuration-interface ;string
  476. (default "wg0"))
  477. (addresses wireguard-configuration-addresses ;string
  478. (default '("10.0.0.1/32")))
  479. (port wireguard-configuration-port ;integer
  480. (default 51820))
  481. (private-key wireguard-configuration-private-key ;string
  482. (default "/etc/wireguard/private.key"))
  483. (peers wireguard-configuration-peers ;list of <wiregard-peer>
  484. (default '())))
  485. (define (wireguard-configuration-file config)
  486. (define (peer->config peer)
  487. (let ((name (wireguard-peer-name peer))
  488. (public-key (wireguard-peer-public-key peer))
  489. (endpoint (wireguard-peer-endpoint peer))
  490. (allowed-ips (wireguard-peer-allowed-ips peer)))
  491. (format #f "[Peer] #~a
  492. PublicKey = ~a
  493. AllowedIPs = ~a
  494. ~a"
  495. name
  496. public-key
  497. (string-join allowed-ips ",")
  498. (if endpoint
  499. (format #f "Endpoint = ~a\n" endpoint)
  500. "\n"))))
  501. (match-record config <wireguard-configuration>
  502. (wireguard interface addresses port private-key peers)
  503. (let* ((config-file (string-append interface ".conf"))
  504. (peers (map peer->config peers))
  505. (config
  506. (computed-file
  507. "wireguard-config"
  508. #~(begin
  509. (mkdir #$output)
  510. (chdir #$output)
  511. (call-with-output-file #$config-file
  512. (lambda (port)
  513. (let ((format (@ (ice-9 format) format)))
  514. (format port "[Interface]
  515. Address = ~a
  516. PostUp = ~a set %i private-key ~a
  517. ~a
  518. ~{~a~^~%~}"
  519. #$(string-join addresses ",")
  520. #$(file-append wireguard "/bin/wg")
  521. #$private-key
  522. #$(if port
  523. (format #f "ListenPort = ~a" port)
  524. "")
  525. (list #$@peers)))))))))
  526. (file-append config "/" config-file))))
  527. (define (wireguard-activation config)
  528. (match-record config <wireguard-configuration>
  529. (private-key)
  530. #~(begin
  531. (use-modules (guix build utils)
  532. (ice-9 popen)
  533. (ice-9 rdelim))
  534. (mkdir-p (dirname #$private-key))
  535. (unless (file-exists? #$private-key)
  536. (let* ((pipe
  537. (open-input-pipe (string-append
  538. #$(file-append wireguard-tools "/bin/wg")
  539. " genkey")))
  540. (key (read-line pipe)))
  541. (call-with-output-file #$private-key
  542. (lambda (port)
  543. (display key port)))
  544. (chmod #$private-key #o400)
  545. (close-pipe pipe))))))
  546. (define (wireguard-shepherd-service config)
  547. (match-record config <wireguard-configuration>
  548. (wireguard interface)
  549. (let ((wg-quick (file-append wireguard "/bin/wg-quick"))
  550. (config (wireguard-configuration-file config)))
  551. (list (shepherd-service
  552. (requirement '(networking))
  553. (provision (list
  554. (symbol-append 'wireguard-
  555. (string->symbol interface))))
  556. (start #~(lambda _
  557. (invoke #$wg-quick "up" #$config)))
  558. (stop #~(lambda _
  559. (invoke #$wg-quick "down" #$config)))
  560. (documentation "Run the Wireguard VPN tunnel"))))))
  561. (define wireguard-service-type
  562. (service-type
  563. (name 'wireguard)
  564. (extensions
  565. (list (service-extension shepherd-root-service-type
  566. wireguard-shepherd-service)
  567. (service-extension activation-service-type
  568. wireguard-activation)))))