The amazon ec2 is no good for sending email. It will most probably end in spam or will be refused by MX’es. There are two quick paths to avoid this problem. The first is offered as a solution by Amazon and it is sending email through Amazon SES. The second is to setup a relay that accepts email from instances and forwards it further.
The first one had some shortcomings for me, I didn’t want to “verify” all of the “from” addresses or domains I wanted my email to originate from, neither I wanted to have login info on ec2 instances. So I’ve stopped there and moved to the second option.
The second option means ec2 instances are clients that have to be somehow authorized by the relay. This is usually done by permitting IP addresses or by asking for authentication. When theoretically it is possible to setup a lookup of IP address in the list of currently running instances that seemed a rather long path to follow. Authentication could be easier to do but I had another option. I already use an internal PKI to achieve mutual SSL/TLS authentication between various services so why not to use it. After some reading, here is what I’ve got. Oh, this is naturally for postfix and most of the information for reading is available here.
Note: below, “myrelay.fqdn” is a fully qualified domain name with a working DNS record and “myport” is the port number
On the sender side I setup the relayhost:
since all the mail will be relayed through the relayhost, it is now safe both to use a client certificate for authentication and verify the server’s certificate, naturally both signed by the same custom CA. Below is the relevan part of main.cf on the “client” side:
#the server connection
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
#don't forget to postmap it ^^^^^^^
#our CA and cert+key files
smtp_tls_CAfile = /path/to/ca.pem
smtp_tls_cert_file = /path/to/cert.pem
#loggin verbosity, may come handy
#smtp_tls_loglevel = 2
and the references tls_policy:
#server certificate has
#to be signed by a trusted
#CA and its CN should be "myrelay.fqdn"
[myrelay.fqdn]:myport secure protocols=TLSv1 match=myrelay.fqdn
Now to the other side. On the relay, I setup a separate service in master.cf to deal with the incoming mail from EC2:
myport inet n - n - - smtpd
# require encryption from clients
# ask them to provide a client certificate
# our certs
# do not trust any others CA but the one loaded above
# relay all mail from clients with trusted certificates, drop all the rest
Start your engines, open the ports and fire an email. After some time fixing the obvious and expected errors, I’ve got the email relayed with something like this in the mail log:
Verified TLS connection established to myrelay[myip]:myport: TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)
Trusted TLS connection established from ec2-xxx.eu-west-1.compute.amazonaws.com[ip]: TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)
Why one is Trusted and the other is Verified? Trusted is the trusted cert, verified is verified peername. I did not setup peername check on relay because I don’t want to issue client certificates for IPs or amazon’s PTR records.
So far so good, doesn’t seem a rocket science if you know what you are doing. So here are also two optionals as bonuses.
1. I didn’t want the amazon IP addresses to appear in the headers of the mails, some paranoid MX would look into them sooner or later. So what I did is to setup xinetd listener on “myport” that will forward the incoming connections to myport+1. Means change the port of the listener (see above master.cf, add 1) and create this smtp4ec2 file in /etc/xinetd.d/
flags = REUSE
socket_type = stream
per_source = 100
wait = no
user = root
disable = no
redirect = localhost myport+1
log_on_failure += HOST
Of course you have to add to /etc/services:
As the result, postfix’s smtpd will accept email from localhost and all your “Received from:” headers will list 127.0.0.1 as IP until it gets to the next-hop from relay. Call it anonymizer if you want, butit isn’t. The valid hostnames are still there in the headers so it can be tracked. This is just to workaround the broken IP reputation filtering.
2. What about SPF? My instances send email from a number of domains and addresses, being a lazy admin, I create a record for some master domain, lets say “spf.myrelay.fqdn”, and then just use the include:spf.myrelay.fqdn for all the senders. This allows them to manage whatever senders they need and yet I can manage my side without changing each SPF of the actual senders.