unofficial mirror of guix-patches@gnu.org 
 help / color / mirror / code / Atom feed
* [bug#46504] [PATCH] services: wireguard: New service.
@ 2021-02-14  9:33 Mathieu Othacehe
  2021-02-14 14:35 ` Brice Waegeneire
  0 siblings, 1 reply; 3+ messages in thread
From: Mathieu Othacehe @ 2021-02-14  9:33 UTC (permalink / raw)
  To: 46504; +Cc: Mathieu Othacehe

* 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))
+
+\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"))
+  (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





^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [bug#46504] [PATCH] services: wireguard: New service.
  2021-02-14  9:33 [bug#46504] [PATCH] services: wireguard: New service Mathieu Othacehe
@ 2021-02-14 14:35 ` Brice Waegeneire
  2021-02-17  9:38   ` bug#46504: " Mathieu Othacehe
  0 siblings, 1 reply; 3+ messages in thread
From: Brice Waegeneire @ 2021-02-14 14:35 UTC (permalink / raw)
  To: Mathieu Othacehe; +Cc: 46504

Hello Mathieu,

Mathieu Othacehe <othacehe@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




^ permalink raw reply	[flat|nested] 3+ messages in thread

* bug#46504: [PATCH] services: wireguard: New service.
  2021-02-14 14:35 ` Brice Waegeneire
@ 2021-02-17  9:38   ` Mathieu Othacehe
  0 siblings, 0 replies; 3+ messages in thread
From: Mathieu Othacehe @ 2021-02-17  9:38 UTC (permalink / raw)
  To: Brice Waegeneire; +Cc: 46504-done


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




^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-02-17  9:40 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-14  9:33 [bug#46504] [PATCH] services: wireguard: New service Mathieu Othacehe
2021-02-14 14:35 ` Brice Waegeneire
2021-02-17  9:38   ` bug#46504: " Mathieu Othacehe

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).