+;;;
+;;; nftables
+;;;
+
+(define %default-nftables-ruleset
+ (plain-file "nftables.conf"
+ "# A simple and safe firewall
+table inet filter {
+ chain input {
+ type filter hook input priority 0; policy drop;
+
+ # early drop of invalid connections
+ ct state invalid drop
+
+ # allow established/related connections
+ ct state { established, related } accept
+
+ # allow from loopback
+ iifname lo accept
+
+ # allow icmp
+ ip protocol icmp accept
+ ip6 nexthdr icmpv6 accept
+
+ # allow ssh
+ tcp dport ssh accept
+
+ # reject everything else
+ reject with icmpx type port-unreachable
+ }
+ chain forward {
+ type filter hook forward priority 0; policy drop;
+ }
+ chain output {
+ type filter hook output priority 0; policy accept;
+ }
+}
+"))
+
+(define-record-type* <nftables-configuration>
+ nftables-configuration
+ make-nftables-configuration
+ nftables-configuration?
+ (package nftables-configuration-package
+ (default nftables))
+ (ruleset nftables-configuration-ruleset ; file-like object
+ (default %default-nftables-ruleset)))
+
+(define nftables-shepherd-service
+ (match-lambda
+ (($ <nftables-configuration> package ruleset)
+ (let ((nft (file-append package "/sbin/nft")))
+ (shepherd-service
+ (documentation "Packet filtering and classification")
+ (provision '(nftables))
+ (start #~(lambda _
+ (invoke #$nft "--file" #$ruleset)))
+ (stop #~(lambda _
+ (invoke #$nft "flush" "ruleset"))))))))
+
+(define nftables-service-type
+ (service-type
+ (name 'nftables)
+ (description
+ "Run @command{nft}, setting up the specified ruleset.")
+ (extensions
+ (list (service-extension shepherd-root-service-type
+ (compose list nftables-shepherd-service))
+ (service-extension profile-service-type
+ (compose list nftables-configuration-package))))
+ (default-value (nftables-configuration))))
+
+\f
+;;;
+;;; PageKite
+;;;
+
+(define-record-type* <pagekite-configuration>
+ pagekite-configuration
+ make-pagekite-configuration
+ pagekite-configuration?
+ (package pagekite-configuration-package
+ (default pagekite))
+ (kitename pagekite-configuration-kitename
+ (default #f))
+ (kitesecret pagekite-configuration-kitesecret
+ (default #f))
+ (frontend pagekite-configuration-frontend
+ (default #f))
+ (kites pagekite-configuration-kites
+ (default '("http:@kitename:localhost:80:@kitesecret")))
+ (extra-file pagekite-configuration-extra-file
+ (default #f)))
+
+(define (pagekite-configuration-file config)
+ (match-record config <pagekite-configuration>
+ (package kitename kitesecret frontend kites extra-file)
+ (mixed-text-file "pagekite.rc"
+ (if extra-file
+ (string-append "optfile = " extra-file "\n")
+ "")
+ (if kitename
+ (string-append "kitename = " kitename "\n")
+ "")
+ (if kitesecret
+ (string-append "kitesecret = " kitesecret "\n")
+ "")
+ (if frontend
+ (string-append "frontend = " frontend "\n")
+ "defaults\n")
+ (string-join (map (lambda (kite)
+ (string-append "service_on = " kite))
+ kites)
+ "\n"
+ 'suffix))))
+
+(define (pagekite-shepherd-service config)
+ (match-record config <pagekite-configuration>
+ (package kitename kitesecret frontend kites extra-file)
+ (with-imported-modules (source-module-closure
+ '((gnu build shepherd)
+ (gnu system file-systems)))
+ (shepherd-service
+ (documentation "Run the PageKite service.")
+ (provision '(pagekite))
+ (requirement '(networking))
+ (modules '((gnu build shepherd)
+ (gnu system file-systems)))
+ (start #~(make-forkexec-constructor/container
+ (list #$(file-append package "/bin/pagekite")
+ "--clean"
+ "--nullui"
+ "--nocrashreport"
+ "--runas=pagekite:pagekite"
+ (string-append "--optfile="
+ #$(pagekite-configuration-file config)))
+ #:log-file "/var/log/pagekite.log"
+ #:mappings #$(if extra-file
+ #~(list (file-system-mapping
+ (source #$extra-file)
+ (target source)))
+ #~'())))
+ ;; SIGTERM doesn't always work for some reason.
+ (stop #~(make-kill-destructor SIGINT))))))
+
+(define %pagekite-accounts
+ (list (user-group (name "pagekite") (system? #t))
+ (user-account
+ (name "pagekite")
+ (group "pagekite")
+ (system? #t)
+ (comment "PageKite user")
+ (home-directory "/var/empty")
+ (shell (file-append shadow "/sbin/nologin")))))
+
+(define pagekite-service-type
+ (service-type
+ (name 'pagekite)
+ (default-value (pagekite-configuration))
+ (extensions
+ (list (service-extension shepherd-root-service-type
+ (compose list pagekite-shepherd-service))
+ (service-extension account-service-type
+ (const %pagekite-accounts))))
+ (description
+ "Run @url{https://pagekite.net/,PageKite}, a tunneling solution to make
+local servers publicly accessible on the web, even behind NATs and firewalls.")))
+
+\f
+;;;
+;;; Yggdrasil
+;;;
+
+(define-record-type* <yggdrasil-configuration>
+ yggdrasil-configuration
+ make-yggdrasil-configuration
+ yggdrasil-configuration?
+ (package yggdrasil-configuration-package
+ (default yggdrasil))
+ (json-config yggdrasil-configuration-json-config
+ (default '()))
+ (config-file yggdrasil-config-file
+ (default "/etc/yggdrasil-private.conf"))
+ (autoconf? yggdrasil-configuration-autoconf?
+ (default #f))
+ (log-level yggdrasil-configuration-log-level
+ (default 'info))
+ (log-to yggdrasil-configuration-log-to
+ (default 'stdout)))
+
+(define (yggdrasil-configuration-file config)
+ (define (scm->yggdrasil-json x)
+ (define key-value?
+ dotted-list?)
+ (define (param->camel str)
+ (string-concatenate
+ (map
+ string-capitalize
+ (string-split str (cut eqv? <> #\-)))))
+ (cond
+ ((key-value? x)
+ (let ((k (car x))
+ (v (cdr x)))
+ (cons
+ (if (symbol? k)
+ (param->camel (symbol->string k))
+ k)
+ v)))
+ ((list? x) (map scm->yggdrasil-json x))
+ ((vector? x) (vector-map scm->yggdrasil-json x))
+ (else x)))
+ (computed-file
+ "yggdrasil.conf"
+ #~(call-with-output-file #$output
+ (lambda (port)
+ ;; it's HJSON, so comments are a-okay
+ (display "# Generated by yggdrasil-service\n" port)
+ (display #$(scm->json-string
+ (scm->yggdrasil-json
+ (yggdrasil-configuration-json-config config)))
+ port)))))
+
+(define (yggdrasil-shepherd-service config)
+ "Return a <shepherd-service> for yggdrasil with CONFIG."
+ (define yggdrasil-command
+ #~(append
+ (list (string-append
+ #$(yggdrasil-configuration-package config)
+ "/bin/yggdrasil")
+ "-useconffile"
+ #$(yggdrasil-configuration-file config))
+ (if #$(yggdrasil-configuration-autoconf? config)
+ '("-autoconf")
+ '())
+ (let ((extraconf #$(yggdrasil-config-file config)))
+ (if extraconf
+ (list "-extraconffile" extraconf)
+ '()))
+ (list "-loglevel"
+ #$(symbol->string
+ (yggdrasil-configuration-log-level config))
+ "-logto"
+ #$(symbol->string
+ (yggdrasil-configuration-log-to config)))))
+ (list (shepherd-service
+ (documentation "Connect to the Yggdrasil mesh network")
+ (provision '(yggdrasil))
+ (requirement '(networking))
+ (start #~(make-forkexec-constructor
+ #$yggdrasil-command
+ #:log-file "/var/log/yggdrasil.log"
+ #:group "yggdrasil"))
+ (stop #~(make-kill-destructor)))))
+
+(define %yggdrasil-accounts
+ (list (user-group (name "yggdrasil") (system? #t))))
+
+(define yggdrasil-service-type
+ (service-type
+ (name 'yggdrasil)
+ (description
+ "Connect to the Yggdrasil mesh network.
+See yggdrasil -genconf for config options.")
+ (extensions
+ (list (service-extension shepherd-root-service-type
+ yggdrasil-shepherd-service)
+ (service-extension account-service-type
+ (const %yggdrasil-accounts))
+ (service-extension profile-service-type
+ (compose list yggdrasil-configuration-package))))))
+
+\f
+;;;
+;;; Keepalived
+;;;
+
+(define-record-type* <keepalived-configuration>
+ keepalived-configuration make-keepalived-configuration
+ keepalived-configuration?
+ (keepalived keepalived-configuration-keepalived ;<package>
+ (default keepalived))
+ (config-file keepalived-configuration-config-file ;file-like
+ (default #f)))
+
+(define keepalived-shepherd-service
+ (match-lambda
+ (($ <keepalived-configuration> keepalived config-file)
+ (list
+ (shepherd-service
+ (provision '(keepalived))
+ (documentation "Run keepalived.")
+ (requirement '(loopback))
+ (start #~(make-forkexec-constructor
+ (list (string-append #$keepalived "/sbin/keepalived")
+ "--dont-fork" "--log-console" "--log-detail"
+ "--pid=/var/run/keepalived.pid"
+ (string-append "--use-file=" #$config-file))
+ #:pid-file "/var/run/keepalived.pid"
+ #:log-file "/var/log/keepalived.log"))
+ (respawn? #f)
+ (stop #~(make-kill-destructor)))))))
+
+(define keepalived-service-type
+ (service-type (name 'keepalived)
+ (extensions (list (service-extension shepherd-root-service-type
+ keepalived-shepherd-service)))
+ (description
+ "Run @uref{https://www.keepalived.org/, Keepalived}
+routing software.")))
+