Secure SMTP and POP access on FreeBSD

I was toying idly with the idea of moving to a new ISP who, while offering a faster connection, did not offer an email facility. This would mean I'd either have to use free email facilities (such as gmail) or start using the domain names I owned already for mail addresses. The latter course naturally implied that I'd need to set up the FreeBSD-based mail server to allow POP and SMTP relay access from outside the LAN, as I often work away from home for brief periods.

Security Certificate

After some research, I determined that I'd need to setup an X.509 certificate to ensure that any SMTP/POP traffic from the Internet would be encrypted and authenticated.

I started by setting up a POP capability. I leaned heavily an article on http://www.vanemery.com/Protocols/POP/qpopper-bsd-howto.html (no longer available) on setting up a secure POP capability on FreeBSD, using qpopper. I already had qpopper installed, and this article gave a clean, step-by-step process to follow.

First, here's the steps to create a certificate, boiled down into a simple script:

  # generate key for root CA cert
  openssl genrsa -des3 -out hydrus-ca.key 2048
  # generate root CA cert
  openssl req -new -x509 -days 3650 -key hydrus-ca.key -out hydrus-ca.crt
  # generate key for mail cert
  openssl genrsa -out mail-key.pem 1024
  # generate cert request; this has to be signed by the CA
  openssl req -new -key mail-key.pem -out mail-cert.csr
  # sign certificate request, using mini-CA facility of x509
  openssl x509 -req -in mail-cert.csr -out mail-cert.pem -sha1 \
   -CA hydrus-ca.crt -CAkey hydrus-ca.key -CAcreateserial -days 3650
  # copy cert and key to location accessible by sendmail/qpopper
  cp mail-cert.pem /etc/mail/certs
  cp mail-key.pem /etc/mail/certs
  chmod 400 /etc/mail/certs/*

Running this script requires a lot of user input, in order to fill in the key fields of the root CA certificate and the mail certificate request. It is possible to create an openssl configuration file to pre-fill these inputs, as I discovered via this write-up on certificate set-up, this time for Debian.

Combining these approaches, we arrive at a modified script to create the root CA and the mail certificate:

  # generate key for root CA cert
  openssl genrsa -des3 -out hydrus-ca.key 2048
  # generate root CA cert
  openssl req -new -x509 -key hydrus-ca.key -out hydrus-ca.crt \
  -config openssl.conf
  # generate key for mail cert
  openssl genrsa -out mail-key.pem 1024
  # generate cert request; this has to be signed by the CA
  # remember to set the Common Name to the FQDN of the server, otherwise
  # users will be plagued at cert acceptance.
  openssl req -new -key mail-key.pem -out mail-cert.csr -config openssl.conf
  # sign certificate request, using mini-CA facility of x509
  openssl x509 -req -in mail-cert.csr -out mail-cert.pem -sha1 \
   -CA hydrus-ca.crt -CAkey hydrus-ca.key -CAcreateserial -days 3650
  # copy cert and key to location accessible by sendmail/qpopper
  cp mail-cert.pem /etc/mail/certs/cert.pem
  cp mail-key.pem /etc/mail/certs/key.pem
  chmod 400 /etc/mail/certs/*

The openssl.conf file the above script relies on contains the following:

  # 
  # OpenSSL configuration file. 
  # 

  # Establish working directory. 
  dir = . 

  [ ca ] 
  default_ca = CA_default 

  [ CA_default ] 
  default_days = 3650
  default_md = md5 
  preserve = no 
  email_in_dn = no 
  nameopt = default_ca 
  certopt = default_ca 
  policy = policy_match 

  [ policy_match ] 
  countryName = match 
  stateOrProvinceName = match 
  organizationName = match 
  organizationalUnitName = optional 
  commonName = supplied 
  emailAddress = optional

  [ req ] 
  default_bits = 2048 # Size of keys 
  default_md = md5 # message digest algorithm 
  string_mask = nombstr # permitted characters 
  distinguished_name = req_distinguished_name 

  [ req_distinguished_name ] 
  # Variable name   Prompt string 
  #----------------------   ---------------------------------- 
  0.organizationName = Organization Name (company) 
  organizationalUnitName = Organizational Unit Name (department, division) 
  emailAddress = Email Address 
  emailAddress_max = 40 
  localityName = Locality Name (city, district) 
  stateOrProvinceName = State or Province Name (full name) 
  countryName = Country Name (2 letter code) 
  countryName_min = 2 
  countryName_max = 2 
  commonName = Common Name (hostname, IP, or your name) 
  commonName_max = 64 

  # Default values for the above, for consistency and less typing. 
  # Variable name   Value 
  #------------------------------   ------------------------------ 
  0.organizationName_default = hydrus.org.uk
  localityName_default = Reading
  stateOrProvinceName_default = Berkshire
  countryName_default = UK

Setting up qpopper

Enable qpopper on pop3 and pop3s with the following lines in /etc/inetd.conf.

  #
  # Qpopper pop3 server
  #
  pop3   stream  tcp  nowait  root  /usr/local/libexec/qpopper  \ 
    qpopper -f /usr/local/etc/qpopper/STLS-110.conf
  pop3s  stream  tcp  nowait  root  /usr/local/libexec/qpopper  \ 
    qpopper -f /usr/local/etc/qpopper/TLS-995.conf

Setup the qpopper configuration files:

/usr/local/etc/qpopper/TLS-995.conf

  # Qpopper Configuration File #1
  # TCP 995 for those e-mail clients that can't handle STARTTLS (STLS)
 
  set tls-support = alternate-port
  set config-file = /usr/local/etc/qpopper/qpopper.conf

/usr/local/etc/qpopper/STLS-110.conf

  # Qpopper Configuration File #2
# Runs on TCP 110, uses STARTTLS (STLS) method
 
set tls-support = stls
set config-file = /usr/local/etc/qpopper/qpopper.conf

/usr/local/etc/qpopper/qpopper.conf

  # Qpopper Main Configuration File

  set statistics
  set downcase-user
  set timeout = 180
  
  set clear-text-password = tls
  set tls-server-cert-file = /etc/mail/certs/cert.pem
  set tls-private-key-file = /etc/mail/certs/key.pem

Setting up sendmail

This part was mostly achieved by reference to this secure sendmail article. To precis that article, first you need saslauthd, which performs a password check on behalf of sendmail. I obtained this by:

  pkg_add -r cyrus-sasl-saslauthd 

Ensure that /usr/local/lib/sasl2/Sendmail.conf exists and contains the following line:

  pwcheck_method: saslauthd

I already had cyrus-sasl2 installed to support authentication against my ISPs mail server. Enable saslauthd by adding saslauthd_enable="YES" to /etc/rc.conf

To instruct sendmail to listen on the smtps port, you must add to the /etc/make.conf options for sendmail (in addition to the settings to compile/link against sasl2):

  # Adding to enable alternate port (smtps) for sendmail...
  SENDMAIL_CFLAGS+= -D_FFR_SMTP_SSL

Rebuild sendmail (ala an earlier journal entry).

Add trust for authenticated users and location of certificates to the sendmail config file, /etc/mail/crimson.mc

  dnl Settings for SMTP AUTH as client and server
  define(`confAUTH_MECHANISMS',`LOGIN PLAIN')
  dnl Allow authenticated users relay access
  TRUST_AUTH_MECH(`PLAIN LOGIN')dnl
  dnl Offer SMTP AUTH only after encryption (STARTTLS) has been negotiated
  define(`confAUTH_OPTIONS',`p,y')dnl
  dnl Don't ask for client cert(s)
  define(`confTLS_SRV_OPTIONS', `V')
  define(`CERT_DIR', `/etc/mail/certs')dnl
  define(`confCACERT_PATH', `CERT_DIR')dnl
  define(`confCACERT', `CERT_DIR/cert.pem')dnl
  define(`confSERVER_CERT', `CERT_DIR/cert.pem')dnl
  define(`confSERVER_KEY', `CERT_DIR/key.pem')dnl
  define(`confCLIENT_CERT', `CERT_DIR/cert.pem')dnl
  define(`confCLIENT_KEY', `CERT_DIR/key.pem')dnl
  DAEMON_OPTIONS(`Port=smtp, Name=MTA')dnl
  dnl Offer STARTTLS at session beginning for smtps (M=s)
  DAEMON_OPTIONS(`Port=smtps, Name=TLSMTA, M=s')dnl

The flags for AUTH_OPTIONS are none-too easy to find, but are described in the doc/op.me file that comes with the sendmail distribution. The options used above are as follows:

  p	  don't permit mechanisms susceptible to simple
	  passive attack (e.g., PLAIN, LOGIN), unless a
	  security layer is active.
  y	  don't permit mechanisms that allow anonymous login.

There is one more option that might be useful:

  A	  Use the AUTH= parameter for the MAIL FROM
	  command only when authentication succeeded.
	  This can be used as a workaround for broken
	  MTAs that do not implement RFC 2554 correctly.

Then, build the sendmail.cf file and restart sendmail:

  cd /etc/mail
  make && make install && make restart

DNS Changes

To be able to use the hydrus.org.uk domain as an email address, I had to add an IP address for hydrus.org.uk in the local DNS server (as described in this Linux oriented DNS article). Otherwise, crimson would refuse to accept mail, saying the "hydrus.org.uk" domain name was unknown. The start of the BIND zone file for hydrus.org.uk now looks like:

  hydrus.org.uk  IN SOA  crimson.hydrus.org.uk. root.crimson.hydrus.org.uk. (
                    10612      ; serial
                    10800      ; refresh (3 hours)
                    3600       ; retry (1 hour)
                    604800     ; expire (1 week)
                    3600       ; minimum (1 hour)
                    )
                 NS      crimson.hydrus.org.uk.
                 A       192.168.0.10