diff --git a/doc/guix.texi b/doc/guix.texi index 64ef5964745..ffc797ccf3c 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -31777,6 +31777,50 @@ Owner's group of the broker process. @end table @end deftp +@subsubheading Mollysocket Service + +@url{https://github.com/mollyim/mollysocket,Mollysocket} is a UnifiedPush +provider for the messaging app @url{https://molly.im,Molly}, a fork of Signal. + +@defvar mollysocket-service-type +The Mollysocket service. Takes @code{mollysocket-configuration} as its value. +By default, this service listens on port 8020 for any Molly client. +@end defvar + +@deftp {Data Type} mollysocket-configuration +The configuration for Mollysocket, with the following fields: + +@table @asis +@item @code{package} (default: @code{mollysocket}) +The Mollysocket package to use. + +@item @code{host} (default: @code{"0.0.0.0"}) +Address for the webserver to listen on. It is recommended that this be set to +localhost, and instead configure a TLS-supporting reverse proxy in front of it. + +@item @code{port} (default: @code{8020}) +Port for the webserver to listen on. + +@item @code{webserver} (default: @code{#t}) +Whether the webserver sould be enabled. If disabled, the server is considered to +be in airgapped mode, and clients must be configured likewise. + +@item @code{allowed-endpoints} (default: @code{'("*")}) +Allowlist of UnifiedPush distributor endpoints, represented as IP addresses or +domain names. Addresses on the local network must be specified explicitly. + +@item @code{allowed-uuids} (default: @code{'("*")}) +Allowlist of Signal account UUIDs. + +@item @code{db} (default: @code{"/var/lib/mollysocket/db.sqlite"}) +Path to the Mollysocket database. Will be created if it doesn't exist. + +@item @code{vapid-key-file} (default: @code{"/var/lib/mollysocket/vapid.key"}) +Path to the VAPID key used to authorize requests to the UnifiedPush distributor. +Will be created if it doesn't exist. +@end table +@end deftp + @node Telephony Services @subsection Telephony Services diff --git a/gnu/services/messaging.scm b/gnu/services/messaging.scm index 59a79e73076..595f9622946 100644 --- a/gnu/services/messaging.scm +++ b/gnu/services/messaging.scm @@ -27,6 +27,7 @@ #:use-module (gnu packages base) #:use-module (gnu packages irc) #:use-module (gnu packages messaging) + #:autoload (gnu packages rust-apps) (mollysocket) #:use-module (gnu packages tls) #:use-module (gnu services) #:use-module (gnu services shepherd) @@ -196,7 +197,18 @@ mosquitto-configuration-config-file mosquitto-configuration-user mosquitto-configuration-group - mosquitto-service-type)) + mosquitto-service-type + + mollysocket-configuration + mollysocket-configuration? + mollysocket-configuration-host + mollysocket-configuration-port + mollysocket-configuration-webserver + mollysocket-configuration-allowed-endpoints + mollysocket-configuration-allowed-uuids + mollysocket-configuration-db + mollysocket-configuration-vapid-key-file + mollysocket-service-type)) ;;; Commentary: ;;; @@ -2283,3 +2295,94 @@ multiple machines simultaneously."))) (const %snuik-accounts)) (service-extension shepherd-root-service-type snuik-services))))) + + +;;; +;;; Mollysocket. +;;; +(define (ms-format label str) + (format #f "~a = ~a~%" label (string-map (match-lambda (#\- #\_) (x x)) str))) +(define (ms-serialize-string label val) + (ms-format label (string-append "'" val "'"))) +(define (ms-serialize-integer label val) + (ms-format label (number->string val))) +(define (ms-serialize-boolean label val) + (ms-format label (if val "true" "false"))) +(define (ms-serialize-list-of-strings label val) + (ms-format label (format #f "[ ~{'~a'~^, ~} ]" val))) + +(define-configuration mollysocket-configuration + (package (file-like mollysocket) "Mollysocket package to use." + (serializer empty-serializer)) + (host (string "127.0.0.1") "Listening address. It is recommended to connect +to Mollysocket through a TLS-supporting reverse proxy.") + (port (integer 8020) "Listening port.") + (webserver (boolean #t) "Enable webserver. If disabled, clients must use +airgapped mode.") + (allowed-endpoints (list-of-strings '("*")) "Allowlist of UnifiedPush +distributor endpoints, represented as IP addresses or domain names. Addresses +on the local network must be specified explicitly.") + (allowed-uuids (list-of-strings '("*")) "Allowlist of Signal account UUIDs.") + (db (string "/var/lib/mollysocket/db.sqlite") "Path to the database. Will be +created if it does not exist.") + (vapid-key-file (string "/var/lib/mollysocket/vapid.key") "Path to the VAPID +key used to authorize requests to the UnifiedPush distributor. Will be created +if it does not exist.") + (prefix ms-)) + +(define (mollysocket-shepherd-service config) + (list (shepherd-service + (documentation "UnifiedPush provider for the Signal client Molly.") + (provision '(mollysocket)) + (requirement '(networking syslogd user-processes)) + (start #~(make-forkexec-constructor + (list #$(file-append mollysocket "/bin/mollysocket") + "-c" #$(mixed-text-file "mollysocket.toml" + (serialize-configuration config + mollysocket-configuration-fields)) + "server") + #:user "mollysocket" #:group "mollysocket" + #:log-file "/var/log/mollysocket.log")) + (stop #~(make-kill-destructor SIGINT))))) + +(define (mollysocket-account-service config) + (list (user-group (name "mollysocket") (system? #t)) + (user-account + (name "mollysocket") + (group "mollysocket") + (system? #t) + (comment "Mollysocket daemon user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin"))))) + +(define (mollysocket-activation-service config) + #~(let* ((mollysocket #$(file-append + (mollysocket-configuration-package config) + "/bin/mollysocket")) + (user (getpwnam "mollysocket")) + (key #$(mollysocket-configuration-vapid-key-file config)) + (dbdir (dirname #$(mollysocket-configuration-db config))) + (keydir (dirname key))) + (define (create file proc mode) + (let ((ret (proc file))) + (chmod file mode) + (chown file (passwd:uid user) (passwd:gid user)) + ret)) + (unless (file-exists? dbdir) (create dbdir mkdir-p #o700)) + (unless (file-exists? keydir) (create keydir mkdir-p #o700)) + (unless (file-exists? key) + (let ((p (create key open-output-file #o600))) ; restrict then write + (with-output-to-port p (lambda () (invoke mollysocket "vapid" "gen"))) + (close-port p))))) + +(define mollysocket-service-type + (service-type + (name 'mollysocket) + (extensions (list (service-extension shepherd-root-service-type + mollysocket-shepherd-service) + (service-extension account-service-type + mollysocket-account-service) + (service-extension activation-service-type + mollysocket-activation-service))) + (default-value (mollysocket-configuration)) + (description "UnifiedPush provider for the Signal client Molly.")))