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