GNU bug report logs - #46504
[PATCH] services: wireguard: New service.

Previous Next

Package: guix-patches;

Reported by: Mathieu Othacehe <othacehe <at> gnu.org>

Date: Sun, 14 Feb 2021 09:34:01 UTC

Severity: normal

Tags: patch

Done: Mathieu Othacehe <othacehe <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 46504 in the body.
You can then email your comments to 46504 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix-patches <at> gnu.org:
bug#46504; Package guix-patches. (Sun, 14 Feb 2021 09:34:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Mathieu Othacehe <othacehe <at> gnu.org>:
New bug report received and forwarded. Copy sent to guix-patches <at> gnu.org. (Sun, 14 Feb 2021 09:34:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Mathieu Othacehe <othacehe <at> gnu.org>
To: guix-patches <at> gnu.org
Cc: Mathieu Othacehe <othacehe <at> gnu.org>
Subject: [PATCH] services: wireguard: New service.
Date: Sun, 14 Feb 2021 10:33:01 +0100
* 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        |  78 +++++++++++++++++++++-
 gnu/services/vpn.scm | 152 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 226 insertions(+), 4 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 68abb968b0..03ad7b0357 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -26243,9 +26243,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)]
@@ -26624,6 +26627,75 @@ 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{address} (default: @code{"10.0.0.1/32"})
+The address to be assigned to the above interface.
+
+@item @code{public-key} (default: @code{"/etc/wireguard/public.key"})
+The public key file for the interface.  It is automatically generated
+from the private key file below if it does not exit.
+
+@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 with CIDR masks 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..51cdf595a5 100644
--- a/gnu/services/vpn.scm
+++ b/gnu/services/vpn.scm
@@ -40,7 +40,26 @@
             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-public-key
+            wireguard-peer-allowed-ips
+
+            wireguard-configuration
+            wireguard-configuration?
+            wireguard-configuration-wireguard
+            wireguard-configuration-interface
+            wireguard-configuration-address
+            wireguard-configuration-port
+            wireguard-configuration-public-key
+            wireguard-configuration-private-key
+            wireguard-configuration-peers
+
+            wireguard-service-type))
 
 ;;;
 ;;; OpenVPN.
@@ -507,3 +526,134 @@ is truncated and rewritten every minute.")
       (remote openvpn-remote-configuration))
      (openvpn-remote-configuration ,openvpn-remote-configuration-fields))
    'openvpn-client-configuration))
