docs: add mail runbook page (mailbox and domain add procedures)
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
# Mail
|
||||
|
||||
A single mail server, `mx1`, runs the [Simple NixOS Mailserver](https://nixos-mailserver.readthedocs.io/)
|
||||
stack (Postfix + Dovecot + Rspamd + OpenDKIM) for the `cnx.email` domain. All of
|
||||
it is declared in `modules/mail.nix`, imported by `machines/mx1/configuration.nix`.
|
||||
|
||||
Mailboxes are **virtual** (not system users): each address is a login account
|
||||
whose password is a four-word passphrase auto-generated by a clan vars generator
|
||||
(e.g. `otter-lantern-cobalt-driftwood-42`). The generator stores both the
|
||||
passphrase (handed to the user) and its sha-512 hash (consumed by SNM); plaintext
|
||||
never lands in the repo.
|
||||
|
||||
The mail cert is obtained by `mx1` itself via ACME DNS-01 against `ns1` (key
|
||||
`acme_mx1`), and carries `mx1.cnx.email` plus the `mta-sts.cnx.email` and
|
||||
`mail.cnx.email` SANs. See [DNS → ACME DNS-01](./dns.md#acme-dns-01).
|
||||
|
||||
## Add a mailbox
|
||||
|
||||
1. Append the address to `accounts` in `modules/mail.nix`:
|
||||
```nix
|
||||
accounts = [
|
||||
"postmaster@cnx.email"
|
||||
"alice@cnx.email" # new
|
||||
];
|
||||
```
|
||||
2. Mint its generated password (user-run, YubiKey touch):
|
||||
```
|
||||
clan vars generate mx1
|
||||
```
|
||||
3. Redeploy: `clan machines update mx1`.
|
||||
4. Hand the passphrase to the user. The generator name replaces `@`→`-at-` and
|
||||
`.`→`-`:
|
||||
```
|
||||
clan vars get mx1 mail-passwd-alice-at-cnx-email/passphrase
|
||||
```
|
||||
|
||||
The DMARC report inbox (`dmarc@cnx.email`) is special: its password comes from
|
||||
the **shared** `mail-dmarc-cred` generator (not the per-machine set) so parsedmarc
|
||||
on `control` can read the same passphrase over the mesh.
|
||||
|
||||
## Add a domain
|
||||
|
||||
Adding a second mail domain is more than a config line — it needs its own DNS
|
||||
records and DKIM key. Steps:
|
||||
|
||||
1. **Serve the zone** (if not already): add it in `modules/dns/domains.nix` and
|
||||
drop a `<domain>.zone` file — see [DNS](./dns.md).
|
||||
2. **Declare the domain** in `modules/mail.nix`:
|
||||
```nix
|
||||
mailserver.domains = [ "cnx.email" "newdomain.tld" ];
|
||||
```
|
||||
Add at least a `postmaster@newdomain.tld` account (RFC 5321 requires it) the
|
||||
same way as above.
|
||||
3. **Publish the mail DNS records** in the new zone, mirroring `cnx.email`'s
|
||||
`; ---- Mail ----` block:
|
||||
- `MX 10 mx1.cnx.email.` — point at the existing MX; do **not** stand up a new
|
||||
hostname unless you really want a separate server.
|
||||
- SPF: `@ TXT "v=spf1 mx -all"`.
|
||||
- DKIM: SNM generates a key per domain on first start under
|
||||
`mailserver.dkimKeyDirectory` (selector `mail`). After deploy, read the
|
||||
public key off `mx1` and paste it as `mail._domainkey TXT`. The private keys
|
||||
are declared as clan state and backed up.
|
||||
- DMARC: `_dmarc TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@cnx.email; ..."`
|
||||
— reports can keep flowing to the existing `dmarc@cnx.email` inbox.
|
||||
4. **DANE / MTA-STS are tied to `mx1.cnx.email`**, the MX hostname — they do **not**
|
||||
need duplicating per sending-domain. A new domain that uses `mx1.cnx.email` as
|
||||
its MX inherits the existing TLSA and MTA-STS policy. Only add new TLSA/MTA-STS
|
||||
records if you introduce a new MX hostname.
|
||||
5. `clan vars generate mx1` (if you added accounts), then
|
||||
`clan machines update mx1`.
|
||||
|
||||
## DKIM, DANE, MTA-STS reminders
|
||||
|
||||
These three are easy to get subtly wrong; the live records for `cnx.email` and
|
||||
the exact commands to regenerate them are in `modules/dns/zones/cnx.email.zone`:
|
||||
|
||||
- **DKIM** key rotation = regenerate under `dkimKeyDirectory`, then republish the
|
||||
`mail._domainkey TXT`.
|
||||
- **DANE TLSA** (`_25._tcp.mx1 TLSA 3 1 1 …`) is the SHA-256 of `mx1`'s cert
|
||||
**public key**. It stays valid across renewals because lego runs with
|
||||
`--reuse-key`; only recompute it if the key changes. The `openssl` one-liner is
|
||||
in the zone file.
|
||||
- **MTA-STS**: bump the `_mta-sts TXT` id **every time** the policy in
|
||||
`modules/mail.nix` changes, or senders keep the cached policy.
|
||||
|
||||
## DMARC reporting
|
||||
|
||||
`mx1` delivers aggregate/forensic DMARC reports to `dmarc@cnx.email`; parsedmarc
|
||||
on `control` polls that mailbox over the mesh and feeds the results into Grafana.
|
||||
See [Monitoring](./monitoring.md).
|
||||
Reference in New Issue
Block a user