diff options
| author | Leitwolf11 | 2026-08-20 19:55:17 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-08-20 19:55:17 +0300 |
| commit | 0c46a64597e252db545334381ee73f6ed8ef4bf9 (patch) | |
| tree | 1ab575ea785e11376f8eab38cdca45a146599ffd /ops | |
| parent | 2804c141aacf27d2e4ca381a39b4ffa2ea36c6c8 (diff) | |
| download | website-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
Diffstat (limited to 'ops')
| -rw-r--r-- | ops/mail/README.md | 35 | ||||
| -rw-r--r-- | ops/mail/certbot-deploy-hook.sh | 7 | ||||
| -rw-r--r-- | ops/mail/dovecot-local.conf | 36 | ||||
| -rw-r--r-- | ops/mail/postfix-master-submission.txt | 13 | ||||
| -rw-r--r-- | ops/mail/postfix-settings.txt | 15 | ||||
| -rw-r--r-- | ops/mail/sender_login | 4 |
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 |