Configuring mail server on OpenBSD 6.5

This guide is mostly notes for myself rather than something readable, but it may be useful anyway. It contains an example of working configuration for OpenSMTPD, SpamPD, SpamAssassin, DKIM Proxy and Dovecot with Sieve support on OpenBSD.

Remember that you should always read manpages before copying someone elses configs. OpenBSD has excellent documentation.


Install required packages:

# pkg_add dkimproxy dovecot dovecot-pigeonhole spampd p5-Mail-SpamAssassin 

Create vmail user:

# useradd -m -d /var/vmail -s /sbin/nologin vmail

Firewall

/etc/pf.conf:

# reassemble fragments
set reassemble yes

# return ICMP for dropped packets
set block-policy return

# enable logging on em0 interface
set loginterface em0

# allow all on Loopback interface
set skip on lo

# define ICMP message types to let in
icmp_types = "{ 0, 8, 3, 4, 11, 30 }"

# scrub packets of weirdness
match in all scrub (no-df random-id max-mss 1440)

# drop urpf-failed packets, add label uRPF
block in quick log from urpf-failed label uRPF

# ICMP
pass in quick inet proto icmp icmp-type $icmp_types
pass in quick inet6 proto icmp6 

# allow SSH, SMTP and IMAP
pass in quick on em0 proto tcp from any to port { ssh, smtp, submission, smtps, imap, imaps }

# drop other incoming traffic
block in quick log on em0 all

# outbound traffic
pass out quick on em0 proto tcp from any to any modulate state
pass out quick on em0 proto udp from any to any keep state
pass out quick on em0 proto icmp from any to any keep state
pass out quick on em0 proto icmp6 from any to any keep state

Reload rules:

# pfctl -f /etc/pf.conf

OpenSMTPD

/etc/mail/smtpd.conf (replace ENCRYPTION_KEY with 32 character random string, for instance use openssl rand -hex 16):

queue compression
queue encryption ENCRYPTION_KEY

table aliases db:/etc/mail/aliases.db
table domains file:/etc/mail/domains
table users file:/etc/mail/users
table passwd file:/etc/mail/passwd

pki mail.example.org cert "/etc/ssl/mail.example.org.crt"        # path to SSL certificate
pki mail.example.org key "/etc/ssl/private/mail.example.org.key" # path to private key

smtp max-message-size 100M

listen on em0 tls auth-optional <passwd>                 # MTA
listen on em0 smtps auth <passwd>                        # MSA (auth required)
listen on em0 port submission tls-require auth <passwd>  # MSA (auth required)

listen on lo0
listen on lo0 port 10028 tag DKIM_OUT
listen on lo0 port 10026 tag SPAM_IN 

action vusers_lmtp_deliver     lmtp "/var/dovecot/lmtp" rcpt-to virtual <users>
action local_lmtp_deliver      lmtp "/var/dovecot/lmtp" rcpt-to alias <aliases>
action relay_spampd            relay host smtp://127.0.0.1:10025 # send to spampd
action relay_dkimproxy_out     relay host smtp://127.0.0.1:10027 # send to dkimproxy_out
action "relay"                 relay 

match \
    from local \
    for local \
    action local_lmtp_deliver

match \
    tag SPAM_IN \
    for domain <domains> \
    action vusers_lmtp_deliver

match \
    tag SPAM_IN \
    for local \
    action local_lmtp_deliver

match \
    tag DKIM_OUT \
    for any \
    action "relay"

match \
    from local \
    for any \
    action relay_dkimproxy_out

match \
    auth \
    from any \
    for any \
    action relay_dkimproxy_out

match \
    from any \
    for domain <domains> \
    action relay_spampd

/etc/domains:

example.org
example.com

/etc/mail/passwd (use smtpctl encrypt to encrypt passwords):

foo@example.org:ENCRYPTED_PASSWORD
bar@example.org:ENCRYPTED_PASSWORD
nonexistent@example.org:ENCRYPTED_PASSWORD

/etc/mail/users:

# example.com mapping to example.org
abuse@example.com         abuse@example.org
postmaster@example.com    postmaster@example.org
webmaster@example.com     webmaster@example.org

foo@example.com           foo@example.org
bar@example.com           bar@example.org

# example.org
abuse@example.org         foo@example.org
postmaster@example.org    foo@example.org
webmaster@example.org     foo@example.org

@example.com              nonexistent@example.org
@example.org              nonexistent@example.org

# final mapping
foo@example.org           vmail
bar@example.org           vmail
nonexistent@example.org   vmail

Append to /etc/mail/aliases if you want to receive mail for root:

root:        foo@example.org 

Generate /etc/mail/aliases.db:

# makemap -t aliases -o /etc/mail/aliases.db /etc/mail/aliases

Validate config:

# smtpd -n

SpamAssassin and SpamPD

Enable and start SpamAssassin:

# rcctl enable spamassassin
# rcctl start spamassassin

Enable, set flags for and start spampd:

# rcctl enable spampd
# rcctl set spampd flags "--port=10025 --relayhost=127.0.0.1:10026 --tagall --aw --rh --maxsize=256 -pid=/var/spampd/spampd.pid"
# rcctl start spampd

Fix permissions:

