tailwart/acl-snippet.hujson
Wayne Hayes 3a9819c3ee docs: capture outbound-relay lessons (IPv6/AAAA trap, SMTP port block, sidecar ACL)
LESSONS.md gains 8-12: container has no IPv6 (AAAA fails before A, no
fallback), host IPv6 != container IPv6, VPS blocks all outbound SMTP
ports (relay over tailnet), sidecar needs a source ACL grant to
initiate, and MtaRoute changes only take effect on restart.

CLAUDE.md and .env.example warn that the smarthost address must be an
IPv4 literal or tailnet IP, never a dual-stack hostname. acl-snippet
adds the tag:stalwart -> tag:mail outbound grant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 22:43:21 +01:00

33 lines
1.7 KiB
Plaintext

// tailwart — merge into your live Tailscale policy (admin console).
// Snippet, not a full policy. Kept here so an upstream pull of any other repo
// can't clobber it.
// 1) tagOwners — add (self-ownership required for auth-key node creation):
// "tag:stalwart": ["autogroup:admin", "tag:stalwart"],
// 2) grants — Stalwart reaches the three shared backends:
{ "src": ["tag:stalwart"], "dst": ["tag:db-postgres"], "ip": ["tcp:5432"] },
{ "src": ["tag:stalwart"], "dst": ["tag:db-redis"], "ip": ["tcp:6379"] },
{ "src": ["tag:stalwart"], "dst": ["tag:garage"], "ip": ["tcp:3900"] },
// 3) grants — the edge proxy (tag:reverse-proxy) reaches the mailbox ports.
// 8080 is the JMAP/admin HTTP tier (fronted by the main L7 Caddy).
// 443 is Stalwart's HTTPS web listener; the edge L4-proxies the public
// mta-sts/autoconfig/autodiscover SNIs to it (Stalwart terminates TLS with
// its wildcard cert). PROXY protocol v2, same as the mail ports.
{
"src": ["tag:reverse-proxy"],
"dst": ["tag:stalwart"],
"ip": ["tcp:25", "tcp:465", "tcp:587", "tcp:143", "tcp:993", "tcp:443", "tcp:8080"],
},
// 4) grant — Stalwart initiates outbound relay to the smarthost (tag:mail).
// Without this, the sidecar can RECEIVE but cannot INITIATE over the tailnet
// (default-deny; the KVM host is a different identity and won't mask this).
// Adjust dst/port to match your smarthost's tag and submission port.
{ "src": ["tag:stalwart"], "dst": ["tag:mail"], "ip": ["tcp:587"] },
// 5) admin console (not this file): assign tag:stalwart to the same OAuth
// client federatedSocial uses, on the Devices/Core + Keys/AuthKeys scopes.
// Missing → 403 "calling actor does not have enough permissions" at boot.