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