diff --git a/admin/admin.go b/admin/admin.go index 048993d..cbe66ad 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -219,12 +219,12 @@ func MakeDomainConfig(ctx context.Context, domain, hostname dns.Domain, accountN DKIM: confDKIM, DMARC: &config.DMARC{ Account: accountName, - Localpart: "dmarc-reports", + Localpart: "dmarcreports", Mailbox: "DMARC", }, TLSRPT: &config.TLSRPT{ Account: accountName, - Localpart: "tls-reports", + Localpart: "tlsreports", Mailbox: "TLSRPT", }, } diff --git a/config/config.go b/config/config.go index c07fb0b..8cbe745 100644 --- a/config/config.go +++ b/config/config.go @@ -61,7 +61,7 @@ type Static struct { HostTLSRPT struct { Account string `sconf-doc:"Account to deliver TLS reports to. Typically same account as for postmaster."` Mailbox string `sconf-doc:"Mailbox to deliver TLS reports to. Recommended value: TLSRPT."` - Localpart string `sconf-doc:"Localpart at hostname to accept TLS reports at. Recommended value: tls-reports."` + Localpart string `sconf-doc:"Localpart at hostname to accept TLS reports at. Recommended value: tlsreports."` ParsedLocalpart smtp.Localpart `sconf:"-"` } `sconf:"optional" sconf-doc:"Destination for per-host TLS reports (TLSRPT). TLS reports can be per recipient domain (for MTA-STS), or per MX host (for DANE). The per-domain TLS reporting configuration is in domains.conf. This is the TLS reporting configuration for this host. If absent, no host-based TLSRPT address is configured, and no host TLSRPT DNS record is suggested."` @@ -324,7 +324,7 @@ type AliasAddress struct { } type DMARC struct { - Localpart string `sconf-doc:"Address-part before the @ that accepts DMARC reports. Must be non-internationalized. Recommended value: dmarc-reports."` + Localpart string `sconf-doc:"Address-part before the @ that accepts DMARC reports. Must be non-internationalized. Recommended value: dmarcreports."` Domain string `sconf:"optional" sconf-doc:"Alternative domain for reporting address, for incoming reports. Typically empty, causing the domain wherein this config exists to be used. Can be used to receive reports for domains that aren't fully hosted on this server. Configure such a domain as a hosted domain without making all the DNS changes, and configure this field with a domain that is fully hosted on this server, so the localpart and the domain of this field form a reporting address. Then only update the DMARC DNS record for the not fully hosted domain, ensuring the reporting address is specified in its \"rua\" field as shown in the suggested DNS settings. Unicode name."` Account string `sconf-doc:"Account to deliver to."` Mailbox string `sconf-doc:"Mailbox to deliver to, e.g. DMARC."` @@ -342,7 +342,7 @@ type MTASTS struct { } type TLSRPT struct { - Localpart string `sconf-doc:"Address-part before the @ that accepts TLSRPT reports. Recommended value: tls-reports."` + Localpart string `sconf-doc:"Address-part before the @ that accepts TLSRPT reports. Recommended value: tlsreports."` Domain string `sconf:"optional" sconf-doc:"Alternative domain for reporting address, for incoming reports. Typically empty, causing the domain wherein this config exists to be used. Can be used to receive reports for domains that aren't fully hosted on this server. Configure such a domain as a hosted domain without making all the DNS changes, and configure this field with a domain that is fully hosted on this server, so the localpart and the domain of this field form a reporting address. Then only update the TLSRPT DNS record for the not fully hosted domain, ensuring the reporting address is specified in its \"rua\" field as shown in the suggested DNS settings. Unicode name."` Account string `sconf-doc:"Account to deliver to."` Mailbox string `sconf-doc:"Mailbox to deliver to, e.g. TLSRPT."` diff --git a/config/doc.go b/config/doc.go index 37c8e5a..af18d28 100644 --- a/config/doc.go +++ b/config/doc.go @@ -546,7 +546,7 @@ See https://pkg.go.dev/github.com/mjl-/sconf for details. # Mailbox to deliver TLS reports to. Recommended value: TLSRPT. Mailbox: - # Localpart at hostname to accept TLS reports at. Recommended value: tls-reports. + # Localpart at hostname to accept TLS reports at. Recommended value: tlsreports. Localpart: # Mailboxes to create for new accounts. Inbox is always created. Mailboxes can be @@ -864,7 +864,7 @@ See https://pkg.go.dev/github.com/mjl-/sconf for details. DMARC: # Address-part before the @ that accepts DMARC reports. Must be - # non-internationalized. Recommended value: dmarc-reports. + # non-internationalized. Recommended value: dmarcreports. Localpart: # Alternative domain for reporting address, for incoming reports. Typically empty, @@ -932,7 +932,7 @@ See https://pkg.go.dev/github.com/mjl-/sconf for details. TLSRPT: # Address-part before the @ that accepts TLSRPT reports. Recommended value: - # tls-reports. + # tlsreports. Localpart: # Alternative domain for reporting address, for incoming reports. Typically empty, diff --git a/quickstart.go b/quickstart.go index 8a0f4fc..0b88f73 100644 --- a/quickstart.go +++ b/quickstart.go @@ -883,7 +883,7 @@ and check the admin page for the needed DNS records.`) sc.Postmaster.Account = accountName sc.Postmaster.Mailbox = "Postmaster" sc.HostTLSRPT.Account = accountName - sc.HostTLSRPT.Localpart = "tls-reports" + sc.HostTLSRPT.Localpart = "tlsreports" sc.HostTLSRPT.Mailbox = "TLSRPT" mox.ConfigStaticPath = filepath.FromSlash("config/mox.conf") diff --git a/tlsrptsend/send_test.go b/tlsrptsend/send_test.go index e69ceb1..f5518e5 100644 --- a/tlsrptsend/send_test.go +++ b/tlsrptsend/send_test.go @@ -54,13 +54,13 @@ func TestSendReports(t *testing.T) { resolver := dns.MockResolver{ TXT: map[string][]string{ "_smtp._tls.xn--74h.example.": { - "v=TLSRPTv1; rua=mailto:tls-reports@xn--74h.example,https://ignored.example/", + "v=TLSRPTv1; rua=mailto:tlsreports@xn--74h.example,https://ignored.example/", }, "_smtp._tls.mailhost.xn--74h.example.": { - "v=TLSRPTv1; rua=mailto:tls-reports1@mailhost.xn--74h.example,mailto:tls-reports2@mailhost.xn--74h.example; rua=mailto:tls-reports3@mailhost.xn--74h.example", + "v=TLSRPTv1; rua=mailto:tlsreports1@mailhost.xn--74h.example,mailto:tlsreports2@mailhost.xn--74h.example; rua=mailto:tlsreports3@mailhost.xn--74h.example", }, "_smtp._tls.noreport.example.": { - "v=TLSRPTv1; rua=mailto:tls-reports@noreport.example", + "v=TLSRPTv1; rua=mailto:tlsreports@noreport.example", }, "_smtp._tls.mailhost.norua.example.": { "v=TLSRPTv1;", @@ -466,34 +466,34 @@ func TestSendReports(t *testing.T) { // Multiple results, some are combined into a single report, another result // generates a separate report to multiple rua's, and the last don't send a report. test(tlsResults, map[string][]tlsrpt.Report{ - "tls-reports@xn--74h.example": {report1}, - "tls-reports1@mailhost.xn--74h.example": {report2}, - "tls-reports2@mailhost.xn--74h.example": {report2}, - "tls-reports3@mailhost.xn--74h.example": {report2}, + "tlsreports@xn--74h.example": {report1}, + "tlsreports1@mailhost.xn--74h.example": {report2}, + "tlsreports2@mailhost.xn--74h.example": {report2}, + "tlsreports3@mailhost.xn--74h.example": {report2}, }) // If MX target has same reporting addresses as recipient domain, only recipient // domain should get a report. - resolver.TXT["_smtp._tls.mailhost.xn--74h.example."] = []string{"v=TLSRPTv1; rua=mailto:tls-reports@xn--74h.example"} + resolver.TXT["_smtp._tls.mailhost.xn--74h.example."] = []string{"v=TLSRPTv1; rua=mailto:tlsreports@xn--74h.example"} test(tlsResults[:2], map[string][]tlsrpt.Report{ - "tls-reports@xn--74h.example": {report1}, + "tlsreports@xn--74h.example": {report1}, }) - resolver.TXT["_smtp._tls.sharedsender.example."] = []string{"v=TLSRPTv1; rua=mailto:tls-reports@xn--74h.example"} + resolver.TXT["_smtp._tls.sharedsender.example."] = []string{"v=TLSRPTv1; rua=mailto:tlsreports@xn--74h.example"} test(tlsResults, map[string][]tlsrpt.Report{ - "tls-reports@xn--74h.example": {report1, report3}, + "tlsreports@xn--74h.example": {report1, report3}, }) // Suppressed addresses don't get a report. - resolver.TXT["_smtp._tls.mailhost.xn--74h.example."] = []string{"v=TLSRPTv1; rua=mailto:tls-reports1@mailhost.xn--74h.example,mailto:tls-reports2@mailhost.xn--74h.example; rua=mailto:tls-reports3@mailhost.xn--74h.example"} + resolver.TXT["_smtp._tls.mailhost.xn--74h.example."] = []string{"v=TLSRPTv1; rua=mailto:tlsreports1@mailhost.xn--74h.example,mailto:tlsreports2@mailhost.xn--74h.example; rua=mailto:tlsreports3@mailhost.xn--74h.example"} db.Insert(ctxbg, - &tlsrptdb.SuppressAddress{ReportingAddress: "tls-reports@xn--74h.example", Until: time.Now().Add(-time.Minute)}, // Expired, so ignored. - &tlsrptdb.SuppressAddress{ReportingAddress: "tls-reports1@mailhost.xn--74h.example", Until: time.Now().Add(time.Minute)}, // Still valid. - &tlsrptdb.SuppressAddress{ReportingAddress: "tls-reports3@mailhost.xn--74h.example", Until: time.Now().Add(31 * 24 * time.Hour)}, // Still valid. + &tlsrptdb.SuppressAddress{ReportingAddress: "tlsreports@xn--74h.example", Until: time.Now().Add(-time.Minute)}, // Expired, so ignored. + &tlsrptdb.SuppressAddress{ReportingAddress: "tlsreports1@mailhost.xn--74h.example", Until: time.Now().Add(time.Minute)}, // Still valid. + &tlsrptdb.SuppressAddress{ReportingAddress: "tlsreports3@mailhost.xn--74h.example", Until: time.Now().Add(31 * 24 * time.Hour)}, // Still valid. ) test(tlsResults, map[string][]tlsrpt.Report{ - "tls-reports@xn--74h.example": {report1}, - "tls-reports2@mailhost.xn--74h.example": {report2}, + "tlsreports@xn--74h.example": {report1}, + "tlsreports2@mailhost.xn--74h.example": {report2}, }) // Make reports success-only, ensuring we don't get a report anymore. @@ -514,7 +514,7 @@ func TestSendReports(t *testing.T) { } } test(tlsResults, map[string][]tlsrpt.Report{ - "tls-reports@xn--74h.example": {report1}, - "tls-reports2@mailhost.xn--74h.example": {report2}, + "tlsreports@xn--74h.example": {report1}, + "tlsreports2@mailhost.xn--74h.example": {report2}, }) } diff --git a/webadmin/admin.js b/webadmin/admin.js index ce11771..31e95d5 100644 --- a/webadmin/admin.js +++ b/webadmin/admin.js @@ -2530,7 +2530,7 @@ const domain = async (d) => { domainConfig.DMARC = null; } } - }, dmarcFieldset = dom.fieldset(style({ display: 'flex', gap: '1em' }), dom.label(attr.title('Address-part before the @ that accepts DMARC reports. Must be non-internationalized. Recommended value: dmarc-reports.'), dom.div('Localpart'), dmarcLocalpart = dom.input(attr.value(domainConfig.DMARC?.Localpart || ''))), dom.label(attr.title("Alternative domain for reporting address, for incoming reports. Typically empty, causing this domain to be used. Can be used to receive reports for domains that aren't fully hosted on this server. Configure such a domain as a hosted domain without making all the DNS changes, and configure this field with a domain that is fully hosted on this server, so the localpart and the domain of this field form a reporting address. Then only update the DMARC DNS record for the hosted domain, ensuring the reporting address is specified in its \"rua\" field as shown in the DNS settings for this domain. Unicode name."), dom.div('Alternative domain (optional)'), dmarcDomain = dom.input(attr.value(domainConfig.DMARC?.Domain || ''))), dom.label(attr.title('Account to deliver to.'), dom.div('Account'), dmarcAccount = dom.select(dom.option(''), (accounts || []).map(s => dom.option(attr.value(s), s + (accountsDisabled?.includes(s) ? ' (disabled)' : ''), s === domainConfig.DMARC?.Account ? attr.selected('') : [])))), dom.label(attr.title('Mailbox to deliver to, e.g. DMARC.'), dom.div('Mailbox'), dmarcMailbox = dom.input(attr.value(domainConfig.DMARC?.Mailbox || ''))), dom.div(dom.span('\u00a0'), dom.div(dom.submitbutton('Save'))))), dom.br(), dom.h2('TLS reporting address'), dom.form(style({ marginTop: '1ex' }), async function submit(e) { + }, dmarcFieldset = dom.fieldset(style({ display: 'flex', gap: '1em' }), dom.label(attr.title('Address-part before the @ that accepts DMARC reports. Must be non-internationalized. Recommended value: dmarcreports.'), dom.div('Localpart'), dmarcLocalpart = dom.input(attr.value(domainConfig.DMARC?.Localpart || ''))), dom.label(attr.title("Alternative domain for reporting address, for incoming reports. Typically empty, causing this domain to be used. Can be used to receive reports for domains that aren't fully hosted on this server. Configure such a domain as a hosted domain without making all the DNS changes, and configure this field with a domain that is fully hosted on this server, so the localpart and the domain of this field form a reporting address. Then only update the DMARC DNS record for the hosted domain, ensuring the reporting address is specified in its \"rua\" field as shown in the DNS settings for this domain. Unicode name."), dom.div('Alternative domain (optional)'), dmarcDomain = dom.input(attr.value(domainConfig.DMARC?.Domain || ''))), dom.label(attr.title('Account to deliver to.'), dom.div('Account'), dmarcAccount = dom.select(dom.option(''), (accounts || []).map(s => dom.option(attr.value(s), s + (accountsDisabled?.includes(s) ? ' (disabled)' : ''), s === domainConfig.DMARC?.Account ? attr.selected('') : [])))), dom.label(attr.title('Mailbox to deliver to, e.g. DMARC.'), dom.div('Mailbox'), dmarcMailbox = dom.input(attr.value(domainConfig.DMARC?.Mailbox || ''))), dom.div(dom.span('\u00a0'), dom.div(dom.submitbutton('Save'))))), dom.br(), dom.h2('TLS reporting address'), dom.form(style({ marginTop: '1ex' }), async function submit(e) { e.preventDefault(); e.stopPropagation(); if (!tlsrptLocalpart.value) { diff --git a/webadmin/admin.ts b/webadmin/admin.ts index 5ace0c5..e60c222 100644 --- a/webadmin/admin.ts +++ b/webadmin/admin.ts @@ -1678,7 +1678,7 @@ const domain = async (d: string) => { dmarcFieldset=dom.fieldset( style({display: 'flex', gap: '1em'}), dom.label( - attr.title('Address-part before the @ that accepts DMARC reports. Must be non-internationalized. Recommended value: dmarc-reports.'), + attr.title('Address-part before the @ that accepts DMARC reports. Must be non-internationalized. Recommended value: dmarcreports.'), dom.div('Localpart'), dmarcLocalpart=dom.input(attr.value(domainConfig.DMARC?.Localpart || '')), ), diff --git a/webadmin/admin_test.go b/webadmin/admin_test.go index bcbeca9..97752dd 100644 --- a/webadmin/admin_test.go +++ b/webadmin/admin_test.go @@ -281,14 +281,14 @@ func TestAdmin(t *testing.T) { tneedErrorCode(t, "user:error", func() { api.DomainLocalpartConfigSave(ctxbg, "bogus.example", nil, false) }) api.DomainLocalpartConfigSave(ctxbg, "mox.example", nil, false) // Restore. - api.DomainDMARCAddressSave(ctxbg, "mox.example", "dmarc-reports", "", "mjl", "DMARC") - tneedErrorCode(t, "user:error", func() { api.DomainDMARCAddressSave(ctxbg, "bogus.example", "dmarc-reports", "", "mjl", "DMARC") }) - tneedErrorCode(t, "user:error", func() { api.DomainDMARCAddressSave(ctxbg, "mox.example", "dmarc-reports", "", "bogus", "DMARC") }) + api.DomainDMARCAddressSave(ctxbg, "mox.example", "dmarcreports", "", "mjl", "DMARC") + tneedErrorCode(t, "user:error", func() { api.DomainDMARCAddressSave(ctxbg, "bogus.example", "dmarcreports", "", "mjl", "DMARC") }) + tneedErrorCode(t, "user:error", func() { api.DomainDMARCAddressSave(ctxbg, "mox.example", "dmarcreports", "", "bogus", "DMARC") }) api.DomainDMARCAddressSave(ctxbg, "mox.example", "", "", "", "") // Restore. - api.DomainTLSRPTAddressSave(ctxbg, "mox.example", "tls-reports", "", "mjl", "TLSRPT") - tneedErrorCode(t, "user:error", func() { api.DomainTLSRPTAddressSave(ctxbg, "bogus.example", "tls-reports", "", "mjl", "TLSRPT") }) - tneedErrorCode(t, "user:error", func() { api.DomainTLSRPTAddressSave(ctxbg, "mox.example", "tls-reports", "", "bogus", "TLSRPT") }) + api.DomainTLSRPTAddressSave(ctxbg, "mox.example", "tlsreports", "", "mjl", "TLSRPT") + tneedErrorCode(t, "user:error", func() { api.DomainTLSRPTAddressSave(ctxbg, "bogus.example", "tlsreports", "", "mjl", "TLSRPT") }) + tneedErrorCode(t, "user:error", func() { api.DomainTLSRPTAddressSave(ctxbg, "mox.example", "tlsreports", "", "bogus", "TLSRPT") }) api.DomainTLSRPTAddressSave(ctxbg, "mox.example", "", "", "", "") // Restore. // todo: cannot enable mta-sts because we have no listener, which would require a tls cert for the domain.