From ebe9606aa97fb3a8255782301fe51fb321324885 Mon Sep 17 00:00:00 2001 From: Aine Date: Wed, 16 Nov 2022 09:00:19 +0200 Subject: [PATCH] real multi-domain support --- README.md | 155 ++--------------------------------------- bot/command.go | 15 ++-- bot/command_admin.go | 6 +- bot/command_owner.go | 6 +- bot/email.go | 16 +++-- bot/settings_room.go | 5 ++ cmd/cmd.go | 1 + docs/dns.md | 159 +++++++++++++++++++++++++++++++++++++++++++ utils/utils.go | 44 ++++++++++-- 9 files changed, 233 insertions(+), 174 deletions(-) create mode 100644 docs/dns.md diff --git a/README.md b/README.md index fbaa9f1..65dbd48 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ so you can use it to send emails from your apps and scripts as well. - [x] Receive attachments - [x] Catch-all mailbox - [x] Map email threads to matrix threads -- [x] Multi-domain aliases +- [x] Multi-domain support #### deep dive @@ -46,7 +46,7 @@ env vars * **POSTMOOGLE_HOMESERVER** - homeserver url, eg: `https://matrix.example.com` * **POSTMOOGLE_LOGIN** - user login/localpart, eg: `moogle` * **POSTMOOGLE_PASSWORD** - user password -* **POSTMOOGLE_DOMAINS** - space separated list of SMTP domains to listen for new emails. The first domain acts as the main (actual) domain, all other as aliases +* **POSTMOOGLE_DOMAINS** - space separated list of SMTP domains to listen for new emails. The first domain acts as the default domain, all other as aliases
other optional config parameters @@ -72,155 +72,7 @@ You can find default values in [config/defaults.go](config/defaults.go) ### 2. DNS (optional) -The following configuration is needed only if you want to send outgoing emails via Postmoogle (it's not necessary if you only want to receive emails). - -
-TL;DR - -1. Configure DMARC record -2. Configure SPF record -3. Configure MX record -4. Configure DKIM record (use `!pm dkim`) - -
- -**First**, add a new DMARC DNS record of the `TXT` type for subdomain `_dmarc` with a proper policy. The simplest policy you can use is: `v=DMARC1; p=quarantine;`. - -
-Example - -```bash -$ dig txt _dmarc.example.com - -; <<>> DiG 9.18.6 <<>> txt _dmarc.example.com -;; global options: +cmd -;; Got answer: -;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57306 -;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 - -;; OPT PSEUDOSECTION: -; EDNS: version: 0, flags:; udp: 1232 -;; QUESTION SECTION: -;_dmarc.example.com. IN TXT - -;; ANSWER SECTION: -_dmarc.example.com. 1799 IN TXT "v=DMARC1; p=quarantine;" - -;; Query time: 46 msec -;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP) -;; WHEN: Sun Sep 04 21:31:30 EEST 2022 -;; MSG SIZE rcvd: 79 -``` - -
- -**Second**, add a new SPF DNS record of the `TXT` type for your domain that will be used with Postmoogle, with format: `v=spf1 ip4:SERVER_IP -all` (replace `SERVER_IP` with your server's IP address) - -
-Example - -```bash -$ dig txt example.com - -; <<>> DiG 9.18.6 <<>> txt example.com -;; global options: +cmd -;; Got answer: -;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24796 -;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1 - -;; OPT PSEUDOSECTION: -; EDNS: version: 0, flags:; udp: 1232 -;; QUESTION SECTION: -;example.com. IN TXT - -;; ANSWER SECTION: -example.com. 1799 IN TXT "v=spf1 ip4:111.111.111.111 -all" - -;; Query time: 36 msec -;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP) -;; WHEN: Sun Sep 04 21:35:04 EEST 2022 -;; MSG SIZE rcvd: 255 -``` - -
- -**Third**, add a new MX DNS record of the `MX` type for your domain that will be used with postmoogle. It should point to the same (sub-)domain. -Looks odd, but some mail servers will refuse to interact with your mail server (and Postmoogle is already a mail server) without MX records. - -
-Example - -```bash -dig MX example.com - -; <<>> DiG 9.18.6 <<>> MX example.com -;; global options: +cmd -;; Got answer: -;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12688 -;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 - -;; OPT PSEUDOSECTION: -; EDNS: version: 0, flags:; udp: 1232 -;; QUESTION SECTION: -;example.com. IN MX - -;; ANSWER SECTION: -example.com. 1799 IN MX 10 example.com. - -;; Query time: 40 msec -;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP) -;; WHEN: Tue Sep 06 16:44:47 EEST 2022 -;; MSG SIZE rcvd: 59 -``` - -
- -**Fourth** (and the last one), add new DKIM DNS record of `TXT` type for subdomain `postmoogle._domainkey` that will be used with postmoogle. - -You can get that signature using the `!pm dkim` command: - -
-!pm dkim - -DKIM signature is: `v=DKIM1; k=rsa; p=MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxJVqmBHhK9FY93q1o3WEaP2GKMh/3LNMyvi1uSjOKxIyfWv685KxX1EUrbHakQRCTtUM7efKEsXsXBh+DQru2TE32yFpL9afA5BbHj3KePGFY8KJ2m0sQxbQcvn2KjJC0IQ15mk0rninPhtphU/2zLsd6e7Rl1m3L+9Osk320GbfDgSKjRPcSiwVMbLJpSOP0H0F3cIu+c1fHZHfmWy0O+us42C3HTLTlD779LTnQnKlAOQD/+DYYqz6TGGxEwUG2BRQ8O8w7/wXEkg/6a/MxNtPnc59g29CpqRsDkuYiR3UIpqzLDoqHlaoKNbYy34R+4aIjfNpmZyR5kIumws+3MJtJt9UhBTMloqd8lZDPaPmX2NEDqbcSTkHMQrphk+EWSCc7OvbKRaXZ0SyJLpLjxRwKrpeO0JAI0ZpnAFS11uBEe9GSS8uzIIFNYVD1vHloAFKvUJEhyuVyz9/SyqTnArN3ZTiC5cqD1MB86q5QPrKqZfp1dAnv7xAJThL0AP/AgMBAAE=`. -You need to add it to your DNS records (if not already): -Add new DNS record with type = `TXT`, key (subdomain/from): `postmoogle._domainkey` and value (to): - -``` -v=DKIM1; k=rsa; p=MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxJVqmBHhK9FY93q1o3WEaP2GKMh/3LNMyvi1uSjOKxIyfWv685KxX1EUrbHakQRCTtUM7efKEsXsXBh+DQru2TE32yFpL9afA5BbHj3KePGFY8KJ2m0sQxbQcvn2KjJC0IQ15mk0rninPhtphU/2zLsd6e7Rl1m3L+9Osk320GbfDgSKjRPcSiwVMbLJpSOP0H0F3cIu+c1fHZHfmWy0O+us42C3HTLTlD779LTnQnKlAOQD/+DYYqz6TGGxEwUG2BRQ8O8w7/wXEkg/6a/MxNtPnc59g29CpqRsDkuYiR3UIpqzLDoqHlaoKNbYy34R+4aIjfNpmZyR5kIumws+3MJtJt9UhBTMloqd8lZDPaPmX2NEDqbcSTkHMQrphk+EWSCc7OvbKRaXZ0SyJLpLjxRwKrpeO0JAI0ZpnAFS11uBEe9GSS8uzIIFNYVD1vHloAFKvUJEhyuVyz9/SyqTnArN3ZTiC5cqD1MB86q5QPrKqZfp1dAnv7xAJThL0AP/AgMBAAE= -``` - -Without that record other email servers may reject your emails as spam, kupo. - -
- -
-Example - -```bash -$ dig TXT postmoogle._domainkey.example.com - -; <<>> DiG 9.18.6 <<>> TXT postmoogle._domainkey.example.com -;; global options: +cmd -;; Got answer: -;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59014 -;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 - -;; OPT PSEUDOSECTION: -; EDNS: version: 0, flags:; udp: 1232 -;; QUESTION SECTION: -;postmoogle._domainkey.example.com. IN TXT - -;; ANSWER SECTION: -postmoogle._domainkey.example.com. 600 IN TXT "v=DKIM1; k=rsa; p=MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxJVqmBHhK9FY93q1o3WEaP2GKMh/3LNMyvi1uSjOKxIyfWv685KxX1EUrbHakQRCTtUM7efKEsXsXBh+DQru2TE32yFpL9afA5BbHj3KePGFY8KJ2m0sQxbQcvn2KjJC0IQ15mk0rninPhtphU/2zLsd6e7Rl1m3L+9Osk320GbfDgSKjRPcSiwVMbLJpSOP0H0F3cIu+c1fHZHfmWy0O+us42C3HTLTlD779LTnQnKlAOQD/+DYYqz6TGGxEwUG2BRQ8O8w7/wXEkg/6a/MxNtPnc59g29CpqRsDkuYiR3UIpqzLDoqHlaoKNbYy34R+4aIjfNpmZyR5kIumws+3MJtJt9UhBTMloqd8lZDPaPmX2NEDqbcSTkHMQrphk+EWSCc7OvbKRaXZ0SyJLpLjxRwKrpeO0JAI0ZpnAFS11uBEe9GSS8uzIIFNYVD1vHloAFKvUJEhyuVyz9/SyqTnArN3ZTiC5cqD1MB86q5QPrKqZfp1dAnv7xAJThL0AP/AgMBAAE=" - -;; Query time: 90 msec -;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP) -;; WHEN: Mon Sep 05 16:16:21 EEST 2022 -;; MSG SIZE rcvd: 525 -``` - -
+Follow the [docs/dns](docs/dns.md) ## Usage @@ -241,6 +93,7 @@ If you want to change them - check available options in the help message (`!pm h --- * **!pm mailbox** - Get or set mailbox of the room +* **!pm domain** - Get or set default domain of the room * **!pm owner** - Get or set owner of the room * **!pm password** - Get or set SMTP password of the room's mailbox diff --git a/bot/command.go b/bot/command.go index 40a6035..23452ac 100644 --- a/bot/command.go +++ b/bot/command.go @@ -70,6 +70,12 @@ func (b *Bot) initCommands() commandList { sanitizer: utils.Mailbox, allowed: b.allowOwner, }, + { + key: roomOptionDomain, + description: "Get or set default domain of the room", + sanitizer: utils.SanitizeDomain, + allowed: b.allowOwner, + }, { key: roomOptionOwner, description: "Get or set owner of the room", @@ -276,7 +282,7 @@ func (b *Bot) sendIntroduction(ctx context.Context, roomID id.RoomID) { msg.WriteString(" SOME_INBOX` command.\n") msg.WriteString("You will then be able to send emails to ") - msg.WriteString(utils.EmailsList("SOME_INBOX", b.domains)) + msg.WriteString(utils.EmailsList("SOME_INBOX", "")) msg.WriteString("` and have them appear in this room.") b.SendNotice(ctx, roomID, msg.String()) @@ -315,7 +321,7 @@ func (b *Bot) sendHelp(ctx context.Context) { msg.WriteString(value) if cmd.key == roomOptionMailbox { msg.WriteString(" (") - msg.WriteString(utils.EmailsList(value, b.domains)) + msg.WriteString(utils.EmailsList(value, cfg.Domain())) msg.WriteString(")") } msg.WriteString("`)") @@ -376,8 +382,9 @@ func (b *Bot) runSend(ctx context.Context) { b.lock(evt.RoomID.String()) defer b.unlock(evt.RoomID.String()) - from := mailbox + "@" + b.domains[0] - ID := utils.MessageID(evt.ID, b.domains[0]) + domain := utils.SanitizeDomain(cfg.Domain()) + from := mailbox + "@" + domain + ID := utils.MessageID(evt.ID, domain) for _, to := range tos { email := utils.NewEmail(ID, "", " "+ID, subject, from, to, body, "", nil) data := email.Compose(b.getBotSettings().DKIMPrivateKey()) diff --git a/bot/command_admin.go b/bot/command_admin.go index 3432640..46fd293 100644 --- a/bot/command_admin.go +++ b/bot/command_admin.go @@ -53,7 +53,7 @@ func (b *Bot) sendMailboxes(ctx context.Context) { for _, mailbox := range slice { cfg := mailboxes[mailbox] msg.WriteString("* `") - msg.WriteString(utils.EmailsList(mailbox, b.domains)) + msg.WriteString(utils.EmailsList(mailbox, cfg.Domain())) msg.WriteString("` by ") msg.WriteString(cfg.Owner()) msg.WriteString("\n") @@ -174,7 +174,7 @@ func (b *Bot) runCatchAll(ctx context.Context, commandSlice []string) { if cfg.CatchAll() != "" { msg.WriteString(cfg.CatchAll()) msg.WriteString(" (") - msg.WriteString(utils.EmailsList(cfg.CatchAll(), b.domains)) + msg.WriteString(utils.EmailsList(cfg.CatchAll(), "")) msg.WriteString(")") } else { msg.WriteString("not set") @@ -203,5 +203,5 @@ func (b *Bot) runCatchAll(ctx context.Context, commandSlice []string) { return } - b.SendNotice(ctx, evt.RoomID, fmt.Sprintf("Catch-all is set to: `%s` (%s).", mailbox, utils.EmailsList(mailbox, b.domains))) + b.SendNotice(ctx, evt.RoomID, fmt.Sprintf("Catch-all is set to: `%s` (%s).", mailbox, utils.EmailsList(mailbox, ""))) } diff --git a/bot/command_owner.go b/bot/command_owner.go index ff8c7e7..c454514 100644 --- a/bot/command_owner.go +++ b/bot/command_owner.go @@ -60,7 +60,7 @@ func (b *Bot) getOption(ctx context.Context, name string) { } if name == roomOptionMailbox { - value = utils.EmailsList(value, b.domains) + value = utils.EmailsList(value, cfg.Domain()) } msg := fmt.Sprintf("`%s` of this room is `%s`\n"+ @@ -87,7 +87,7 @@ func (b *Bot) setOption(ctx context.Context, name, value string) { if name == roomOptionMailbox { existingID, ok := b.getMapping(value) if ok && existingID != "" && existingID != evt.RoomID { - b.SendNotice(ctx, evt.RoomID, fmt.Sprintf("Mailbox `%s` (%s) already taken, kupo", value, utils.EmailsList(value, b.domains))) + b.SendNotice(ctx, evt.RoomID, fmt.Sprintf("Mailbox `%s` (%s) already taken, kupo", value, utils.EmailsList(value, ""))) return } } @@ -116,7 +116,7 @@ func (b *Bot) setOption(ctx context.Context, name, value string) { b.rooms.Delete(old) } b.rooms.Store(value, evt.RoomID) - value = fmt.Sprintf("%s@%s", value, b.domains[0]) + value = fmt.Sprintf("%s@%s", value, utils.SanitizeDomain(cfg.Domain())) } err = b.setRoomSettings(evt.RoomID, cfg) diff --git a/bot/email.go b/bot/email.go index 84a8ca7..d1ddf82 100644 --- a/bot/email.go +++ b/bot/email.go @@ -147,12 +147,13 @@ func (b *Bot) SendEmailReply(ctx context.Context) { b.Error(ctx, evt.RoomID, "mailbox is not configured, kupo") return } + domain := utils.SanitizeDomain(cfg.Domain()) b.lock(evt.RoomID.String()) defer b.unlock(evt.RoomID.String()) - fromMailbox := mailbox + "@" + b.domains[0] - meta := b.getParentEmail(evt) + fromMailbox := mailbox + "@" + domain + meta := b.getParentEmail(evt, domain) // when email was sent from matrix and reply was sent from matrix again if fromMailbox != meta.From { meta.To = meta.From @@ -173,7 +174,7 @@ func (b *Bot) SendEmailReply(ctx context.Context) { } body := content.Body - meta.MessageID = utils.MessageID(evt.ID, b.domains[0]) + meta.MessageID = utils.MessageID(evt.ID, domain) meta.References = meta.References + " " + meta.MessageID b.log.Debug("send email reply: %+v", meta) email := utils.NewEmail(meta.MessageID, meta.InReplyTo, meta.References, meta.Subject, meta.From, meta.To, body, "", nil) @@ -240,7 +241,7 @@ func (b *Bot) getParentEvent(evt *event.Event) (id.EventID, *event.Event) { return threadID, decrypted } -func (b *Bot) getParentEmail(evt *event.Event) parentEmail { +func (b *Bot) getParentEmail(evt *event.Event, domain string) parentEmail { var parent parentEmail threadID, parentEvt := b.getParentEvent(evt) parent.ThreadID = threadID @@ -251,7 +252,7 @@ func (b *Bot) getParentEmail(evt *event.Event) parentEmail { return parent } - parent.MessageID = utils.MessageID(parentEvt.ID, b.domains[0]) + parent.MessageID = utils.MessageID(parentEvt.ID, domain) parent.From = utils.EventField[string](&parentEvt.Content, eventFromKey) parent.To = utils.EventField[string](&parentEvt.Content, eventToKey) parent.InReplyTo = utils.EventField[string](&parentEvt.Content, eventMessageIDkey) @@ -298,8 +299,9 @@ func (b *Bot) saveSentMetadata(ctx context.Context, queued bool, threadID id.Eve b.Error(ctx, evt.RoomID, "cannot send notice: %v", err) return } - b.setThreadID(evt.RoomID, utils.MessageID(evt.ID, b.domains[0]), threadID) - b.setThreadID(evt.RoomID, utils.MessageID(msgID, b.domains[0]), threadID) + domain := utils.SanitizeDomain(cfg.Domain()) + b.setThreadID(evt.RoomID, utils.MessageID(evt.ID, domain), threadID) + b.setThreadID(evt.RoomID, utils.MessageID(msgID, domain), threadID) b.setLastEventID(evt.RoomID, threadID, msgID) } diff --git a/bot/settings_room.go b/bot/settings_room.go index 50efc6f..f46c97a 100644 --- a/bot/settings_room.go +++ b/bot/settings_room.go @@ -15,6 +15,7 @@ const acRoomSettingsKey = "cc.etke.postmoogle.settings" const ( roomOptionOwner = "owner" roomOptionMailbox = "mailbox" + roomOptionDomain = "domain" roomOptionNoSend = "nosend" roomOptionNoSender = "nosender" roomOptionNoRecipient = "norecipient" @@ -44,6 +45,10 @@ func (s roomSettings) Mailbox() string { return s.Get(roomOptionMailbox) } +func (s roomSettings) Domain() string { + return s.Get(roomOptionDomain) +} + func (s roomSettings) Owner() string { return s.Get(roomOptionOwner) } diff --git a/cmd/cmd.go b/cmd/cmd.go index 83f3fd1..f3d2932 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -34,6 +34,7 @@ func main() { cfg := config.New() log = logger.New("postmoogle.", cfg.LogLevel) utils.SetLogger(log) + utils.SetDomains(cfg.Domains) log.Info("#############################") log.Info("Postmoogle") diff --git a/docs/dns.md b/docs/dns.md new file mode 100644 index 0000000..1cac2f2 --- /dev/null +++ b/docs/dns.md @@ -0,0 +1,159 @@ +# DNS configuration + +the following configuration is required only if you want to send emails from Postmoogle + +# MX + +Add a new MX DNS record of the `MX` type for your domain that will be used with postmoogle. +It should point to the same (sub-)domain. +Looks odd, but some mail servers will refuse to interact with your mail server +(and Postmoogle is already a mail server) without MX records. + +
+Example + +```bash +dig MX example.com + +; <<>> DiG 9.18.6 <<>> MX example.com +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12688 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 1232 +;; QUESTION SECTION: +;example.com. IN MX + +;; ANSWER SECTION: +example.com. 1799 IN MX 10 example.com. + +;; Query time: 40 msec +;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP) +;; WHEN: Tue Sep 06 16:44:47 EEST 2022 +;; MSG SIZE rcvd: 59 +``` + +
+ +# SPF + +Aadd a new SPF DNS record of the `TXT` type for your domain that will be used with Postmoogle, +with format: `v=spf1 ip4:SERVER_IP4 -all` (replace `SERVER_IP4` with your server's IP address), +for servers with IPv6: `v=spf1 ip6:SERVER_IP6 -all` (you may use both `ip4` and `ip6` in one TXT record). + +
+Example + +```bash +$ dig txt example.com + +; <<>> DiG 9.18.6 <<>> txt example.com +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24796 +;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1 + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 1232 +;; QUESTION SECTION: +;example.com. IN TXT + +;; ANSWER SECTION: +example.com. 1799 IN TXT "v=spf1 ip4:111.111.111.111 -all" + +;; Query time: 36 msec +;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP) +;; WHEN: Sun Sep 04 21:35:04 EEST 2022 +;; MSG SIZE rcvd: 255 +``` + +
+ +# DMARC + +Add a new DMARC DNS record of the `TXT` type for subdomain `_dmarc` with a proper policy. +The simplest policy you can use is: `v=DMARC1; p=quarantine;`. + +
+Example + +```bash +$ dig txt _dmarc.example.com + +; <<>> DiG 9.18.6 <<>> txt _dmarc.example.com +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57306 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 1232 +;; QUESTION SECTION: +;_dmarc.example.com. IN TXT + +;; ANSWER SECTION: +_dmarc.example.com. 1799 IN TXT "v=DMARC1; p=quarantine;" + +;; Query time: 46 msec +;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP) +;; WHEN: Sun Sep 04 21:31:30 EEST 2022 +;; MSG SIZE rcvd: 79 +``` + +
+ +# DKIM + +Add new DKIM DNS record of `TXT` type for subdomain `postmoogle._domainkey` that will be used with postmoogle. +You can get that signature using the `!pm dkim` command: + +
+!pm dkim + +DKIM signature is: `v=DKIM1; k=rsa; p=MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxJVqmBHhK9FY93q1o3WEaP2GKMh/3LNMyvi1uSjOKxIyfWv685KxX1EUrbHakQRCTtUM7efKEsXsXBh+DQru2TE32yFpL9afA5BbHj3KePGFY8KJ2m0sQxbQcvn2KjJC0IQ15mk0rninPhtphU/2zLsd6e7Rl1m3L+9Osk320GbfDgSKjRPcSiwVMbLJpSOP0H0F3cIu+c1fHZHfmWy0O+us42C3HTLTlD779LTnQnKlAOQD/+DYYqz6TGGxEwUG2BRQ8O8w7/wXEkg/6a/MxNtPnc59g29CpqRsDkuYiR3UIpqzLDoqHlaoKNbYy34R+4aIjfNpmZyR5kIumws+3MJtJt9UhBTMloqd8lZDPaPmX2NEDqbcSTkHMQrphk+EWSCc7OvbKRaXZ0SyJLpLjxRwKrpeO0JAI0ZpnAFS11uBEe9GSS8uzIIFNYVD1vHloAFKvUJEhyuVyz9/SyqTnArN3ZTiC5cqD1MB86q5QPrKqZfp1dAnv7xAJThL0AP/AgMBAAE=`. +You need to add it to your DNS records (if not already): +Add new DNS record with type = `TXT`, key (subdomain/from): `postmoogle._domainkey` and value (to): + +``` +v=DKIM1; k=rsa; p=MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxJVqmBHhK9FY93q1o3WEaP2GKMh/3LNMyvi1uSjOKxIyfWv685KxX1EUrbHakQRCTtUM7efKEsXsXBh+DQru2TE32yFpL9afA5BbHj3KePGFY8KJ2m0sQxbQcvn2KjJC0IQ15mk0rninPhtphU/2zLsd6e7Rl1m3L+9Osk320GbfDgSKjRPcSiwVMbLJpSOP0H0F3cIu+c1fHZHfmWy0O+us42C3HTLTlD779LTnQnKlAOQD/+DYYqz6TGGxEwUG2BRQ8O8w7/wXEkg/6a/MxNtPnc59g29CpqRsDkuYiR3UIpqzLDoqHlaoKNbYy34R+4aIjfNpmZyR5kIumws+3MJtJt9UhBTMloqd8lZDPaPmX2NEDqbcSTkHMQrphk+EWSCc7OvbKRaXZ0SyJLpLjxRwKrpeO0JAI0ZpnAFS11uBEe9GSS8uzIIFNYVD1vHloAFKvUJEhyuVyz9/SyqTnArN3ZTiC5cqD1MB86q5QPrKqZfp1dAnv7xAJThL0AP/AgMBAAE= +``` + +Without that record other email servers may reject your emails as spam, kupo. + +
+ +
+Example + +```bash +$ dig TXT postmoogle._domainkey.example.com + +; <<>> DiG 9.18.6 <<>> TXT postmoogle._domainkey.example.com +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59014 +;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 1232 +;; QUESTION SECTION: +;postmoogle._domainkey.example.com. IN TXT + +;; ANSWER SECTION: +postmoogle._domainkey.example.com. 600 IN TXT "v=DKIM1; k=rsa; p=MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxJVqmBHhK9FY93q1o3WEaP2GKMh/3LNMyvi1uSjOKxIyfWv685KxX1EUrbHakQRCTtUM7efKEsXsXBh+DQru2TE32yFpL9afA5BbHj3KePGFY8KJ2m0sQxbQcvn2KjJC0IQ15mk0rninPhtphU/2zLsd6e7Rl1m3L+9Osk320GbfDgSKjRPcSiwVMbLJpSOP0H0F3cIu+c1fHZHfmWy0O+us42C3HTLTlD779LTnQnKlAOQD/+DYYqz6TGGxEwUG2BRQ8O8w7/wXEkg/6a/MxNtPnc59g29CpqRsDkuYiR3UIpqzLDoqHlaoKNbYy34R+4aIjfNpmZyR5kIumws+3MJtJt9UhBTMloqd8lZDPaPmX2NEDqbcSTkHMQrphk+EWSCc7OvbKRaXZ0SyJLpLjxRwKrpeO0JAI0ZpnAFS11uBEe9GSS8uzIIFNYVD1vHloAFKvUJEhyuVyz9/SyqTnArN3ZTiC5cqD1MB86q5QPrKqZfp1dAnv7xAJThL0AP/AgMBAAE=" + +;; Query time: 90 msec +;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP) +;; WHEN: Mon Sep 05 16:16:21 EEST 2022 +;; MSG SIZE rcvd: 525 +``` + +
+ +# rDNS + +> additional PTR record will help you to get better spam score + +Configure Reverse DNS of your server. Unfortunately, rDNS is provider-specific, so you have to find out how to configure it with your hosting provider. Search for something like: `PROVIDER configure "rdns"` (where `PROVIDER` is your hosting provider name) diff --git a/utils/utils.go b/utils/utils.go index 19cf31c..e937cf0 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -7,13 +7,21 @@ import ( "gitlab.com/etke.cc/go/logger" ) -var log *logger.Logger +var ( + log *logger.Logger + domains []string +) // SetLogger for utils func SetLogger(loggerInstance *logger.Logger) { log = loggerInstance } +// SetDomains for later use +func SetDomains(slice []string) { + domains = slice +} + // Mailbox returns mailbox part from email address func Mailbox(email string) string { index := strings.LastIndex(email, "@") @@ -24,16 +32,24 @@ func Mailbox(email string) string { } // EmailsList returns human-readable list of mailbox's emails for all available domains -func EmailsList(mailbox string, domains []string) string { +func EmailsList(mailbox string, domain string) string { var msg strings.Builder + domain = SanitizeDomain(domain) + msg.WriteString(mailbox) + msg.WriteString("@") + msg.WriteString(domain) + count := len(domains) - 1 - for i, domain := range domains { - msg.WriteString(mailbox) - msg.WriteString("@") - msg.WriteString(domain) + for i, aliasDomain := range domains { if i < count { msg.WriteString(", ") } + if aliasDomain == domain { + continue + } + msg.WriteString(mailbox) + msg.WriteString("@") + msg.WriteString(aliasDomain) } return msg.String() @@ -44,6 +60,22 @@ func Hostname(email string) string { return email[strings.LastIndex(email, "@")+1:] } +// SanitizeDomain checks that input domain is available for use +func SanitizeDomain(domain string) string { + domain = strings.TrimSpace(domain) + if domain == "" { + return domains[0] + } + + for _, allowed := range domains { + if domain == allowed { + return domain + } + } + + return domains[0] +} + // Bool converts string to boolean func Bool(str string) bool { str = strings.ToLower(str)