;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2017 Julien Lepiller <julien@lepiller.eu>
+;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org>
+;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
;;;
;;; This file is part of GNU Guix.
;;;
openvpn-remote-configuration
openvpn-ccd-configuration
generate-openvpn-client-documentation
- generate-openvpn-server-documentation))
+ generate-openvpn-server-documentation
+
+ wireguard-peer
+ wireguard-peer?
+ wireguard-peer-name
+ wireguard-peer-endpoint
+ wireguard-peer-allowed-ips
+
+ wireguard-configuration
+ wireguard-configuration?
+ wireguard-configuration-wireguard
+ wireguard-configuration-interface
+ wireguard-configuration-addresses
+ wireguard-configuration-port
+ wireguard-configuration-private-key
+ wireguard-configuration-peers
+
+ wireguard-service-type))
;;;
;;; OpenVPN.
(define (uglify-field-name name)
(match name
- ('verbosity "verb")
- (_ (let ((str (symbol->string name)))
- (if (string-suffix? "?" str)
- (substring str 0 (1- (string-length str)))
- str)))))
+ ('verbosity "verb")
+ (_ (let ((str (symbol->string name)))
+ (if (string-suffix? "?" str)
+ (substring str 0 (1- (string-length str)))
+ str)))))
(define (serialize-field field-name val)
(if (eq? field-name 'pid-file)
(format #t "")
(format #t "~a ~a\n" (uglify-field-name field-name) val)))
(define serialize-string serialize-field)
+(define-maybe string)
(define (serialize-boolean field-name val)
(if val
- (serialize-field field-name val)
+ (serialize-field field-name "")
(format #t "")))
(define (ip-mask? val)
#f))
(define (serialize-tls-auth role location)
- (serialize-field 'tls-auth
- (string-append location " " (match role
- ('server "0")
- ('client "1")))))
+ (if location
+ (serialize-field 'tls-auth
+ (string-append location " " (match role
+ ('server "0")
+ ('client "1"))))
+ #f))
(define (tls-auth? val)
(or (eq? val #f)
(string? val)))
(for-each
(lambda (ccd)
(match ccd
- ((name config-string)
- (call-with-output-file
- (string-append #$output "/" name)
- (lambda (port) (display config-string port))))))
+ ((name config-string)
+ (call-with-output-file
+ (string-append #$output "/" name)
+ (lambda (port) (display config-string port))))))
'#$files))))))
(define-syntax define-split-configuration
"The device type used to represent the VPN connection.")
(ca
- (string "/etc/openvpn/ca.crt")
+ (maybe-string "/etc/openvpn/ca.crt")
"The certificate authority to check connections against.")
(cert
- (string "/etc/openvpn/client.crt")
+ (maybe-string "/etc/openvpn/client.crt")
"The certificate of the machine the daemon is running on. It should be signed
by the authority given in @code{ca}.")
(key
- (string "/etc/openvpn/client.key")
- "The key of the machine the daemon is running on. It must be the whose
+ (maybe-string "/etc/openvpn/client.key")
+ "The key of the machine the daemon is running on. It must be the key whose
certificate is @code{cert}.")
(comp-lzo?
"Don't close and reopen TUN/TAP device or run up/down scripts across
SIGUSR1 or --ping-restart restarts.")
+ (fast-io?
+ (boolean #f)
+ "(Experimental) Optimize TUN/TAP/UDP I/O writes by avoiding a call to
+poll/epoll/select prior to the write operation.")
+
(verbosity
(number 3)
"Verbosity level."))
"Add an additional layer of HMAC authentication on top of the TLS control
channel to protect against DoS attacks.")
+ (auth-user-pass
+ (maybe-string 'disabled)
+ "Authenticate with server using username/password. The option is a file
+containing username/password on 2 lines. Do not use a file-like object as it
+would be added to the store and readable by any user.")
+
(verify-key-usage?
(key-usage #t)
"Whether to check the server certificate has server usage extension.")
(client-to-client?
(boolean #f)
- "When true, clients are alowed to talk to each other inside the VPN.")
+ "When true, clients are allowed to talk to each other inside the VPN.")
(keepalive
(keepalive '(10 120))
(status
(string "/var/run/openvpn/status")
"The status file. This file shows a small report on current connection. It
-is trunkated and rewritten every minute.")
+is truncated and rewritten every minute.")
(client-config-dir
(openvpn-ccd-list '())
(lambda ()
(serialize-configuration config
(match role
- ('server
- openvpn-server-configuration-fields)
- ('client
- openvpn-client-configuration-fields))))))
+ ('server
+ openvpn-server-configuration-fields)
+ ('client
+ openvpn-client-configuration-fields))))))
(ccd-dir (match role
- ('server (create-ccd-directory
- (openvpn-server-configuration-client-config-dir
- config)))
- ('client #f))))
+ ('server (create-ccd-directory
+ (openvpn-server-configuration-client-config-dir
+ config)))
+ ('client #f))))
(computed-file "openvpn.conf"
#~(begin
(use-modules (ice-9 match))
(call-with-output-file #$output
(lambda (port)
(match '#$role
- ('server (display "" port))
- ('client (display "client\n" port)))
+ ('server (display "" port))
+ ('client (display "client\n" port)))
(display #$config-str port)
(match '#$role
- ('server (display
- (string-append "client-config-dir "
- #$ccd-dir "\n") port))
- ('client (display "" port)))))))))
+ ('server (display
+ (string-append "client-config-dir "
+ #$ccd-dir "\n") port))
+ ('client (display "" port)))))))))
(define (openvpn-shepherd-service role)
(lambda (config)
(let* ((config-file (openvpn-config-file role config))
(pid-file ((match role
- ('server openvpn-server-configuration-pid-file)
- ('client openvpn-client-configuration-pid-file))
+ ('server openvpn-server-configuration-pid-file)
+ ('client openvpn-client-configuration-pid-file))
config))
(openvpn ((match role
- ('server openvpn-server-configuration-openvpn)
- ('client openvpn-client-configuration-openvpn))
+ ('server openvpn-server-configuration-openvpn)
+ ('client openvpn-client-configuration-openvpn))
config))
(log-file (match role
- ('server "/var/log/openvpn-server.log")
- ('client "/var/log/openvpn-client.log"))))
+ ('server "/var/log/openvpn-server.log")
+ ('client "/var/log/openvpn-client.log"))))
(list (shepherd-service
(documentation (string-append "Run the OpenVPN "
(match role
- ('server "server")
- ('client "client"))
+ ('server "server")
+ ('client "client"))
" daemon."))
(provision (match role
- ('server '(vpn-server))
- ('client '(vpn-client))))
+ ('server '(vpn-server))
+ ('client '(vpn-client))))
(requirement '(networking))
(start #~(make-forkexec-constructor
(list (string-append #$openvpn "/sbin/openvpn")
(shell (file-append shadow "/sbin/nologin")))))
(define %openvpn-activation
- #~(mkdir-p "/var/run/openvpn"))
+ #~(begin
+ (use-modules (guix build utils))
+ (mkdir-p "/var/run/openvpn")))
(define openvpn-server-service-type
(service-type (name 'openvpn-server)
(remote openvpn-remote-configuration))
(openvpn-remote-configuration ,openvpn-remote-configuration-fields))
'openvpn-client-configuration))
+
+\f
+;;;
+;;; Wireguard.
+;;;
+
+(define-record-type* <wireguard-peer>
+ wireguard-peer make-wireguard-peer
+ wireguard-peer?
+ (name wireguard-peer-name)
+ (endpoint wireguard-peer-endpoint
+ (default #f)) ;string
+ (public-key wireguard-peer-public-key) ;string
+ (allowed-ips wireguard-peer-allowed-ips)) ;list of strings
+
+(define-record-type* <wireguard-configuration>
+ wireguard-configuration make-wireguard-configuration
+ wireguard-configuration?
+ (wireguard wireguard-configuration-wireguard ;<package>
+ (default wireguard-tools))
+ (interface wireguard-configuration-interface ;string
+ (default "wg0"))
+ (addresses wireguard-configuration-addresses ;string
+ (default '("10.0.0.1/32")))
+ (port wireguard-configuration-port ;integer
+ (default 51820))
+ (private-key wireguard-configuration-private-key ;string
+ (default "/etc/wireguard/private.key"))
+ (peers wireguard-configuration-peers ;list of <wiregard-peer>
+ (default '())))
+
+(define (wireguard-configuration-file config)
+ (define (peer->config peer)
+ (let ((name (wireguard-peer-name peer))
+ (public-key (wireguard-peer-public-key peer))
+ (endpoint (wireguard-peer-endpoint peer))
+ (allowed-ips (wireguard-peer-allowed-ips peer)))
+ (format #f "[Peer] #~a
+PublicKey = ~a
+AllowedIPs = ~a
+~a"
+ name
+ public-key
+ (string-join allowed-ips ",")
+ (if endpoint
+ (format #f "Endpoint = ~a\n" endpoint)
+ "\n"))))
+
+ (match-record config <wireguard-configuration>
+ (wireguard interface addresses port private-key peers)
+ (let* ((config-file (string-append interface ".conf"))
+ (peers (map peer->config peers))
+ (config
+ (computed-file
+ "wireguard-config"
+ #~(begin
+ (mkdir #$output)
+ (chdir #$output)
+ (call-with-output-file #$config-file
+ (lambda (port)
+ (let ((format (@ (ice-9 format) format)))
+ (format port "[Interface]
+Address = ~a
+PostUp = ~a set %i private-key ~a
+~a
+~{~a~^~%~}"
+ #$(string-join addresses ",")
+ #$(file-append wireguard "/bin/wg")
+ #$private-key
+ #$(if port
+ (format #f "ListenPort = ~a" port)
+ "")
+ (list #$@peers)))))))))
+ (file-append config "/" config-file))))
+
+(define (wireguard-activation config)
+ (match-record config <wireguard-configuration>
+ (private-key)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+ (mkdir-p (dirname #$private-key))
+ (unless (file-exists? #$private-key)
+ (let* ((pipe
+ (open-input-pipe (string-append
+ #$(file-append wireguard-tools "/bin/wg")
+ " genkey")))
+ (key (read-line pipe)))
+ (call-with-output-file #$private-key
+ (lambda (port)
+ (display key port)))
+ (chmod #$private-key #o400)
+ (close-pipe pipe))))))
+
+(define (wireguard-shepherd-service config)
+ (match-record config <wireguard-configuration>
+ (wireguard interface)
+ (let ((wg-quick (file-append wireguard "/bin/wg-quick"))
+ (config (wireguard-configuration-file config)))
+ (list (shepherd-service
+ (requirement '(networking))
+ (provision (list
+ (symbol-append 'wireguard-
+ (string->symbol interface))))
+ (start #~(lambda _
+ (invoke #$wg-quick "up" #$config)))
+ (stop #~(lambda _
+ (invoke #$wg-quick "down" #$config)))
+ (documentation "Run the Wireguard VPN tunnel"))))))
+
+(define wireguard-service-type
+ (service-type
+ (name 'wireguard)
+ (extensions
+ (list (service-extension shepherd-root-service-type
+ wireguard-shepherd-service)
+ (service-extension activation-service-type
+ wireguard-activation)))))