SMTP Relay

Jump to: navigation, search


When outbound network connections to ports 25 (smtp), 587 (submission), 465 (smtps) are blocked (as with AT&T U-verse), sending email from a system can be troublesome; however it's likely that other outbound ports are open which can be used instead. This article will cover setting up an inbound SSL authenticated-only smtpd instance on port 443, which then relays email upstream to a standard mail provider also using SSL with the outbound connection requiring authentication.

any client software -> this.server:443 w/SSL and auth -> upstream normal SMTP w/SSL and auth


Note: any modern Linux distribution should work, however package installation names and config file locations may vary slightly. CentOS 7 ships with Postfix 2.10.x, Cyrus SASL 2.1.x and OpenSSL 1.0.2k.

Conventions Used

  • - the name of the DNS listed server being implemented here
  • supercoolusername/supercoolpassword - the login created for herein
  • - we will use Mailgun as the upstream hop, any upstream standard SMTP will work
  • - the login credentials to the upstream Mailgun server
  • port 443 - port 443/tcp is normally used for HTTPS and tends to be open in all firewalls, etc.

Note: any port can be used depending on needs, 443 was chosen as it's already open in most firewalls and other network blocking situations. This would mean you cannot share this SMTP relay server with a true HTTPS website, however. Using port 60 is a good alternate choice if port 443 is not possible.

SSL Certificates

Both inbound to the relay server and outbound to the upstream host connections will be implemented - obtain an SSL certificate with the name of your preferred domain. This can be self-signed, a free one from CAcert, StartCom SSL, etc. - it's the same type of SSL key/certificate used in Apache webservers.

You will typically need the Intermediate CA certificates for your SSL cert as appropriate and the Intermediate for the upstream SMTP server. Intermediate CA certificates are generally used to prevent man-in-the-middle attacks on SSL connections and provide a higher security posture.

Software Installation

Install the required software:

yum -y install postfix cyrus-sasl cyrus-sasl-lib cyrus-sasl-plain cyrus-sasl-md5 openssl mailx iptables-services

Outbound Configuration

Configuring the outbound SMTP from the server first is recommended; it's easier to test locally from the server before setting up the inbound SMTP daemon. By configuring and testing this portion first we confirm any future failures should not be related to dispatching outbound email to the upstream connection.

First, obtain the upstream Intermediate CA certificate(s) and save them concatenated to a single PEM in /etc/postfix/. The Mailgun services herein use Geotrust Global CA so we'll grab that:

cd /etc/postfix/

Next, create a plain text file /etc/postfix/sasl_passwdthat contains the upstream authentication username/password info. Secure it from prying eyes and then use postmap to create a Postfix-friendly database file out of it.

For Mailgun this information is listed separately for each one of your configured domains, not your primary account login; the username is typically in form.

echo '' >> /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
chmod 0640 /etc/postfix/sasl_passwd*
chown root:postfix /etc/postfix/sasl_passwd*

Now add the configuration to Postfix for the outbound connection - these are the smtp_* settings (think "postfix as a client"):

myhostname =
mynetworks_style = host
relayhost = []:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = may
smtp_tls_note_starttls_offer = yes
smtp_tls_CAfile = /etc/postfix/GeoTrust_Global_CA.pem

Lastly ensure Postfix is enabled at boot and start it:

systemctl enable --now postfix.service

If this is the first time configuring Postfix on a new installation, be sure and set the other needed settings such as inet_interfaces, mynetworks_style and similar configuration options. By default the config is locked to localhost and cannot receive mail from the outside world.

Basic Outbound Testing

At this point you should be able to send an email using mail which will connect to the local Postfix instance on port 25 and relay your outbound email:

mail -s "Test subject"

Examining /var/log/maillog should show the email being accepted by the upstream host for delivery like so:

Oct  4 18:00:53 myserver postfix/smtp[949]: 8C3F4421E2: to=<>,[]:587, delay=0.95, delays=0.35/0.11/0.28/0.21, dsn=2.0.0, status=sent (250 Great success)

Before proceeding further, ensure that this basic functionality is working.

Inbound Configuration

This part is a little more involved, as precautions need to be taken to ensure that SSL is mandatory and a username/password is required for anything. Only persons with these credentials can send email, even to local server users.

First, save the SSL key and certificate for this server together in a single PEM file; additionally you'll need the Intermediate CA from where the certificate was obtained. StartCom SSL is used in this example.

The key must be decrypted (without a password) - you may have to manually decrypt the key first, like so: openssl rsa -in encrypted.key -out decrypted.key

cd /etc/postfix/
wget -O StartCom_CA.pem
cat /path/to/server.key >> /etc/postfix/mydomain-org.pem
cat /path/to/server.crt >> /etc/postfix/mydomain-org.pem
chmod 0640 /etc/postfix/mydomain-org.pem
chown root:postfix /etc/postfix/mydomain-org.pem

Next, create a SASL database file with the user/pass combo of your choosing to use for logging in -- the -c flag is to create a new databases, so if you're adding a second, third, etc. user drop that from the command. The -u foo option is the realm that is used for authentication domain later, it can be anything but we'll use our DNS name to make it easy.

saslpasswd2 -c -f /etc/postfix/sasldb2 -u -a smtpauth supercoolusername
chmod 0640 /etc/postfix/sasldb2
chown root:postfix /etc/postfix/sasldb2

