aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xregenerate-secrets.sh7
-rw-r--r--tw/services/files/nullmailer-remotes.enc13
-rw-r--r--tw/services/mail.scm107
-rw-r--r--tw/system.scm7
4 files changed, 133 insertions, 1 deletions
diff --git a/regenerate-secrets.sh b/regenerate-secrets.sh
index b0c0c3fc..42b94282 100755
--- a/regenerate-secrets.sh
+++ b/regenerate-secrets.sh
@@ -66,3 +66,10 @@ EOF
enc tw/services/files/personal-data-exporter/conso.json vin << EOF
{"prm": "$(pass www/conso-api | sed -rn '/^prm: /s///p')", "api-token": "$(pass www/conso-api | head -1)"}
EOF
+
+enc tw/services/files/nullmailer-remotes.enc lud vin << EOF
+# https://www.mythic-beasts.com/support/hosting/mail/client#outgoing
+smtp-auth.mythic-beasts.com smtp port=587 starttls \
+ user='$(pass www/mythic-beasts/email/cron | sed -rn '/^username: /s///p')' \
+ pass='$(pass www/mythic-beasts/email/cron | head -1)'
+EOF
diff --git a/tw/services/files/nullmailer-remotes.enc b/tw/services/files/nullmailer-remotes.enc
new file mode 100644
index 00000000..c3ccc16a
--- /dev/null
+++ b/tw/services/files/nullmailer-remotes.enc
@@ -0,0 +1,13 @@
+-----BEGIN AGE ENCRYPTED FILE-----
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHBESlBiZyBZV3Jx
+akVwZVZ4NUNUQ2t3cXl6dnMrVEJVbDlZcURaWmwvWURKWHNzMEhjCklUNC85Z0E2
+NFdkbUUzM2s5WlAxWmx3cDRwQzN0QWJ0aFplNlZkM2c1LzQKLT4gc3NoLWVkMjU1
+MTkgL1NXSFVBIC84UmhrdHErZW9Bc1AvQVdjVGRGSUI1SVhQVUZMNTVveVk3UUhm
+MW16QVEKVG1rcDNLWDBaU2hyL2FONi9VQ044SVhxenhRak5hdkFJSXZzY1NKOU52
+MAotLS0gNkFzSnhHR3BiNEhrdGpiUk1xbzhtTUl2SVNRRlpqQ0h6SGhwTWpPb1VD
+dwr/aYp6f+xKLcgTBbszisXksl8E1E1Hmjrn2yKG7Bd9e4+paTlzeDIB/JgZtWZz
+X3liFIPSsbtCdKQU53cGAiutPhOQiiO1ID6zr7UJ2K/f0vSuqqL1ZWGQOMk53Niv
+qRrhlOUv9+yTOBK64kvcK6AT9CfiAaHhLSF5GpQ7b8i3NzU89Lhn7uKe/zqzOghp
+z6aquV3F34claJUjZsgS/mJWT7FT6iN7vlhk1XsZCQ9io3QF78eKfjg/++EPltOR
+p7EBtJkVcvTrMb/LlGY=
+-----END AGE ENCRYPTED FILE-----
diff --git a/tw/services/mail.scm b/tw/services/mail.scm
new file mode 100644
index 00000000..2eb5c435
--- /dev/null
+++ b/tw/services/mail.scm
@@ -0,0 +1,107 @@
+(define-module (tw services mail)
+ #:use-module (gnu)
+ #:use-module ((gnu packages admin) #:select (shadow))
+ #:use-module ((gnu packages mail) #:select (nullmailer))
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services shepherd)
+ #:use-module (gnu system privilege)
+ #:use-module (guix gexp)
+ #:use-module (guix modules) ; `source-module-closure'
+ #:use-module (guix records)
+ #:use-module (tw services secrets)
+ #:export (mta-configuration
+ mta-service-type))
+
+(define-configuration/no-serialization mta-configuration
+ (host-name string "The system's host name, which is needed by nullmailer.")
+ (user (string "mail") "The UNIX user name to allocate for the MTA.")
+ (group (string "mail") "The UNIX user group to allocate for the MTA."))
+
+(define (mta-accounts config)
+ (match-record config <mta-configuration> (user group)
+ (list (user-account
+ (name user) (group group) (system? #t)
+ (comment "Nullmailer daemon user")
+ (home-directory "/var/spool/nullmailer")
+ (shell (file-append shadow "/sbin/nologin")))
+ (user-group (name group) (system? #t)))))
+
+(define (mta-secrets config)
+ (match-record config <mta-configuration> (user group)
+ (list (secret (encrypted-file (local-file "files/nullmailer-remotes.enc"))
+ (destination "/etc/nullmailer/remotes")
+ (user user) (group group)))))
+
+(define (mta-setuid-programs config)
+ ;; Allow any user to send mail. This also prevents annoying failures
+ ;; when root tries to send mail, since nullmailer-send cannot read the
+ ;; messages it puts in the queue with 0600 permissions.
+ (match-record config <mta-configuration> (user)
+ (map (lambda (prog)
+ (privileged-program
+ (program (file-append nullmailer prog))
+ (setuid? #t) (user user)))
+ '("/sbin/sendmail" "/bin/mailq"))))
+
+(define (mta-shepherd-services config)
+ (match-record config <mta-configuration> (user group)
+ (list (shepherd-service
+ (documentation "Run a basic Mail Transfer Agent.")
+ (provision '(nullmailer))
+ (start #~(make-forkexec-constructor
+ (list #$(file-append nullmailer "/sbin/nullmailer-send"))
+ #:user #$user #:group #$group))
+ (stop #~(make-kill-destructor))))))
+
+;; Ideally we'd use `etc-service-type' here, but that would install
+;; /etc/nullmailer as a directory symlink pointing into /gnu/store, which
+;; blocks installation of /etc/nullmailer/remotes later.
+(define (mta-activation config)
+ (match-record config <mta-configuration> (host-name user)
+ (with-imported-modules (source-module-closure '((guix build utils)))
+ #~(begin
+ (use-modules ((srfi srfi-26) #:select (cut))
+ ((guix build utils) #:select (mkdir-p)))
+ (define (rm-f path)
+ (false-if-exception (delete-file path)))
+ (define (mkdir-if-not-exist path)
+ (catch 'system-error (lambda () (mkdir path))
+ (lambda args
+ (or (= EEXIST (system-error-errno args))
+ (apply throw args)))))
+
+ (rm-f "/etc/nullmailer")
+ (mkdir-p "/etc/nullmailer")
+ (for-each (lambda (source target)
+ (rm-f target)
+ (symlink source target))
+ '(#$(plain-file "nm-me" host-name)
+ #$(plain-file "nm-adminaddr" "timo@twilken.net")
+ #$(plain-file "nm-allmailfrom" "cron@twilken.net"))
+ '("/etc/nullmailer/me"
+ "/etc/nullmailer/adminaddr"
+ "/etc/nullmailer/allmailfrom"))
+
+ ;; Create nullmailer's data directories and socket.
+ ;; No idea why it doesn't do this itself.
+ (let ((dirs '("/var/spool/nullmailer/queue"
+ "/var/spool/nullmailer/failed"
+ "/var/spool/nullmailer/tmp"))
+ (trigger-path "/var/spool/nullmailer/trigger"))
+ (for-each mkdir-if-not-exist dirs)
+ (unless (file-exists? trigger-path)
+ (system* #$(file-append coreutils "/bin/mkfifo") "-m" "600" trigger-path))
+ (let ((user (getpw #$user)))
+ (for-each (cut chown <> (passwd:uid user) (passwd:gid user))
+ (cons trigger-path dirs))))))))
+
+(define mta-service-type
+ (service-type
+ (name 'mta)
+ (description "Run the Mail Transfer Agent @code{nullmailer}, to forward system emails.")
+ (extensions (list (service-extension shepherd-root-service-type mta-shepherd-services)
+ (service-extension privileged-program-service-type mta-setuid-programs)
+ (service-extension account-service-type mta-accounts)
+ (service-extension activation-service-type mta-activation)
+ (service-extension secrets-service-type mta-secrets)))))
diff --git a/tw/system.scm b/tw/system.scm
index d9e33f0e..a66a9230 100644
--- a/tw/system.scm
+++ b/tw/system.scm
@@ -1,6 +1,5 @@
(define-module (tw system)
#:use-module (ice-9 string-fun)
- #:use-module (srfi srfi-26)
#:use-module (gnu)
#:use-module (gnu services)
#:use-module (gnu system)
@@ -10,6 +9,8 @@
#:use-module (tw packages catppuccin)
#:use-module (tw packages scanner)
#:use-module (tw services desktop)
+ #:use-module (tw services mail)
+ #:use-module (tw services secrets)
#:use-module (tw services wireguard))
(use-package-modules acl admin android avahi backup certs cups curl disk
@@ -114,6 +115,10 @@
(simple-service 'guix-gc mcron-service-type
(list #~(job "0 2 * * *" "guix gc -d 2w")))
+ (service mta-service-type
+ (mta-configuration
+ (host-name host-name)))
+
;; Network setup
(service dhcp-client-service-type)
(service ntp-service-type)