Skip to content

Instantly share code, notes, and snippets.

@kav2k
Last active December 18, 2017 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kav2k/614263401d9ed07ebad8aa5729e06686 to your computer and use it in GitHub Desktop.
Save kav2k/614263401d9ed07ebad8aa5729e06686 to your computer and use it in GitHub Desktop.
Short intro to convincingly telling other mail servers "I'm not a spammer, honest!"

First off, I would like to apologize if any of the information presented here comes off as obvious and an insult to anyone's intelligence / knowledge. This is not the intention by any stretch — I simply have to go on no assumptions of relevant knowledge and as such try to explain, at least briefly, most concepts involved.

So, how does sending mail between servers actually work? The sending server (acting as a Mail Transfer Agent, or MTA) connects to another MTA using SMTP (Simple Mail Transfer Protocol). The negotiation starts by a EHLO command where the sender tells the receiver its identity, and then tells what "From" address it's sending for.

In case of your server, it says:

"Hello, I'm server.example.net, and I'm sending mail on behalf of admin@example.org"

Generally, the receiver doesn't have a confirmation (from this connection alone) to go along with this; all it sees from the connection is the IP of the sender (in your case, 11.22.33.44).

Here comes the first line of defence: the receiver checks that the sender IP's reverse-resolves to the presented name as its canonical name, also known as PTR DNS record. If it does not, a lot of mail servers will just drop the mail or treat it as very probable spam. So, no lying about your identity!

Good news: your mail server passes this test.

$ dig -x 11.22.33.44
44.33.22.11.in-addr.arpa. 10728 IN  PTR     server.example.net.

However, all it does is say "the sender is who he says it is". The next problem is "is the sender authorized to send mail for @example.org?" This is non-trivial: as you can say, the canonical name is not even a subdomain of the address you're sending for (.net vs .org)!

Enter: SPF, or Sender Policy Framework. It's another DNS record (this time for example.org) that explains "who is allowed to send mail on my behalf". A mail server can check that record, match it with the sender, and act accordingly (allow, reject outright or use in spam detection).

Now, this is where your server isn't set up well: there's no SPF record for example.org.

$ dig example.org TXT
(nothing in answer)

This check is very important nowadays, and therefore many reputable mail providers will treat your mail with suspicion, up to silently dropping emails without putting them to spam.

Well, the good news is, you don't even have to touch your mail server configuration to fix it. All you need to add is a DNS record.

It should be a TXT record for example.org itself (so, subdomain @), stating "v=spf1 a:server.example.net ~all". For a zone file format (since you host your own nameserver), with a 3-hour cache period, it would be:

@ 10800 IN TXT "v=spf1 a:server.example.net ~all"

v=spf1 specifies that it's an SPF record, version 1. a:server.example.net means "resolve server.example.net, and trust that IP to send mail for me". ~all means "soft-fail everything that doesn't match previous rules". Soft-failing means "treat as suspicious, but generally don't drop". If you're sure no mail should be sent by anything but what's specified, use -all instead.

OK, suppose you set all that up. Now server.example.net can prove it is server.example.net and is allowed to send mail in @example.org domain. However, not every mail exchange terminates in one hop. Each hop adds a Received: email header to explain the previous exchange, but can we trust it? A malicious relay can say "I got this email from mail.google.com (IP-for-that), sent on behalf of ceo@google.com, honest!" and you can't verify this information.

To solve that problem, enter DKIM (DomainKeys Idenified Mail). It consists of a keypair - secret, known to the mail server, and public - published in DNS of the mail domain. The server computes a signature of the email body and some headers using the private key, and adds it as a mail header. Then, whoever receives the mail (directly from the server, or otherwise - even your mail program) can query the DNS record to check that "it's been sent by a server that has the authority to sign mail for this domain, and has not been tampered with". So it solves both the trust-in-relay problem and, as a bonus, serves as integrity protection.

I will not explain how to set up DKIM signing here, since it's dependent on the server software used; there should be plenty of guides. I'll just show how a hypothetical DNS TXT record for DKIM would look like:

$ dig something._domainkey.example.org TXT
something._domainkey.example.org. 10728 IN      TXT     "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD5HDfSNpT2PevPta53JAaHl2TTQxgb93lpBm+yNeynmg/Ip0039vFc00mSctu17usx3OlSPy6x9ZpqpHyZdjTQM6DhXQziH7EVoNshdZDaUWGJx48IXi6W0tA7oiOlJn+bLKkIeuTmHhJPT52o/e6O/4P6JmaMjoN8tbG9HFD+EQIDAQAA"

This says that the DKIM RSA public key "something" (called a selector, you may have multiple per domain) is p=...; the server has the private part of the keypair and uses it for signing.

Okay; PTR, SPF and DKIM all set up. What else could you possibly want?!

Well, a couple of "nice to have" things.

First is DMARC (Domain Message Authentication Reporting & Conformance) - a record somewhat similar to SPF (no server configuration needed) that defines a policy on how to deal with mail that fails SPF/DKIM checks. It's nice to have, but entirely optional.

Second is opportunistic TLS encryption for the server. Without it, mail is sent in plain text - and can be tampered with by active attackers. DKIM signature won't always help as it can be simply stripped. Some providers, like Gmail, will mark your email insecure in case you don't enable it.

You use Postfix; all you need to do is point postfix to your Letsencrypt certificate/key and add smtp_use_tls=yes (note: not smptd, that for incoming) to main.cf config file.

Once you have it all set up, you can test your configuration with https://www.checktls.com/perl/live/TestSender.pl, which will receive an email from you, subject it to all the tests and reply back to the same address with a report. Alternatively, you can use DKIM Verifier extension for Thunderbird to check DKIM/SPF.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment