Documentation
¶
Overview ¶
Package tlsrpt implements SMTP TLS Reporting, RFC 8460.
TLSRPT allows a domain to publish a policy requesting feedback of TLS connectivity to its SMTP servers. Reports can be sent to an address defined in the TLSRPT DNS record. These reports can be parsed by tlsrpt.
Index ¶
- Variables
- func FormatAlert(alert uint8) string
- type Extension
- type FailureDetails
- type FailureDetailsJSON
- type PolicyType
- type RUA
- type Record
- type Report
- type ReportJSON
- type Result
- type ResultJSON
- type ResultPolicy
- type ResultPolicyJSON
- type ResultType
- type Summary
- type SummaryJSON
- type TLSRPTDateRange
- type TLSRPTDateRangeJSON
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrNoRecord = errors.New("tlsrpt: no tlsrpt dns txt record") ErrMultipleRecords = errors.New("tlsrpt: multiple tlsrpt records") // Must be treated as if domain does not implement TLSRPT. ErrDNS = errors.New("tlsrpt: temporary error") ErrRecordSyntax = errors.New("tlsrpt: record syntax error") )
var ErrNoReport = errors.New("no tlsrpt report found")
var (
MetricLookup stub.HistogramVec = stub.HistogramVecIgnore{}
)
Functions ¶
func FormatAlert ¶ added in v0.0.14
FormatAlert formats a TLS alert in the form "alert-<num>" or "alert-<num>-<shortcode>".
Types ¶
type FailureDetails ¶
type FailureDetails struct { ResultType ResultType SendingMTAIP string ReceivingMXHostname string ReceivingMXHelo string ReceivingIP string FailedSessionCount int64 AdditionalInformation string FailureReasonCode string }
func Details ¶ added in v0.0.8
func Details(t ResultType, r string) FailureDetails
Details is a convenience function to compose a FailureDetails.
func (FailureDetails) Convert ¶ added in v0.0.9
func (v FailureDetails) Convert() FailureDetailsJSON
type FailureDetailsJSON ¶ added in v0.0.9
type FailureDetailsJSON struct { ResultType ResultType `json:"result-type"` SendingMTAIP string `json:"sending-mta-ip"` ReceivingMXHostname string `json:"receiving-mx-hostname"` ReceivingMXHelo string `json:"receiving-mx-helo,omitempty"` ReceivingIP string `json:"receiving-ip"` FailedSessionCount int64 `json:"failed-session-count"` AdditionalInformation string `json:"additional-information"` FailureReasonCode string `json:"failure-reason-code"` }
func (FailureDetailsJSON) Convert ¶ added in v0.0.9
func (v FailureDetailsJSON) Convert() FailureDetails
type PolicyType ¶ added in v0.0.8
type PolicyType string
PolicyType indicates the policy success/failure results are for.
const ( // For DANE, against a mail host (not recipient domain). TLSA PolicyType = "tlsa" // For MTA-STS, against a recipient domain (not a mail host). STS PolicyType = "sts" // Recipient domain did not have MTA-STS policy, or mail host (TSLA base domain) // did not have DANE TLSA records. NoPolicyFound PolicyType = "no-policy-found" )
type RUA ¶ added in v0.0.8
type RUA string
RUA is a reporting address with scheme and special characters ",", "!" and ";" not encoded.
type Record ¶
type Record struct { Version string // "TLSRPTv1", for "v=". // Aggregate reporting URI, for "rua=". "rua=" can occur multiple times, each can // be a list. RUAs [][]RUA Extensions []Extension }
Record is a parsed TLSRPT record, to be served under "_smtp._tls.<domain>".
Example:
v=TLSRPTv1; rua=mailto:tlsrpt@mox.example;
func Lookup ¶
func Lookup(ctx context.Context, elog *slog.Logger, resolver dns.Resolver, domain dns.Domain) (rrecord *Record, rtxt string, rerr error)
Lookup looks up a TLSRPT DNS TXT record for domain at "_smtp._tls.<domain>" and parses it.
Example ¶
package main import ( "context" "log" "log/slog" "github.com/mjl-/mox/dns" "github.com/mjl-/mox/tlsrpt" ) func main() { ctx := context.Background() resolver := dns.StrictResolver{} domain, err := dns.ParseDomain("domain.example") if err != nil { log.Fatalf("parsing domain: %v", err) } // Lookup TLSRPT record in DNS, and parse it. record, txt, err := tlsrpt.Lookup(ctx, slog.Default(), resolver, domain) if err != nil { log.Fatalf("looking up tlsrpt record: %v", err) } log.Printf("TLSRPT record: %s", txt) log.Printf("Parsed: %v", record) }
Output:
func ParseRecord ¶
ParseRecord parses a TLSRPT record.
type Report ¶
type Report struct { OrganizationName string DateRange TLSRPTDateRange ContactInfo string ReportID string Policies []Result }
Report is a TLSRPT report.
func (*Report) Add ¶ added in v0.0.8
func (r *Report) Add(policy ResultPolicy, success, failure int64, fds ...FailureDetails)
Add is a convenience function for merging making a Result and merging it into the report.
func (Report) Convert ¶ added in v0.0.9
func (v Report) Convert() ReportJSON
type ReportJSON ¶ added in v0.0.9
type ReportJSON struct { OrganizationName string `json:"organization-name"` DateRange TLSRPTDateRangeJSON `json:"date-range"` ContactInfo string `json:"contact-info"` // Email address. ReportID string `json:"report-id"` Policies []ResultJSON `json:"policies"` }
ReportJSON is a TLS report with field names as used in the specification. These field names are inconvenient to use in JavaScript, so after parsing a ReportJSON is turned into a Report.
func Parse ¶
func Parse(r io.Reader) (*ReportJSON, error)
Parse parses a Report. The maximum size is 20MB.
func ParseMessage ¶
ParseMessage parses a Report from a mail message. The maximum size of the message is 15MB, the maximum size of the decompressed report is 20MB.
Example ¶
package main import ( "log" "log/slog" "strings" "github.com/mjl-/mox/tlsrpt" ) func main() { // Message, as received over SMTP. msg := `From: <tlsrpt@mail.sender.example.com> To: <mts-sts-tlsrpt@example.net> Subject: Report Domain: example.net Report-ID: <735ff.e317+bf22029@example.net> TLS-Report-Domain: example.net TLS-Report-Submitter: mail.sender.example.com MIME-Version: 1.0 Content-Type: application/tlsrpt+gzip Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="mail.sender.example!example.com!1013662812!1013749130.json.gz" H4sIAPZreGUAA51UbW/aMBD+DL/Cyr5NdeokJIVI0za1aPswdRWgiXWqImMbai2JI9tBMMR/n52Y lwkx2KQocXx3vud57s6bbscTcoFL/gtrLkpY4oJ5KfDuRVHhcg2n3o1xoVgzKHG5sLZNt9PxlMZS Q7uveRsRoiCBqAdRMEEobZ5nG9zxWEnPeYZRGg/M8+x1O1ubiYhSY6IhL+fC+iqtoGSVkJqXiw/E oVr5bIWLKmcNutYOObUBMUriXnhHYBjRCPbuCIazhCE46CUMI9YnvVkbVYmcE86UCfphUFpWbnPt SO7/oV5XzKFpKB0sSksDzJ1h95dMKiNkCsaT8TJw3h2vEJSlQDNleRx2Vyl46xeY5/6O2vqYWuuE VxlemOh+0kPIa3Zf/kRBhTmjtAjPHWNSwVeh9BHSs4nbDPa9bYI9VRcFlkeyaKFxDlVNCFNqXpul +dr2IaIubY44CpObY9+5SVVLduIYoego0c6LMm1Wag924yBLpupc78tBmGmLOSe2O9mq4pLRvWrK dJ2RGhYaQ161bYeClM76KZ4RarozCNP0UCDJCOPLJqJVajcJxSq4VCELm9ETbgFCjUNL7hyJZpJ0 rmAptJG0sr38bzyiK3mEl3gcYneZIh/5QRD5cXKJrEG188CUcnuZmLLbMZZFc7XYA1+1rlR6e9tO rPJP5tlZMhsH3gNOwTvgJhoQAEEYARq9AWOn2aPQ451iwLtC7CXOOW1vOtdrfxE6GPT9OPBNGf0k vEak/jVVgDNMftbVf/ZUdGy3oyIZVo2dNudPYzTIvmXD0Sh7Gn2dfs+ePk4+Z1+Gj5/MZzi9Hw4f hg9Oqa4bc7N46W67vwF2Eq+hDAYAAA== ` msg = strings.ReplaceAll(msg, "\n", "\r\n") // Parse the email message, and the TLSRPT report within. reportJSON, err := tlsrpt.ParseMessage(slog.Default(), strings.NewReader(msg)) if err != nil { log.Fatalf("parsing tlsrpt report in message: %v", err) } log.Printf("report: %#v", reportJSON) }
Output:
func (ReportJSON) Convert ¶ added in v0.0.9
func (v ReportJSON) Convert() Report
type Result ¶
type Result struct { Policy ResultPolicy Summary Summary FailureDetails []FailureDetails }
func MakeResult ¶ added in v0.0.8
func MakeResult(policyType PolicyType, domain dns.Domain, fds ...FailureDetails) Result
func (*Result) Add ¶ added in v0.0.8
func (r *Result) Add(success, failure int64, fds ...FailureDetails)
Add increases the success/failure counts of a result, and adds any failure details.
func (Result) Convert ¶ added in v0.0.9
func (r Result) Convert() ResultJSON
type ResultJSON ¶ added in v0.0.9
type ResultJSON struct { Policy ResultPolicyJSON `json:"policy"` Summary SummaryJSON `json:"summary"` FailureDetails []FailureDetailsJSON `json:"failure-details"` }
func (ResultJSON) Convert ¶ added in v0.0.9
func (r ResultJSON) Convert() Result
type ResultPolicy ¶
type ResultPolicy struct { Type PolicyType String []string Domain string // ASCII/A-labels, ../rfc/8460:704 MXHost []string }
func TLSAPolicy ¶ added in v0.0.8
func TLSAPolicy(records []adns.TLSA, tlsaBaseDomain dns.Domain) ResultPolicy
TLSAPolicy returns a policy for DANE.
type ResultPolicyJSON ¶ added in v0.0.9
type ResultPolicyJSON struct { Type PolicyType `json:"policy-type"` String []string `json:"policy-string"` Domain string `json:"policy-domain"` MXHost []string `json:"mx-host"` // Example in RFC has errata, it originally was a single string. ../rfc/8460-eid6241 ../rfc/8460:1779 }
type ResultType ¶
type ResultType string
ResultType represents a TLS error.
const ( ResultSTARTTLSNotSupported ResultType = "starttls-not-supported" ResultCertificateHostMismatch ResultType = "certificate-host-mismatch" ResultCertificateExpired ResultType = "certificate-expired" ResultTLSAInvalid ResultType = "tlsa-invalid" ResultDNSSECInvalid ResultType = "dnssec-invalid" ResultDANERequired ResultType = "dane-required" ResultCertificateNotTrusted ResultType = "certificate-not-trusted" ResultSTSPolicyInvalid ResultType = "sts-policy-invalid" ResultSTSWebPKIInvalid ResultType = "sts-webpki-invalid" ResultValidationFailure ResultType = "validation-failure" // Other error. ResultSTSPolicyFetch ResultType = "sts-policy-fetch-error" )
func TLSFailureDetails ¶ added in v0.0.8
func TLSFailureDetails(err error) (ResultType, string)
TLSFailureDetails turns errors encountered during TLS handshakes into a result type and failure reason code for use with FailureDetails.
Errors from crypto/tls, including local and remote alerts, from crypto/x509, and generic i/o and timeout errors are recognized.
type SummaryJSON ¶ added in v0.0.9
type TLSRPTDateRange ¶
note: with TLSRPT prefix to prevent clash in sherpadoc types.
func (TLSRPTDateRange) Convert ¶ added in v0.0.9
func (v TLSRPTDateRange) Convert() TLSRPTDateRangeJSON
type TLSRPTDateRangeJSON ¶ added in v0.0.9
type TLSRPTDateRangeJSON struct { Start time.Time `json:"start-datetime"` End time.Time `json:"end-datetime"` }
func (TLSRPTDateRangeJSON) Convert ¶ added in v0.0.9
func (v TLSRPTDateRangeJSON) Convert() TLSRPTDateRange
func (*TLSRPTDateRangeJSON) UnmarshalJSON ¶ added in v0.0.9
func (dr *TLSRPTDateRangeJSON) UnmarshalJSON(buf []byte) error
UnmarshalJSON is defined on the date range, not the individual time.Time fields because it is easier to keep the unmodified time.Time fields stored in the database.