From 43b2e440c38a39eb64088bd6c08771c060aa10fc Mon Sep 17 00:00:00 2001 From: Mathieu Othacehe Date: Sat, 13 Feb 2021 10:07:47 +0100 Subject: services: wireguard: New service. * gnu/services/vpn.scm (wireguard-peer, wireguard-configuration): New records. (wireguard-service-type): New variable. * doc/guix.texi (VPN Services): Document it. --- doc/guix.texi | 73 +++++++++++++++++++++++++-- gnu/services/vpn.scm | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 207 insertions(+), 4 deletions(-) diff --git a/doc/guix.texi b/doc/guix.texi index 535c98a453..942d5f93df 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -26336,9 +26336,12 @@ Defaults to @samp{()}. @cindex virtual private network (VPN) The @code{(gnu services vpn)} module provides services related to -@dfn{virtual private networks} (VPNs). It provides a @emph{client} service for -your machine to connect to a VPN, and a @emph{server} service for your machine -to host a VPN@. Both services use @uref{https://openvpn.net/, OpenVPN}. +@dfn{virtual private networks} (VPNs). + +@subsubheading OpenVPN + +It provides a @emph{client} service for your machine to connect to a +VPN, and a @emph{server} service for your machine to host a VPN@. @deffn {Scheme Procedure} openvpn-client-service @ [#:config (openvpn-client-configuration)] @@ -26717,6 +26720,70 @@ Defaults to @samp{#f}. @c %end of automatic openvpn-server documentation +@subsubheading Wireguard + +@defvr {Scheme Variable} wireguard-service-type +A service type for a Wireguard tunnel interface. Its value must be a +@code{wireguard-configuration} record as in this example: + +@lisp +(service wireguard-service-type + (wireguard-configuration + (peers + (list + (wireguard-peer + (name "my-peer") + (endpoint "my.wireguard.com:51820") + (public-key "hzpKg9X1yqu1axN6iJp0mWf6BZGo8m1wteKwtTmDGF4=") + (allowed-ips '("10.0.0.2/32"))))))) +@end lisp + +@end defvr + +@deftp {Data Type} wireguard-configuration +Data type representing the configuration of the Wireguard service. + +@table @asis +@item @code{wireguard} +The wireguard package to use for this service. + +@item @code{interface} (default: @code{"wg0"}) +The interface name for the VPN. + +@item @code{addresses} (default: @code{'("10.0.0.1/32")}) +The IP addresses to be assigned to the above interface. + +@item @code{private-key} (default: @code{"/etc/wireguard/private.key"}) +The private key file for the interface. It is automatically generated if +the file does not exist. + +@item @code{peers} (default: @code{'()}) +The authorized peers on this interface. This is a list of +@var{wireguard-peer} records. + +@end table +@end deftp + +@deftp {Data Type} wireguard-peer +Data type representing a Wireguard peer attached to a given interface. + +@table @asis +@item @code{name} +The peer name. + +@item @code{endpoint} (default: @code{#f}) +The optional endpoint for the peer, such as +@code{"demo.wireguard.com:51820"}. + +@item @code{public-key} +The peer public-key represented as a base64 string. + +@item @code{allowed-ips} +A list of IP addresses from which incoming traffic for this peer is +allowed and to which incoming traffic for this peer is directed. + +@end table +@end deftp @node Network File System @subsection Network File System diff --git a/gnu/services/vpn.scm b/gnu/services/vpn.scm index 70f2617c7e..3e315a6df2 100644 --- a/gnu/services/vpn.scm +++ b/gnu/services/vpn.scm @@ -40,7 +40,24 @@ (define-module (gnu services vpn) 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. @@ -507,3 +524,122 @@ (define (generate-openvpn-client-documentation) (remote openvpn-remote-configuration)) (openvpn-remote-configuration ,openvpn-remote-configuration-fields)) 'openvpn-client-configuration)) + + +;;; +;;; Wireguard. +;;; + +(define-record-type* + 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 make-wireguard-configuration + wireguard-configuration? + (wireguard wireguard-configuration-wireguard ; + (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 + (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 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 + (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 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))))) -- cgit v1.2.3