Update the Postfix SASL config file to use this database file instead of the default which is PAM -- we're not going to create system users at all. This also means there is no need to run the saslauthd daemon, the SASL library dynamically linked into Postfix (/usr/libexec/postfix/smtpd -> /usr/lib64/ will read this config and use the DB directly as needed.

pwcheck_method: auxprop
auxprop_plugin: sasldb
sasldb_path: /etc/postfix/sasldb2
mech_list: plain login
log_level: 1

Add the configuration of the smtpd_* settings in Postfix to accept the incoming SSL connections, again specifying parameters to lock it down for security. Note that the smtpd_sasl_local_domain setting matched the realm named used above in the saslpasswd2 command.

smtpd_tls_cert_file = /etc/postfix/mydomain-org.pem
smtpd_tls_key_file = /etc/postfix/mydomain-org.pem
smtpd_tls_CAfile = /etc/postfix/StartCom_CA.pem
smtpd_tls_loglevel = 0
smtpd_tls_mandatory_ciphers = medium
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_sasl_type = cyrus
smtpd_sasl_local_domain =
smtpd_sasl_path = smtpd
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes

The last major step is to define the listening daemon on a port; the /etc/postfix/ file usually contains a commented out example for smtps (port 465), this definition should be inserted right at that spot. Because we want the standard localhost:25 connection to still work for basic server things, we're explicitly passing some of the "force SSL, force login" settings here as commandline options only on this port. The 443 is the port to use, change as you see fit.

443       inet  n       -       n       -       -       smtpd
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_security_level=encrypt
  -o smtpd_tls_auth_only=yes
  -o smtpd_enforce_tls=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject_unauth_destination
  -o milter_macro_daemon_name=ORIGINATING

Finally, restart Postfix:

systemctl restart postfix

Local Relay Testing

Before configuring iptables to allow traffic in, testing should be performed. First you need to generate a base64 encoded version of the username and password to be used as a SMTP client, this is required for manual testing and is part of the PLAIN style SMTP specification.

echo -ne '\000supercoolusername\000supercoolpassword' | openssl base64

This should give a string of alphanumeric characters and maybe a few symbols that is used in the AUTH PLAIN command typed below. then try testing the process locally using the openssl tool. Things you type are denoted with ### TYPE THIS ### inline - for more help on how to manually "talk SMTP" hit up Google.

# openssl s_client -quiet -starttls smtp -connect localhost:443

250 DSN
EHLO                      ### TYPE THIS ###
250-SIZE 10240000
250 DSN
235 2.7.0 Authentication successful
MAIL FROM: someone@server.local       ### TYPE THIS ###
250 2.1.0 Ok
RCPT TO:            ### TYPE THIS ###
250 2.1.5 Ok
QUIT                                  ### TYPE THIS ###
221 2.0.0 Bye

The above is actually testing multiple things all at once; TLS (SSL) is working, authentication is working and relaying as an authenticated user is working.

Next, test that not using SSL is blocked:

# telnet localhost 443

Trying ::1...
Connected to localhost.
Escape character is '^]'.
220 ESMTP Postfix
250-SIZE 10240000
250 DSN
530 5.7.0 Must issue a STARTTLS command first
221 2.0.0 Bye

Finally, test that you cannot try sending mail without authentication - the RCPT TO: should be tested against local server accounts (i.e. root@localhost) and relayed accounts (i.e. In all cases the 554 error should happen:

# openssl s_client -quiet -starttls smtp -connect localhost:443

250 DSN
250-SIZE 10240000
250 DSN
MAIL FROM: someone@server.local
250 2.1.0 Ok
554 5.7.1 <localhost[::1]>: Client host rejected: Access denied
221 2.0.0 Bye

All of these tests ensure that it's working when SSL and authentication is used, but denied in all other cases. We specifically did not enable the local server to send email on this port without SSL and authentication, however localhost:25 should still work for that.

Allowing Remote Access

As a last step, open the iptables firewall to allow port 443 usage.

Do not open port 25 unless your server actually receives normal email from the Internet

If you already use iptables, the rule might be added like so:

-A INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT

If this is a brand new setup, or iptables wasn't used here's a more full-fledged example that allows 22 (SSH), 80 (HTTP) and 443 which is pretty average for a server (note: I recommend fail2ban if opening SSH):

-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -d fe80::/64 -p udp -m udp --dport 546 -m state --state NEW -j ACCEPT
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited

Once this is open, perform the same testing as the Local section above, except use the DNS name of the server to connect.

For completeness sake because we love security and stopping the bad guys, here's a simple fail2ban configuration that will monitor the SSH port for brute force attacks and null route the evildoers:

ignoreip =
bantime = 600
findtime  = 600
maxretry = 3

enabled = true
maxretry = 5
usedns = no

Configuring mailx

One of the most common ways to send email from cron jobs, scripts and other assorted commands is using the mail (mailx) command. To configure the client systems to use this relay, a ~/.mailrc configuration needs to be set up like so:

account myrelay {
  set from=myname@mylaptop.local
  set smtp-hostname=mylaptop.local
  set smtp=smtp://
  set smtp-use-starttls
  set smtp-auth=plain
  set ssl-verify=warn
  set v15-compat

Then the usage is with the -A myrelay commandline option:

mail -A myrelay -s "Test email"

Using other clients should work in a normal fashion with their built in configuration options, just choose STARTTLS in the connection options (not direct SSL or similar). The use of direct SSL (smtps) to the server is not used herein; STARTTLS is more universally compatible.