Thursday, 19 September 2013

Configuring postfix under OS X (10.8) with stunnel to Virgin Media SMTP

I am using the system-default version of postfix, but with SASL authentication provided by dovecot (as configured in this post).  One additional complexity of my configuration is that I need to have my ISP (Virgin Media) handle outgoing mail for me, as if I don't then mail is often bounced by the destination server as I am part of a blacklisted subnet.  This is further complicated in that Virgin Media run their SMTP server on a non-standard port and postfix doesn't support encrypted delivery on non-standard ports (see this message on the VM forums for some clarification).

I'm not going to cover standard postfix configuration, as there are better sites than this to do that, but I want to cover the use of dovecot to provide SASL authentication to my SMTP clients (i.e. me using my MacBook or Phone to send e-mail) and the tunnelling of outgoing SMTP through to Virgin Media.

Postfix


These are the additions/changes I made to /etc/postfix/main.cf:

relayhost = [127.0.0.1]:8465

mydomain_fallback = localhost

smtpd_use_tls = yes
smtpd_tls_security_level = encrypt
smtpd_tls_auth_only = yes
smtpd_tls_key_file = /private/etc/ssl/private/mydomain_com.key
smtpd_tls_cert_file = /private/etc/ssl/mydomain_com.crt
smtpd_tls_CAfile = /private/etc/ssl/private/PositiveSSLCA2.crt
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = /opt/local/var/run/dovecot/auth-client
smtpd_sasl_security_options = noanonymous
smtpd_pw_server_security_options = gssapi,login,plain
smtp_use_tls = yes
smtp_sasl_auth_enable = yes
smtp_sasl_security_options =
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd

Note that the smtp_sasl_password_maps file looks like this:

$ ls -l /etc/postfix/sasl_passwd
-rw-------  1 root  wheel  114 Sep 18 10:57 sasl_passwd
$ sudo cat /etc/postfix/sasl_passwd
[127.0.0.1]:8465        username@virginmedia.com:password

Whenever you make a change to this file you need to run postmap on the file in order to recreate the hash file:

$ sudo postmap /etc/postfix/sasl_passwd

postfix launchd configuration


There is, of course, a default launchd configuration file for postfix, however I think I removed the Disabled key, in order to avoid having to use the -w flag with launchctl.  This is the whole file:

$ cat /System/Library/LaunchDaemons/org.postfix.master.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>KeepAlive</key>
        <true/>
        <key>Label</key>
        <string>org.postfix.master</string>
        <key>Program</key>
        <string>/usr/libexec/postfix/master</string>
        <key>ProgramArguments</key>
        <array>
                <string>master</string>
        </array>
        <key>QueueDirectories</key>
        <array>
                <string>/var/spool/postfix/maildrop</string>
        </array>
        <key>AbandonProcessGroup</key>
        <true/>
</dict>
</plist>


stunnel


In order to install stunnel, I used macports:

$ sudo port install stunnel

The stunnel configuration file is quite simple:

$ cat /opt/local/etc/stunnel/smtp_virginmedia.conf
client = yes
connect = smtp.virginmedia.com:465

This is simple as I want to run stunnel in "inetd" mode, which means that the system will listen on the local port used to tunnel the connection (port 8465) and invoke stunnel when a connection is accepted.  The alternative approach is to run stunnel in "daemon" mode where it runs all the time listening on whatever ports it is configured to tunnel.  I chose "inetd" mode as I thought it might be more efficient and I also wanted to play about with launchd configuration files.

OS X doesn't have a traditional inetd and instead uses launchd. Configuring that took a fair amount of time.  The macport stunnel port doesn't provide a launchd configuration file, however given stunnel came from macports I followed their convention of locating the .plist file in /opt/local/etc/LaunchDaemons and creating a symbolic link to /Library/LaunchDaemons:

$ sudo bash
# cd /opt/local/etc/LaunchDaemons
# mkdir org.macports.stunnel
# vi org.macports.stunnel/org.macports.stunnel.plist

Here are the contents:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
<plist version='1.0'>
<dict>
    <key>Label</key>
    <string>org.macports.stunnel</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/local/bin/stunnel</string>
<string>/opt/local/etc/stunnel/smtp_virginmedia.conf</string>
    </array>
    <key>inetdCompatibility</key>
    <dict>
        <key>Wait</key>
        <false/>
    </dict>
    <key>Debug</key>
    <false/>
    <key>Disabled</key>
    <false/>
    <key>KeepAlive</key>
    <false/>
    <key>Sockets</key>
    <dict>
        <key>Listeners</key>
        <dict>
            <key>SockServiceName</key>
            <string>8465</string>
            <key>SockType</key>
            <string>stream</string>
            <key>SockFamily</key>
            <string>IPv4</string>
        </dict>
    </dict>
</dict>
</plist>

