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:
@@ -0,0 +1,81 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// port25Addr is where compose.override.yml publishes port 25: no TLS required,
|
||||
// no AUTH offered - a stranger's mail server talking to this one.
|
||||
const port25Addr = "127.0.0.1:20025"
|
||||
|
||||
// deliverPort25 is one transaction on port 25 the way another mail server makes
|
||||
// it: HELO, MAIL, RCPT, DATA with message as it stands. The result says at
|
||||
// which stage it was refused, like attemptSend's.
|
||||
func deliverPort25(from, to, message string) sendResult {
|
||||
var res sendResult
|
||||
conn, err := net.DialTimeout("tcp", port25Addr, 10*time.Second)
|
||||
if err != nil {
|
||||
res.dialErr = fmt.Errorf("dial %s: %w", port25Addr, err)
|
||||
return res
|
||||
}
|
||||
c, err := smtp.NewClient(conn, selfpostHostname)
|
||||
if err != nil {
|
||||
res.dialErr = fmt.Errorf("smtp handshake: %w", err)
|
||||
_ = conn.Close()
|
||||
return res
|
||||
}
|
||||
defer c.Close()
|
||||
if err := c.Hello("mx.stranger.e2e.test"); err != nil {
|
||||
res.dialErr = err
|
||||
return res
|
||||
}
|
||||
if err := c.Mail(from); err != nil {
|
||||
res.mailErr = err
|
||||
_ = c.Quit()
|
||||
return res
|
||||
}
|
||||
if err := c.Rcpt(to); err != nil {
|
||||
res.rcptErr = err
|
||||
_ = c.Quit()
|
||||
return res
|
||||
}
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
res.dataErr = err
|
||||
_ = c.Quit()
|
||||
return res
|
||||
}
|
||||
if _, err := w.Write([]byte(message)); err != nil {
|
||||
res.dataErr = err
|
||||
} else if err := w.Close(); err != nil {
|
||||
res.dataErr = err
|
||||
}
|
||||
_ = c.Quit()
|
||||
return res
|
||||
}
|
||||
|
||||
// waitForPort25Ready polls port 25 until Postfix greets on it. A bare connect
|
||||
// is not enough: Docker's proxy accepts on the published port whether or not
|
||||
// anything listens behind it.
|
||||
func waitForPort25Ready(timeout time.Duration) error {
|
||||
return waitFor("port 25 to greet", timeout, 500*time.Millisecond, func() (bool, error) {
|
||||
conn, err := net.DialTimeout("tcp", port25Addr, 2*time.Second)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer conn.Close()
|
||||
_ = conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
buf := make([]byte, 4)
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if string(buf[:n]) != "220 " {
|
||||
return false, fmt.Errorf("greeting starts with %q", buf[:n])
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user