e2e: the run ends on port 25, with a report, junk and inbound mail

Until now the suite only read back the configuration generated for the
two optional port-25 features. DMARC ingest shipped in 1.7.0 with a
delivery service Postfix could not start, and nothing sent a report
through it until a real one arrived on the test server on 2026-10-11.

The last steps recreate the container with INBOUND_RELAY_ENABLE and
DMARC_REPORTS_ENABLE on, over the same /data, and talk to port 25 as a
stranger's mail server:

- the container log has no postconf warning, the session survives;
- a domain takes the SelfPost-hosted report address; a gzip report
  mailed to it appears under Outbound > DMARC reports; an address nobody
  chose is refused at RCPT;
- a report on another domain and a message with no report are accepted,
  dropped with "dropped: ..." on their delivery lines, counted as two
  parse failures, absent from the queue and from the reports;
- an inbound domain with the sink as upstream and one listed recipient:
  mail for it reaches the sink, another recipient and another domain are
  refused at RCPT.

The stage directory now holds docker-compose.yml and .env, as an
operator's project directory does (a full backup needs them; the
restore scenario that uses it is not in this commit - the panel's full
backup turned out not to work, see the next one).

make e2e on the test server: ok.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-11 17:40:47 +03:00
co-authored by Claude Opus 5.5
parent 4dd46ea355
commit 8496b289ec
6 changed files with 444 additions and 4 deletions
+13
View File
@@ -24,6 +24,10 @@ type stack struct {
baseComposeFile string
overrideFile string
stageDir string
// env is added to compose's own environment. The shipped compose file takes
// the optional features from there (${INBOUND_RELAY_ENABLE:-false} and the
// like), so this is how a later part of the run switches them on.
env []string
}
func newStack() (*stack, error) {
@@ -64,6 +68,7 @@ func (s *stack) compose(args ...string) ([]byte, error) {
// looked at (that mapping only reaches the container, not compose's own
// variable interpolation), so it has to be set here too.
cmd.Env = append(os.Environ(), "SELFPOST_HOSTNAME="+selfpostHostname)
cmd.Env = append(cmd.Env, s.env...)
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
@@ -102,6 +107,14 @@ func (s *stack) restartSelfpost() error {
return err
}
// recreateSelfpost replaces the selfpost container with one started from the
// same /data and the environment as it is now (see env), leaving the DNS and
// sink sidecars and their state alone.
func (s *stack) recreateSelfpost() error {
_, err := s.compose("up", "-d", "--force-recreate", "--no-deps", "selfpost")
return err
}
func (s *stack) down() {
_, _ = s.compose("down", "-v", "--remove-orphans")
}