# cd /Library/LaunchDaemons
# ln -s /opt/local/etc/LaunchDaemons/org.macports.stunnel/org.macports.stunnel.plist .
# launchctl load /Library/LaunchDaemons/org.macports.stunnel.plist

Testing


It took me a while to get the configuration working (mostly because of broken postfix configuration, but also because I was using the wrong username), however the following method is the best I found to test both the stunnel configuration and the authentication against the Virgin Media SMTP server:

Create the Base64 encoding of the username/password you put into /etc/postfix/sasl_passwd

$ perl -MMIME::Base64 -e 'print encode_base64("username");'
dXNlcm5hbWU=
$ perl -MMIME::Base64 -e 'print encode_base64("password");'
cGFzc3dvcmQ=

and invoke the smtp tunnel:

 $ telnet localhost 8465
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 know-smtprelay-3-imp bizsmtp ESMTP server ready
EHLO trojanfoe.com
250-know-smtprelay-3-imp hello [82.31.123.207], pleased to meet you
250-HELP
250-AUTH LOGIN PLAIN
250-SIZE 36700160
250-8BITMIME
250 OK
AUTH LOGIN
334 VXNlcm5hbWU6
dXNlcm5hbWU=
334 UGFzc3dvcmQ6
cGFzc3dvcmQ=
535 Authentication Credentials Invalid (VM300)
Connection closed by foreign host.

The reason the authentication failed was because I literally pasted the Base64 of "username" and "password", however once you get the username/password right you will be able to see this succeeding).

That's pretty much it; you'll need to unload postfix (if it's currently loaded) from launchd and reload it, using the normal:

$ sudo launchctl load /System/Library/LaunchDaemons/org.postfix.master.plist

Configuring Dovecot under OS X (10.8)

Installation

I use macports for any third-party software I want to install, so installing dovecot was as trivial as:

$ sudo bash
# port install dovecot

Configuration

dovecot Configuration

I had to make a number of changes to the dovecot.conf file in order to get dovecot working:

# cd /opt/local/etc/dovecot
# cp dovecot-example.conf dovecot.conf

Here is the configuration, with comments stripped using sed:

# sed '/^[ \t]*#/d' dovecot.conf | sed '/^[ \t]*$/d'

base_dir = /opt/local/var/run/dovecot/
protocols = imaps
log_path = /opt/local/var/log/dovecot.log
ssl = yes
ssl_cert_file = /private/etc/ssl/mydomain_com.crt
ssl_key_file = /private/etc/ssl/private/mydomain_com.key
ssl_ca_file = /private/etc/ssl/private/PositiveSSLCA2.crt
login_dir = /opt/local/var/run/dovecot/login
login_chroot = yes
login_user = _dovecot
mail_privileged_group = mail
protocol imap {
  login_executable = /opt/local/libexec/dovecot/imap-login
  mail_executable = /opt/local/libexec/dovecot/imap
}
protocol pop3 {
  login_executable = /opt/local/libexec/dovecot/pop3-login
  mail_executable = /opt/local/libexec/dovecot/pop3
}
protocol lda {
  auth_socket_path = /opt/local/var/run/dovecot/auth-master
}
auth_executable = /opt/local/libexec/dovecot/dovecot-auth
auth default {
  mechanisms = plain login
  passdb pam {
    args = login
  }
  userdb passwd {
  }
  user = root
  socket listen {
    master {
      path = /opt/local/var/run/dovecot/auth-master
      mode = 0660
    }
    client {
      path = /opt/local/var/run/dovecot/auth-client
      mode = 0660
    }
  }
}
dict {
}
plugin {
}


Note that the "socket listen" section only really needs the client part, and that will be used to provide authentication for postfix's smtpd.

pam configuration

None required (we cheated with "args = login" in the "passwd pam" section).

launchd configuration

Macports installs a working launchd configuration in /Library/LaunchDaemons/org.macports.dovecot.plist:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
<plist version='1.0'>
<dict>
<key>Label</key><string>org.macports.dovecot</string>
<key>ProgramArguments</key>
<array>
        <string>/opt/local/bin/daemondo</string>
        <string>--label=dovecot</string>
        <string>--start-cmd</string>
        <string>/opt/local/sbin/dovecot</string>
        <string>;</string>
        <string>--pid=fileauto</string>
        <string>--pidfile</string>
        <string>/opt/local/var/run/dovecot/master.pid</string>
</array>
<key>Debug</key><false/>
<key>Disabled</key><false/>
<key>KeepAlive</key><true/>
</dict>
</plist>

(I cannot remember if I changed the Disabled value from true to false, but you can leave it false and use -w in the commands below):

# launchctl load  /Library/LaunchDaemons/org.macports.dovecot.plist

If you make any configuration changes then it's best to unload the dovecot (change load to unload in the above command) and reload it .

Router configuration

You'll need to forward port 993 from your router through to the server, but I cannot help you there.