+
+
+;;;
+;;; 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"))
+  (address            wireguard-configuration-address ;string
+                      (default "10.0.0.1/32"))
+  (port               wireguard-configuration-port ;integer
+                      (default 51820))
+  (public-key         wireguard-configuration-public-key ;string
+                      (default "/etc/wireguard/public.key"))
+  (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 address 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~^~%~}"
+                               #$address
+                               #$(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>
+    (public-key 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)))
+
+        (mkdir-p (dirname #$public-key))
+        (unless (file-exists? #$public-key)
+          (let* ((pipe
+                  (open-input-pipe (string-append
+                                    #$(file-append wireguard-tools "/bin/wg")
+                                    " genkey < " #$private-key)))
+                 (key (read-line pipe)))
+            (call-with-output-file #$public-key
+              (lambda (port)
+                (display key port)))
+            (close-pipe pipe))))))
+
+(define (wireguard-shepherd-service config)
+  (match-record config <wireguard-configuration>
+    (wireguard)
+    (let ((wg-quick (file-append wireguard "/bin/wg-quick"))
+          (config (wireguard-configuration-file config)))
+      (list (shepherd-service
+             (requirement '(networking))
+             (provision '(wireguard))
+             (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)))))
-- 
2.30.0





Information forwarded to guix-patches <at> gnu.org:
bug#46504; Package guix-patches. (Sun, 14 Feb 2021 14:36:02 GMT) Full text and rfc822 format available.

Message #8 received at 46504 <at> debbugs.gnu.org (full text, mbox):

From: Brice Waegeneire <brice <at> waegenei.re>
To: Mathieu Othacehe <othacehe <at> gnu.org>
Cc: 46504 <at> debbugs.gnu.org
Subject: Re: bug#46504: [PATCH] services: wireguard: New service.
Date: Sun, 14 Feb 2021 15:35:03 +0100
Hello Mathieu,

Mathieu Othacehe <othacehe <at> gnu.org> writes:

> * gnu/services/vpn.scm (wireguard-peer, wireguard-configuration): New records.
> (wireguard-service-type): New variable.
> * doc/guix.texi (VPN Services): Document it.
> ---

[...]

Cool, more intergration of Wireguard in Guix! I started wiriting such a
service but didn't finialized it yet. Tho, I wasn't sure if it needed to
be implemented with wg-quick since upstream describe it as « a very
quick and dirty bash script for reading a few extra variables from
wg(8)-style configuration files, and automatically configures the
interface »¹.

> +
> +(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"))
> +  (address            wireguard-configuration-address ;string
> +                      (default "10.0.0.1/32"))
> +  (port               wireguard-configuration-port ;integer
> +                      (default 51820))
> +  (public-key         wireguard-configuration-public-key ;string
> +                      (default "/etc/wireguard/public.key"))
> +  (private-key        wireguard-configuration-private-key ;string
> +                      (default "/etc/wireguard/private.key"))
> +  (peers              wireguard-configuration-peers ;list of <wiregard-peer>
> +                      (default '())))
> +

wg-quick(8) say that the ”Address” attribute can be specified multiple
times and is  « a comma-separated list of IP (v4 or v6) addresses
(optionally with CIDR masks) to be assigned to  the interface. », so the
“address” field should probably be “addresses”, a list of string.

Some of the missing attributes from wg-quick(8) like “DNS” or hooks
seems realy usefull, maybe a “extra-config” field to the record could be
added to support all of thoses attributes.

Why having a “public-key” field since it is derived from the private
key?  It seems to allow missconfiguration: what happen if the private
and public part of a key don't match, or if only the “public-key” is
set?

[...]

> +(define (wireguard-shepherd-service config)
> +  (match-record config <wireguard-configuration>
> +    (wireguard)
> +    (let ((wg-quick (file-append wireguard "/bin/wg-quick"))
> +          (config (wireguard-configuration-file config)))
> +      (list (shepherd-service
> +             (requirement '(networking))
> +             (provision '(wireguard))
> +             (start #~(lambda _
> +                       (invoke #$wg-quick "up" #$config)))
> +             (stop #~(lambda _
> +                       (invoke #$wg-quick "down" #$config)))
> +             (documentation "Run the Wireguard VPN tunnel"))))))

If I understand correclty it's not possible to specify which vpn to stop
if using several of them.  Can the provision's symbol be derived from
the interface name to be able to do “sudo herd stop wireguard-wg0”?

> +(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)))))

¹ https://git.zx2c4.com/wireguard-tools/tree/README.md#n47

Cheers,
- Brice




Reply sent to Mathieu Othacehe <othacehe <at> gnu.org>:
You have taken responsibility. (Wed, 17 Feb 2021 09:39:02 GMT) Full text and rfc822 format available.

Notification sent to Mathieu Othacehe <othacehe <at> gnu.org>:
bug acknowledged by developer. (Wed, 17 Feb 2021 09:39:02 GMT) Full text and rfc822 format available.

Message #13 received at 46504-done <at> debbugs.gnu.org (full text, mbox):

From: Mathieu Othacehe <othacehe <at> gnu.org>
To: Brice Waegeneire <brice <at> waegenei.re>
Cc: 46504-done <at> debbugs.gnu.org
Subject: Re: bug#46504: [PATCH] services: wireguard: New service.
Date: Wed, 17 Feb 2021 10:38:33 +0100
Hello Brice,

> Cool, more intergration of Wireguard in Guix! I started wiriting such a
> service but didn't finialized it yet. Tho, I wasn't sure if it needed to
> be implemented with wg-quick since upstream describe it as « a very
> quick and dirty bash script for reading a few extra variables from
> wg(8)-style configuration files, and automatically configures the
> interface »¹.

Yeah, this made me hesitate too. However, I think that having this small
service is always better than a raw configuration file. It would of
course be nice to have a more complete service, maybe relying on
Guile-Netlink in the future.

> wg-quick(8) say that the ”Address” attribute can be specified multiple
> times and is  « a comma-separated list of IP (v4 or v6) addresses
> (optionally with CIDR masks) to be assigned to  the interface. », so the
> “address” field should probably be “addresses”, a list of string.

You're right, fixed.

> Some of the missing attributes from wg-quick(8) like “DNS” or hooks
> seems realy usefull, maybe a “extra-config” field to the record could be
> added to support all of thoses attributes.

Yes, I'll add it as a follow-up.

> Why having a “public-key” field since it is derived from the private
> key?  It seems to allow missconfiguration: what happen if the private
> and public part of a key don't match, or if only the “public-key” is
> set?

The rationale was that when the private key is generated, the user can
just "cat /etc/wireguard/public.key" instead of running "wg pubkey <
/etc/wireguard/private.key" but I agree it is misleading and I removed
this field.

I pushed the revised patch as 43b2e440c38a39eb64088bd6c08771c060aa10fc.

Thanks,

Mathieu




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Wed, 17 Mar 2021 11:24:04 GMT) Full text and rfc822 format available.

This bug report was last modified 3 years and 30 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.