summaryrefslogtreecommitdiff
path: root/gnu/services/vpn.scm
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2025-03-13 11:50:00 +0100
committerLudovic Courtès <ludo@gnu.org>2025-03-23 19:30:05 +0100
commit59bd1337d07f5bbbe4d75edb4e0e7b75ff338bd0 (patch)
treec68b2993a6c4f1e7c168c9549bdf493c4918cffd /gnu/services/vpn.scm
parent8d77e252d2e53c9a0c9cbc4f64b480753db0472f (diff)
services: wireguard: Turn monitoring into a Shepherd timer.
* gnu/services/vpn.scm (<wireguard-configuration>)[schedule]: Change default value. (wireguard-monitoring-program): New procedure, with code taken from… (wireguard-monitoring-jobs): … here. Remove. (wireguard-shepherd-services): New procedure, with code taken from… (wireguard-shepherd-service): … here. Remove. * doc/guix.texi (VPN Services): Update. Reviewed-by: Maxim Cournoyer <maxim.cournoyer@gmail.com> Change-Id: I6851ddf1eb9480bdc9e6c6c6b88958ab2e6225d7
Diffstat (limited to 'gnu/services/vpn.scm')
-rw-r--r--gnu/services/vpn.scm199
1 files changed, 104 insertions, 95 deletions
diff --git a/gnu/services/vpn.scm b/gnu/services/vpn.scm
index 3f1f8661d8f..f97cbac7bb3 100644
--- a/gnu/services/vpn.scm
+++ b/gnu/services/vpn.scm
@@ -34,7 +34,6 @@
34 #:use-module (gnu services) 34 #:use-module (gnu services)
35 #:use-module (gnu services configuration) 35 #:use-module (gnu services configuration)
36 #:use-module (gnu services dbus) 36 #:use-module (gnu services dbus)
37 #:use-module (gnu services mcron)
38 #:use-module (gnu services shepherd) 37 #:use-module (gnu services shepherd)
39 #:use-module (gnu system shadow) 38 #:use-module (gnu system shadow)
40 #:use-module (gnu packages admin) 39 #:use-module (gnu packages admin)
@@ -43,6 +42,7 @@
43 #:use-module (guix packages) 42 #:use-module (guix packages)
44 #:use-module (guix records) 43 #:use-module (guix records)
45 #:use-module (guix gexp) 44 #:use-module (guix gexp)
45 #:use-module (guix diagnostics)
46 #:use-module (guix i18n) 46 #:use-module (guix i18n)
47 #:use-module (guix deprecation) 47 #:use-module (guix deprecation)
48 #:use-module (srfi srfi-1) 48 #:use-module (srfi srfi-1)
@@ -757,7 +757,7 @@ strongSwan.")))
757 (monitor-ips? wireguard-configuration-monitor-ips? ;boolean 757 (monitor-ips? wireguard-configuration-monitor-ips? ;boolean
758 (default #f)) 758 (default #f))
759 (monitor-ips-interval wireguard-configuration-monitor-ips-interval 759 (monitor-ips-interval wireguard-configuration-monitor-ips-interval
760 (default '(next-minute (range 0 60 5)))) ;string | list 760 (default "*/5 * * * *")) ;string | list
761 (pre-up wireguard-configuration-pre-up ;list of strings 761 (pre-up wireguard-configuration-pre-up ;list of strings
762 (default '())) 762 (default '()))
763 (post-up wireguard-configuration-post-up ;list of strings 763 (post-up wireguard-configuration-post-up ;list of strings
@@ -919,117 +919,126 @@ public key, if any."
919 '() 919 '()
920 peers))) 920 peers)))
921 921
922(define (wireguard-shepherd-service config) 922(define (wireguard-monitoring-program config)
923 (match-record config <wireguard-configuration> 923 (match-record config <wireguard-configuration>
924 (wireguard interface shepherd-requirement) 924 (interface monitor-ips-interval peers)
925 (let ((host-names (endpoint-host-names peers)))
926 (when (null? host-names)
927 (warning (G_ "'monitor-ips?' is #t but no host name to monitor~%")))
928
929 ;; Loosely based on WireGuard's own 'reresolve-dns.sh' shell script
930 ;; (see: https://raw.githubusercontent.com/WireGuard/wireguard-tools/
931 ;; master/contrib/reresolve-dns/reresolve-dns.sh).
932 (program-file
933 (format #f "wireguard-~a-monitoring" interface)
934 (with-imported-modules (source-module-closure
935 '((gnu services herd)
936 (guix build utils)))
937 #~(begin
938 (use-modules (gnu services herd)
939 (guix build utils)
940 (ice-9 popen)
941 (ice-9 match)
942 (ice-9 textual-ports)
943 (srfi srfi-1)
944 (srfi srfi-26))
945
946 (define (resolve-host name)
947 "Return the IP address resolved from NAME."
948 (let* ((ai (car (getaddrinfo name)))
949 (sa (addrinfo:addr ai)))
950 (inet-ntop (sockaddr:fam sa)
951 (sockaddr:addr sa))))
952
953 (define wg #$(file-append wireguard-tools "/bin/wg"))
954
955 #$(procedure-source strip-port/maybe)
956
957 (define service-name
958 '#$(wireguard-service-name interface))
959
960 (when (live-service-running
961 (current-service service-name))
962 (let* ((pipe (open-pipe* OPEN_READ wg "show"
963 #$interface "endpoints"))
964 (lines (string-split (get-string-all pipe)
965 #\newline))
966 ;; IPS is an association list mapping
967 ;; public keys to IP addresses.
968 (ips (map (match-lambda
969 ((public-key ip)
970 (cons public-key
971 (strip-port/maybe ip))))
972 (map (cut string-split <> #\tab)
973 (remove string-null?
974 lines)))))
975 (close-pipe pipe)
976 (for-each
977 (match-lambda
978 ((key . host-name)
979 (let ((resolved-ip (resolve-host
980 (strip-port/maybe
981 host-name)))
982 (current-ip (assoc-ref ips key)))
983 (unless (string=? resolved-ip current-ip)
984 (format #t "resetting `~a' peer \
985endpoint to `~a' due to stale IP (`~a' instead of `~a')~%"
986 key host-name
987 current-ip resolved-ip)
988 (invoke wg "set" #$interface "peer" key
989 "endpoint" host-name)))))
990 '#$host-names)))))))))
991
992(define (wireguard-shepherd-services config)
993 (match-record config <wireguard-configuration>
994 (wireguard interface monitor-ips? monitor-ips-interval shepherd-requirement)
925 (let ((wg-quick (file-append wireguard "/bin/wg-quick")) 995 (let ((wg-quick (file-append wireguard "/bin/wg-quick"))
926 (auto-start? (wireguard-configuration-auto-start? config)) 996 (auto-start? (wireguard-configuration-auto-start? config))
927 (config (wireguard-configuration-file config))) 997 (config-file (wireguard-configuration-file config)))
928 (list (shepherd-service 998 (define monitoring-service
999 (and monitor-ips?
1000 (shepherd-service
1001 (provision (list (symbol-append
1002 (wireguard-service-name interface)
1003 '-monitoring)))
1004 (requirement (list 'user-processes
1005 (wireguard-service-name interface)))
1006 (modules '((shepherd service timer)))
1007 (start #~(make-timer-constructor
1008 #$(if (string? monitor-ips-interval)
1009 #~(cron-string->calendar-event
1010 #$monitor-ips-interval)
1011 monitor-ips-interval)
1012 (command '(#$(wireguard-monitoring-program config)))
1013 #:wait-for-termination? #t))
1014 (stop #~(make-timer-destructor))
1015 (documentation "Monitor the Wireguard VPN tunnel.")
1016 (actions (list shepherd-trigger-action)))))
1017
1018 (cons (shepherd-service
929 (requirement `(networking user-processes ,@shepherd-requirement)) 1019 (requirement `(networking user-processes ,@shepherd-requirement))
930 (provision (list (wireguard-service-name interface))) 1020 (provision (list (wireguard-service-name interface)))
931 (start #~(lambda _ 1021 (start #~(lambda _
932 (invoke #$wg-quick "up" #$config))) 1022 (invoke #$wg-quick "up" #$config-file)))
933 (stop #~(lambda _ 1023 (stop #~(lambda _
934 (invoke #$wg-quick "down" #$config) 1024 (invoke #$wg-quick "down" #$config-file)
935 #f)) ;stopped! 1025 #f)) ;stopped!
936 (actions (list (shepherd-configuration-action config))) 1026 (actions (list (shepherd-configuration-action config-file)))
937 (auto-start? auto-start?) 1027 (auto-start? auto-start?)
938 (documentation "Run the Wireguard VPN tunnel")))))) 1028 (documentation "Run the Wireguard VPN tunnel"))
939 1029 (or (and=> monitoring-service list)
940(define (wireguard-monitoring-jobs config) 1030 '())))))
941 ;; Loosely based on WireGuard's own 'reresolve-dns.sh' shell script (see:
942 ;; https://raw.githubusercontent.com/WireGuard/wireguard-tools/
943 ;; master/contrib/reresolve-dns/reresolve-dns.sh).
944 (match-record config <wireguard-configuration>
945 (interface monitor-ips? monitor-ips-interval peers)
946 (let ((host-names (endpoint-host-names peers)))
947 (if monitor-ips?
948 (if (null? host-names)
949 (begin
950 (warn "monitor-ips? is #t but no host name to monitor")
951 '())
952 ;; The mcron monitor job may be a string or a list; ungexp strips
953 ;; one quote level, which must be added back when a list is
954 ;; provided.
955 (list
956 #~(job
957 (if (string? #$monitor-ips-interval)
958 #$monitor-ips-interval
959 '#$monitor-ips-interval)
960 #$(program-file
961 (format #f "wireguard-~a-monitoring" interface)
962 (with-imported-modules (source-module-closure
963 '((gnu services herd)
964 (guix build utils)))
965 #~(begin
966 (use-modules (gnu services herd)
967 (guix build utils)
968 (ice-9 popen)
969 (ice-9 match)
970 (ice-9 textual-ports)
971 (srfi srfi-1)
972 (srfi srfi-26))
973
974 (define (resolve-host name)
975 "Return the IP address resolved from NAME."
976 (let* ((ai (car (getaddrinfo name)))
977 (sa (addrinfo:addr ai)))
978 (inet-ntop (sockaddr:fam sa)
979 (sockaddr:addr sa))))
980
981 (define wg #$(file-append wireguard-tools "/bin/wg"))
982
983 #$(procedure-source strip-port/maybe)
984
985 (define service-name '#$(wireguard-service-name
986 interface))
987
988 (when (live-service-running
989 (current-service service-name))
990 (let* ((pipe (open-pipe* OPEN_READ wg "show"
991 #$interface "endpoints"))
992 (lines (string-split (get-string-all pipe)
993 #\newline))
994 ;; IPS is an association list mapping
995 ;; public keys to IP addresses.
996 (ips (map (match-lambda
997 ((public-key ip)
998 (cons public-key
999 (strip-port/maybe ip))))
1000 (map (cut string-split <> #\tab)
1001 (remove string-null?
1002 lines)))))
1003 (close-pipe pipe)
1004 (for-each
1005 (match-lambda
1006 ((key . host-name)
1007 (let ((resolved-ip (resolve-host
1008 (strip-port/maybe
1009 host-name)))
1010 (current-ip (assoc-ref ips key)))
1011 (unless (string=? resolved-ip current-ip)
1012 (format #t "resetting `~a' peer \
1013endpoint to `~a' due to stale IP (`~a' instead of `~a')~%"
1014 key host-name
1015 current-ip resolved-ip)
1016 (invoke wg "set" #$interface "peer" key
1017 "endpoint" host-name)))))
1018 '#$host-names)))))))))
1019 '())))) ;monitor-ips? is #f
1020 1031
1021(define wireguard-service-type 1032(define wireguard-service-type
1022 (service-type 1033 (service-type
1023 (name 'wireguard) 1034 (name 'wireguard)
1024 (extensions 1035 (extensions
1025 (list (service-extension shepherd-root-service-type 1036 (list (service-extension shepherd-root-service-type
1026 wireguard-shepherd-service) 1037 wireguard-shepherd-services)
1027 (service-extension activation-service-type 1038 (service-extension activation-service-type
1028 wireguard-activation) 1039 wireguard-activation)
1029 (service-extension profile-service-type 1040 (service-extension profile-service-type
1030 (compose list 1041 (compose list
1031 wireguard-configuration-wireguard)) 1042 wireguard-configuration-wireguard))))
1032 (service-extension mcron-service-type
1033 wireguard-monitoring-jobs)))
1034 (description "Set up Wireguard @acronym{VPN, Virtual Private Network} 1043 (description "Set up Wireguard @acronym{VPN, Virtual Private Network}
1035tunnels."))) 1044tunnels.")))