~ruther/guix-local

7fb7d071cfda3a7d49499b7ed3b4571f0eb99c3e — Lilah Tascheter 5 months ago 368d2d9
gnu: services: Add mollysocket service.

* gnu/services/messaging.scm (ms-format,ms-serialize-string,
ms-serialize-integer, ms-serialize-boolean,
ms-serialize-list-of-strings, mollysocket-shepherd-service,
mollysocket-account-service, mollysocket-activation-service): New
procedures.
(mollysocket-configuration): New configuration.
(mollysocket-service-type): New variable.
* doc/guix.texi (Messaging Services)[Mollysocket Service]: New section.

Change-Id: I87d57b69a6d8fa8841a95add084aa383385de4c7
2 files changed, 148 insertions(+), 1 deletions(-)

M doc/guix.texi
M gnu/services/messaging.scm
M doc/guix.texi => doc/guix.texi +44 -0
@@ 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


M gnu/services/messaging.scm => gnu/services/messaging.scm +104 -1
@@ 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.")))