# chown _spampd /var/spool/spamassassin
# chown _spampd /var/spool/spamassassin/spampd

DKIM Proxy

/etc/dkimproxy_out.conf:

# specify what address/port DKIMproxy should listen on
listen    127.0.0.1:10027

# specify what address/port DKIMproxy forwards mail to
relay     127.0.0.1:10028

# specify what domains DKIMproxy can sign for (comma-separated, no spaces)
domain    example.org

# specify what signatures to add
signature dkim(c=relaxed)
signature domainkeys(c=nofws)

# specify location of the private key
keyfile   /etc/mail/dkim/private.key

# specify the selector (i.e. the name of the key record put in DNS)
selector  default

# control how many processes DKIMproxy uses
#  - more information on these options (and others) can be found by
#    running `perldoc Net::Server::PreFork'.
#min_servers 5
#min_spare_servers 2

Generate keys:

# mkdir /etc/mail/dkim
# openssl genrsa -out /etc/mail/dkim/private.key 2048
# openssl rsa -in /etc/mail/dkim/private.key -pubout -out /etc/mail/dkim/public.key
# chmod 640 /etc/mail/dkim/private.key 
# chgrp _dkimproxy /etc/mail/dkim/private.key

For DNS record, you will need public key without header and footer and with all lines concatenated:

# echo $(sed '1d;$d' /etc/mail/dkim/public.key | tr -d $'\n')

(Configuration of DKIM, SPF and other DNS records is out of scope of this guide.)

Enable and start dkimproxy_out:

# rcctl enable dkimproxy_out
# rcctl start dkimproxy_out

Dovecot

/etc/dovecot/conf.d/10-mail.conf:

mail_location = maildir:/var/vmail/%d/%n/Maildir:LAYOUT=fs

/etc/dovecot/conf.d/10-ssl.conf:

ssl = required
ssl_cert = </etc/ssl/mail.example.org.crt
ssl_key = </etc/ssl/private/mail.example.org.key

/etc/dovecot/conf.d/15-lda.conf:

postmaster_address = postmaster@example.org
hostname = example.org
lda_mailbox_autocreate = yes

/etc/dovecot/conf.d/20-lmtp.conf:

protocol lmtp {
  mail_plugins = $mail_plugins sieve
}

/etc/dovecot/conf.d/10-auth.conf:

#!include auth-system.conf.ext
!include auth-passwdfile.conf.ext

/etc/dovecot/conf.d/auth-passwdfile.conf.ext:

passdb {
  args = /etc/mail/passwd
  driver = passwd-file
}

userdb {
  args = uid=vmail gid=vmail home=/var/vmail/%d/%n
  driver = static
}

To debug auth issues, edit /etc/dovecot/conf.d/10-logging.conf:

auth_verbose = yes

Append to /etc/login.conf:

dovecot:\
        :openfiles-cur=2048:\
        :openfiles-max=4096:\
        :tc=daemon:

Rebuild database:

# cap_mkdb /etc/login.conf

Spam handling with Sieve

/etc/dovecot/conf.d/90-sieve.conf:

plugin {
   sieve_before = /var/vmail/sieve/
}

Create scripts directory:

# mkdir /var/vmail/sieve
# chown vmail:vmail /var/vmail/sieve

Create script /var/vmail/sieve/junk.sieve:

require "fileinto";
if header :contains "X-Spam-Flag" "YES" {
    fileinto "Junk";
    stop;
}

Enable and start dovecot:

# rcctl enable dovecot
# rcctl start dovecot

Optional: autoconfiguration support for Thunderbird

Check this and this for more information.

/etc/httpd.conf:

ext_ip = "YOUR_EXTERNAL_IP"

server "default" {
    listen on $ext_ip port 80
    root "/default-server/htdocs"
}

server "autoconfig.example.org" {
    listen on $ext_ip port 80
    root "/autoconfig.example.org"
}

types {
    include "/usr/share/misc/mime.types"
}

/var/www/autoconfig.example.org/mail/config-v1.1.xml:

<?xml version="1.0" encoding="UTF-8"?>
<clientConfig version="1.1">
  <emailProvider id="example.org">
    <domain>example.org</domain>
    <displayName>Example.org Mail</displayName>
    <displayShortName>example</displayShortName>
    <incomingServer type="imap">
      <hostname>mail.example.org</hostname>
      <port>993</port>
      <socketType>SSL</socketType>
      <authentication>password-cleartext</authentication>
      <username>%EMAILADDRESS%</username>
    </incomingServer>
    <outgoingServer type="smtp">
      <hostname>mail.example.org</hostname>
      <port>465</port>
      <socketType>SSL</socketType>
      <authentication>password-cleartext</authentication>
      <username>%EMAILADDRESS%</username>
    </outgoingServer>
    <outgoingServer type="smtp">
      <hostname>mail.example.org</hostname>
      <port>587</port>
      <socketType>STARTTLS</socketType>
      <authentication>password-cleartext</authentication>
      <username>%EMAILADDRESS%</username>
    </outgoingServer>
  </emailProvider>
</clientConfig>

Enable and start httpd:

# rcctl enable httpd
# rcctl start httpd  
If you have any comments, contact me by email.