Port 25 accepts only configured domains and listed recipients, then forwards to an upstream; the outbound path is unchanged when the flag is off.
Co-authored-by: Cursor <cursoragent@cursor.com>
- internal/web/auth/ratelimit_test.go: the login/setup limiter's ceiling,
per-address scope, window reset and the sweep that keeps finished
buckets out of memory.
- internal/web/auth/handlers_test.go: sign-in (session issued, refusals
that do not reveal which usernames exist, a lockout a correct password
cannot bypass, the setup hint before the first administrator) and the
one-time setup link (creates the first global administrator and then
closes, rejects a wrong or expired token, refuses credentials the panel
would not accept later, rate-limited).
- internal/web/handlers/authz_test.go: every global-only route answers a
domain administrator — and a request with no principal — with 404, with
a positive control so the table cannot pass on a handler that always
404s. This is the check that would have caught the send-log leak.
- test/e2e: CoreDNS pinned to 1.14.6 instead of latest; the level-1
failure message quoted RATE_LIMIT_MESSAGES_PER_IP=5 while the stand
sets 50.
docs/plans/code-review.md P4 checked off except the optional backup-boot
test; CHANGELOG updated. No production code changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Pin compose and local trial to ghcr.io/mixeme/selfpost:1.0.0, close the
CHANGELOG cut, and retire implementation-plan / v1.x-closure-plan.
Includes the post-cut startup fixes needed for a green release e2e gate:
root-owned TLS copies for postfix check, maillog_file_prefixes for /data,
hostname gate and traversable /data, panel /healthz before setup, and
setup-token / TempDir reclaim via docker exec.
Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The PTR check reported a correctly published record as wrong. The lookups
went through the container's resolver (127.0.0.11) which forwards to the
host's systemd-resolved, and systemd-resolved synthesises the reverse
lookup of the machine's own addresses from the local hostname rather than
asking public DNS. On the production host that meant
203.0.113.10 -> provider-assigned-hostname (does not match)
while public DNS has had 203.0.113.10 -> selfpost.example.com all along.
These checks exist to report what a receiving mail server sees, so they
now dial recursive resolvers themselves, defaulting to 1.1.1.1, 8.8.8.8
and 9.9.9.9 and overridable with SELFPOST_DNS_RESOLVERS. The e2e stand
sets it to its CoreDNS, which the `dns:` directive alone no longer covers.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Separate test/e2e Go module drives the shipped deploy/docker-compose.yml
(plus a test-only override: self-signed cert, low ports, isolated compose
project) against a fake DNS zone (CoreDNS) and an smtp-sink MX, exactly as
an administrator and their applications would over HTTP/SMTP — covering the
class of failure unit tests can't see (container wiring). Positive path:
setup -> login -> domain -> DKIM record published into the fake zone ->
application -> SMTP AUTH send -> DKIM verified against the DNS-published
key -> send-log queued->sent. Negative: no-AUTH/unauthenticated relay,
sender/login mismatch, L1 (anvil) and L2 (panel) rate limits, journal-milter
fail-open, SELFPOST_HOSTNAME gate, session survives docker restart.
release.yml moves off qemu to a native per-arch build (amd64/arm64), each
gated by this suite before its tag is pushed and merged into the version
manifest.
Verified green on selfpost.example.com via `make e2e`; go vet/gofmt clean in
both modules.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>