#17: implement DANE TLSA, MTA-STS policy resolution, ARC sealing and mail authentication
This commit is contained in:
1 parent
df8b809683
commit
bd0af98076
22 files changed
+3296
No files matched your search
@@ -0,0 +1,18 @@
|
||||
package port
|
||||
|
||||
import "context"
|
||||
|
||||
// ArcSealer adds an Authenticated Received Chain set to a message
|
||||
// (RFC 8617, PLAN.md section 5).
|
||||
//
|
||||
// It matters on a host that passes mail onward: a forwarder that modifies a
|
||||
// message breaks its DKIM signature, and the final receiver then sees a DMARC
|
||||
// failure for mail that authenticated correctly at the first hop. The ARC set
|
||||
// records this hop's verdict and seals it so the failure can be recognised as
|
||||
// a forwarding artefact rather than a forgery.
|
||||
type ArcSealer interface {
|
||||
// Seal returns the message with an ARC set prepended. A chain that
|
||||
// already failed is returned untouched: RFC 8617 section 5.1.2 forbids
|
||||
// extending it.
|
||||
Seal(ctx context.Context, message []byte, authResult InboundAuthResult) ([]byte, error)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package port
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"lambdamail/protocols/internal/domain/entity"
|
||||
)
|
||||
|
||||
// DnsVerifier re-reads published records through resolvers outside our
|
||||
// control. PLAN.md section 7.5 requires this step because the provider API
|
||||
// confirming a write only proves the API accepted it, not that the world can
|
||||
// see it.
|
||||
type DnsVerifier interface {
|
||||
// VerifyRecord reports whether the record is visible with the expected
|
||||
// value. A lookup failure is reported as not-visible with the reason,
|
||||
// never as a hard error, so one unreachable resolver cannot fail a sync.
|
||||
VerifyRecord(ctx context.Context, record entity.DnsRecord) (visible bool, detail string)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package port
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Authentication result values as they appear in an Authentication-Results
|
||||
// header (RFC 8601 section 2.7) and in the email_messages columns.
|
||||
const (
|
||||
AuthResultNone = "none"
|
||||
AuthResultPass = "pass"
|
||||
AuthResultFail = "fail"
|
||||
AuthResultSoftFail = "softfail"
|
||||
AuthResultNeutral = "neutral"
|
||||
AuthResultTempError = "temperror"
|
||||
AuthResultPermError = "permerror"
|
||||
)
|
||||
|
||||
// InboundAuthInput is everything the authentication checks need about one
|
||||
// inbound transaction.
|
||||
type InboundAuthInput struct {
|
||||
ClientIP net.IP
|
||||
HeloDomain string
|
||||
EnvelopeFrom string
|
||||
Message []byte
|
||||
}
|
||||
|
||||
// InboundAuthResult carries the verdicts recorded against the message and
|
||||
// surfaced to the user (PLAN.md section 6.1).
|
||||
type InboundAuthResult struct {
|
||||
SPF string
|
||||
DKIM string
|
||||
DMARC string
|
||||
|
||||
// DmarcPolicy is the policy the sending domain published: "none",
|
||||
// "quarantine" or "reject". Empty when the domain publishes no record.
|
||||
DmarcPolicy string
|
||||
|
||||
// AuthenticationResults is the RFC 8601 header to prepend to the stored
|
||||
// message so downstream filters and the user can see how it was judged.
|
||||
AuthenticationResults string
|
||||
}
|
||||
|
||||
// MailAuthenticator runs SPF, DKIM and DMARC over an inbound message.
|
||||
type MailAuthenticator interface {
|
||||
// Authenticate never fails the delivery on its own: a DNS outage yields
|
||||
// a temperror verdict, not an error.
|
||||
Authenticate(ctx context.Context, input InboundAuthInput) InboundAuthResult
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package port
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"lambdamail/protocols/internal/domain/entity"
|
||||
)
|
||||
|
||||
// TLSPolicyResolver determines the transport security policy that applies to
|
||||
// one outbound destination, combining DANE and MTA-STS with the precedence of
|
||||
// RFC 8461 section 2 (PLAN.md section 6.2).
|
||||
type TLSPolicyResolver interface {
|
||||
// Resolve never returns an error for a destination that simply publishes
|
||||
// no policy: that is an opportunistic destination, not a failure. It
|
||||
// errors only when a published policy could not be evaluated, which the
|
||||
// caller must treat as a reason to defer rather than to downgrade.
|
||||
Resolve(ctx context.Context, destinationDomain string, mxHost string) (entity.TLSPolicy, error)
|
||||
}
|
||||
Reference in new issue
Block a user