Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLeitwolf112026-08-20 19:55:17 +0300
committerLeitwolf112026-08-20 19:55:17 +0300
commit0c46a64597e252db545334381ee73f6ed8ef4bf9 (patch)
tree1ab575ea785e11376f8eab38cdca45a146599ffd
parent2804c141aacf27d2e4ca381a39b4ffa2ea36c6c8 (diff)
downloadwebsite-0c46a64597e252db545334381ee73f6ed8ef4bf9.tar.gz
Provision the support mailbox boundary with reproducible Postfix inbound delivery, Dovecot IMAPS, authenticated STARTTLS submission, sender ownership enforcement, SELinux Maildir policy, and certificate renewal hooks
-rw-r--r--ops/mail/README.md35
-rw-r--r--ops/mail/certbot-deploy-hook.sh7
-rw-r--r--ops/mail/dovecot-local.conf36
-rw-r--r--ops/mail/postfix-master-submission.txt13
-rw-r--r--ops/mail/postfix-settings.txt15
-rw-r--r--ops/mail/sender_login4
6 files changed, 101 insertions, 9 deletions
diff --git a/ops/mail/README.md b/ops/mail/README.md
index c942d8e..a9f1c63 100644
--- a/ops/mail/README.md
+++ b/ops/mail/README.md
@@ -5,13 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-or-later
# Transactional mail boundary
-The registry API submits verification and password-recovery messages to Postfix over the loopback interface. Postfix is
-send-only, requires TLS for remote SMTP delivery, and signs `xsharp-lang.xyz` mail with OpenDKIM selector `registry`.
-The `noreply@xsharp-lang.xyz` address is an automated sender, not a mailbox or support address.
+The registry API submits verification and password-recovery messages to Postfix over the loopback interface. Postfix
+requires TLS for remote SMTP delivery and signs `xsharp-lang.xyz` mail with OpenDKIM selector `registry`. The automated
+`noreply@xsharp-lang.xyz` sender remains non-login. `support@xsharp-lang.xyz` is the only interactive mailbox and is
+delivered as Maildir to the dedicated, non-shell `support` account.
Production DNS publishes:
- an unproxied `mail.xsharp-lang.xyz` address record;
+- an MX record with priority 10 targeting `mail.xsharp-lang.xyz`;
- a strict SPF record authorizing the registry host;
- the `registry._domainkey` public key;
- a strict-alignment DMARC policy.
@@ -20,6 +22,27 @@ The private DKIM key and ASP.NET Core connection/OAuth secrets live only on the
hosting provider should set reverse DNS for `193.111.77.89` to `mail.xsharp-lang.xyz`; this cannot be configured through
Cloudflare DNS.
-Install both `opendkim` and `opendkim-tools`. Keep the selector private key owned by `opendkim:opendkim` with mode
-`0600`, and make every parent key directory traversable by the `opendkim` group. On SELinux hosts, restore the packaged
-OpenDKIM contexts before starting the service.
+Install `postfix`, `opendkim`, `opendkim-tools`, `dovecot`, and `certbot`. Keep the selector private key owned by
+`opendkim:opendkim` with mode `0600`, and make every parent key directory traversable by the `opendkim` group. On SELinux
+hosts, restore packaged contexts before starting the services.
+
+The support password is supplied only through `XSHARP_SUPPORT_EMAIL_PASSWORD` while provisioning. It is converted to the
+host's password hash and must never be copied into a repository, environment file, or Postfix/Dovecot configuration.
+Remove the legacy `support: postmaster` alias before enabling local delivery, because aliases take precedence over the
+local account.
+
+The dedicated home and every Maildir directory are owned by `support:support` with mode `0700`. Because this deployment
+keeps mail under `/var/lib/xsharp-mail` instead of `/home`, SELinux must persist `user_home_dir_t` on the account home and
+`mail_home_rw_t` on `Maildir(/.*)?` through `semanage fcontext`, followed by `restorecon`. A generic `var_lib_t` context
+allows login but blocks both Postfix delivery and Dovecot index writes.
+
+Client settings:
+
+- address and username: `support@xsharp-lang.xyz`;
+- incoming mail: IMAPS at `mail.xsharp-lang.xyz:993`, TLS required;
+- outgoing mail: submission at `mail.xsharp-lang.xyz:587`, STARTTLS required;
+- SMTP sender: exactly `support@xsharp-lang.xyz`.
+
+Port 25 accepts mail for the local domain but never authenticates users or acts as an open relay. Port 587 requires
+Dovecot authentication and rejects an authenticated account that tries to use another sender address. Certificate
+renewal installs `certbot-deploy-hook.sh` so Postfix and Dovecot reload the renewed key material.
diff --git a/ops/mail/certbot-deploy-hook.sh b/ops/mail/certbot-deploy-hook.sh
new file mode 100644
index 0000000..8021ee8
--- /dev/null
+++ b/ops/mail/certbot-deploy-hook.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# SPDX-FileCopyrightText: 2026 Leitwolf <xs-lang.chess031@slmails.com>
+# SPDX-License-Identifier: Apache-2.0
+
+set -eu
+systemctl reload postfix
+systemctl reload dovecot
diff --git a/ops/mail/dovecot-local.conf b/ops/mail/dovecot-local.conf
new file mode 100644
index 0000000..81947a2
--- /dev/null
+++ b/ops/mail/dovecot-local.conf
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: 2026 Leitwolf <xs-lang.chess031@slmails.com>
+# SPDX-License-Identifier: Apache-2.0
+
+protocols = imap
+listen = *, ::
+mail_location = maildir:~/Maildir
+mail_privileged_group = mail
+first_valid_uid = 1000
+
+disable_plaintext_auth = yes
+auth_mechanisms = plain login
+auth_username_format = %Ln
+
+ssl = required
+ssl_min_protocol = TLSv1.2
+ssl_prefer_server_ciphers = yes
+ssl_cert = </etc/letsencrypt/live/mail.xsharp-lang.xyz/fullchain.pem
+ssl_key = </etc/letsencrypt/live/mail.xsharp-lang.xyz/privkey.pem
+
+service auth {
+ unix_listener /var/spool/postfix/private/auth {
+ mode = 0660
+ user = postfix
+ group = postfix
+ }
+}
+
+service imap-login {
+ inet_listener imap {
+ port = 0
+ }
+ inet_listener imaps {
+ port = 993
+ ssl = yes
+ }
+}
diff --git a/ops/mail/postfix-master-submission.txt b/ops/mail/postfix-master-submission.txt
new file mode 100644
index 0000000..2ee91c7
--- /dev/null
+++ b/ops/mail/postfix-master-submission.txt
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2026 Leitwolf <xs-lang.chess031@slmails.com>
+# SPDX-License-Identifier: Apache-2.0
+
+submission inet n - n - - smtpd
+ -o syslog_name=postfix/submission
+ -o smtpd_tls_security_level=encrypt
+ -o smtpd_sasl_auth_enable=yes
+ -o smtpd_sasl_type=dovecot
+ -o smtpd_sasl_path=private/auth
+ -o smtpd_sasl_security_options=noanonymous
+ -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+ -o smtpd_sender_login_maps=lmdb:/etc/postfix/sender_login
+ -o smtpd_sender_restrictions=reject_authenticated_sender_login_mismatch
diff --git a/ops/mail/postfix-settings.txt b/ops/mail/postfix-settings.txt
index 5009040..f5e7425 100644
--- a/ops/mail/postfix-settings.txt
+++ b/ops/mail/postfix-settings.txt
@@ -4,15 +4,24 @@
myhostname = mail.xsharp-lang.xyz
mydomain = xsharp-lang.xyz
myorigin = $mydomain
-inet_interfaces = loopback-only
+inet_interfaces = all
inet_protocols = ipv4
-mydestination = localhost
+mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
mynetworks = 127.0.0.0/8
+home_mailbox = Maildir/
disable_vrfy_command = yes
-smtpd_relay_restrictions = permit_mynetworks,reject
+smtpd_relay_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
+smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
smtp_tls_security_level = encrypt
smtp_tls_CApath = /etc/pki/tls/certs
smtp_tls_loglevel = 1
+smtpd_tls_cert_file = /etc/letsencrypt/live/mail.xsharp-lang.xyz/fullchain.pem
+smtpd_tls_key_file = /etc/letsencrypt/live/mail.xsharp-lang.xyz/privkey.pem
+smtpd_tls_security_level = may
+smtpd_tls_auth_only = yes
+smtpd_sasl_type = dovecot
+smtpd_sasl_path = private/auth
+smtpd_sasl_auth_enable = no
milter_default_action = tempfail
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891
diff --git a/ops/mail/sender_login b/ops/mail/sender_login
new file mode 100644
index 0000000..def4f4d
--- /dev/null
+++ b/ops/mail/sender_login
@@ -0,0 +1,4 @@
+# SPDX-FileCopyrightText: 2026 Leitwolf <xs-lang.chess031@slmails.com>
+# SPDX-License-Identifier: Apache-2.0
+
+support@xsharp-lang.xyz support