Host emails §

To get in charge of your communication tools is a big deal to more freedom and independence. This part will describe how to host an email server at home. No database will be required because it has no benefit on a small server. Let's keep things simple !

Setting up a mail server is often considered difficult. That's why we will split this tutorial in small easy steps: complex, but not complicated.

In order, you will:

Of course, you'll learn how to add new accounts and how to configure a mail client.

You first have to open port 25 (smtp) in your firewall and router.

Some ISP are very restrictive with this port. You can ask them to be more permissive, but make inquiries first to avoid bad surprises. At last resort, we'll discuss how to use external smtp serices.

Read also opensmtpd main developper article about this subject.

https://poolp.org/posts/2019-09-14/setting-up-a-mail-server-with-opensmtpd-dovecot-and-rspamd/

DNS configuration for an email server §

Emails requires specific DNS records. You have add at least two new fields.

First, make sure you have an "A" record (probably):

athome.tld.  IN  A  192.0.2.2

Of course, use your real IP here.

You must add a MX field pointing to the previous record:

athome.tld.  IN  MX  1  athome.tld.

Notice ending dots.

"1" describe the weight of the field. This parameter will be used to prioritize mails servers because some of them will be backups if the main server is unavailable. We'll talk about this later.

Some may want to use a specific domain, another A field to organize their mail servers. That is not mandatory, but interesting:

mail1.athome.tld. IN  MX  1  mail1.athome.tld.
mail1.athome.tld. IN  A      192.0.2.2

Below is another example using a different syntax:

$ORIGIN athome.tld
[...]
@      IN  MX  10  mail1.athome.tld.
@      IN  A       192.0.2.2
mail1  IN  A       192.0.2.3

Here, you can see the mail server for this zone is hosted on a different IP.

That's all for now.

Get certificates §

To encrypt traffic with your email server, you'll need TLS certificates.

It is explained in the chapter about httpd.

Notice you can use self-signed certificates if you prefer, that is absolutely not an issue. (see man ssl(8))

In the next parts, we'll consider using letsencrypt certificates to keep consistent.

Just remember to have a certificate for the domain of the MX field of your server.

A mail server in 10 minutes (almost ^^) §

I you're in a hurry, here is a quick setup to have a working email server. It will also be a good starting point to build on later. With the following:

Later, we'll discuss virtual users to go further and achieve more specific tasks. But here, we're in a rush so let's go! 😊

smtpd.conf file §

Edit /etc/mail/smtpd.conf:

# Generic config
# Tables 
table aliases "/etc/mail/aliases"

# Certificates
pki athome.tld key "/etc/ssl/private/athome.tld.key"
pki athome.tld cert "/etc/ssl/athome.tld.crt"

# Get messages
listen on all tls pki athome.tld 
# To send with mail client
listen on all port submission tls-require pki athome.tld auth 

# ACTIONS
action "sendthismail" relay 

# System aliases
action in_maildir maildir alias <aliases>

# Get
match for local action in_maildir
match from any for domain athome.tld action in_maildir

# Sending
match for any action "sendthismail"
match auth from any for any action "sendthismail"

That's it 😊

There a almost more comments than reals instructions.

Dovecot configuration (IMAP) §

Install dovecot:

# pkg_add dovecot.

In /etc/dovecot/local.conf add:

# listen on IPV4 and IPV6.
listen = *, [::]

# We use Maildir
mail_location = maildir:~/Maildir

# only username, not user@domain.tld to auth
auth_username_format = %n

# imap > pop
protocols = imap

# Safety. Edit those lines !
ssl = yes
ssl_cert = </etc/ssl/athome.tld.crt
ssl_key = </etc/ssl/private/athome.tld.key
disable_plaintext_auth = yes

# auth methods
passdb {
        driver = bsdauth
}

userdb {
        driver = passwd
}

Reload dovecot:

# rcctl restart dovecot

Here you go, it's done, you can configure your mail client.

Full mail server with virtual users §

"Virtual users" means they are not UNIX system users with a shell. However, they are obviously using your mail server.

New accounts won't be created with adduser but you will edit a file with the username and it's password hash.

Here we'll discuss how to install a SMTP (Opensmtpd) and IMAP (Dovecot) server

One user to rule them all: _vmail §

We will create an user with the sweet name "_vmail" to manage every emails access. It won't have any shell, it's safer 😊.

# useradd -m -g =uid -c "Virtual Mail" -d /var/vmail -s /sbin/nologin _vmail

A new directory is created: /var/vmail. Emails will be stored inside, but following a structure with subdirectories for each virtual users. As example:

/var/vmail/athome.tld/batman/Maildir
/var/vmail/athome.tld/user/Maildir
/var/vmail/athome.tld/ninja/Maildir
...

/etc/mail/virtuals §

In this file, we write user's emails, one on each line. Actually, it works like /etc/mail/aliases. (read man aliases(5) 😉)

heroes@athome.tld batman@athome.tld,superman@athome.tld
batman@athome.tld _vmail
superman@athome.tld _vmail
scoobydoo@athome.tld _vmail

As you can see, every email addresses belongs to user _vmail.

Notice the first line: you see an example showing how to transfer emails sent to "heroes@athome.tld" to "batman@athome.tld" and "superman@athome.tld".

/etc/mail/passwd §

Same as before, one line per passphrase:

batman@athome.tld:$2b$09$lerdFpdQtnu.Bs5EpAsVbeF851GjdD0aza8IDhho38i1DOHk.ujzi
superman@athome.tld:$2b$09$VRU/CYJUS3QZHVUFP70xIOURPbiNQeyOEZHoZo6NOY3uO.XSpd2MW

Notice how ":" split email address and the hashed passphrase: <email>:<hash>.

To hash passphrases, use encrypt:

encrypt -b a -p

Or

smtpctl encrypt passphrase

You're prompted to enter the passphrase. Press "Enter" to see the hash of the password and copy it in /etc/mail/passwd.

(Facultative) Adjust files permissions §

Previous files shouldn't be readable by everyone. Let's adjust permissions so root and mail daemons -- dovecot and smtpd -- can read passwords. It is not mandatory, but I suggest this part as good habits that can hurt anyone 😉.

Dovecot and smtpd run as users _dovecot and _smtpd. If one day, one of this process is compromised, it will avoid privilege escalation and threaten the whole system.

Let's create a new group _maildaemons for _dovecot and _smtpd to ease permissions:

# groupadd _maildaemons
# usermod -G _maildaemons _smtpd
# usermod -G _maildaemons _dovecot

Of course, if you haven't installed dovecot yet since we discuss about it later: "# pkg_add dovecot".

Now we can set owner and group for files storing logins and passwords:

# chown root:_maildaemons /etc/mail/passwd /etc/mail/virtuals

Finally, only root can modify these files and _maildaemons can only read. Others can't do anything:

# chmod 640 /etc/mail/passwd /etc/mail/virtuals

To check everything is as expected:

# ls -l /etc/mail/passwd
-rw-r-----  1 root  _maildaemons  17226 Nov 12 08:40 /etc/mail/passwd

Opensmtpd (smtpd) config §

Opensmtpd or smtpd is the default mail server shipped with OpenBSD. You just have to configure it.

First of all, make sure you opened ports: 25 (smtp), 587 (submission) and 993 (imaps). The latter will be user with dovecot. Do not care about 465 (smtps) since it is deprecated.

To configure smtpd, edit /etc/mail/smtpd.conf. Instructions will be applied in order.

We will split it in 3 parts:

Adjust the example below to your needs:

# Generic configuration
# Tables 
table aliases "/etc/mail/aliases"
table passwd "/etc/mail/passwd"
table virtuals "/etc/mail/virtuals"

# Certificates
pki athome.tld key "/etc/ssl/private/athome.tld.key"
pki athome.tld cert "/etc/ssl/athome.tld.crt"

# Listening ports
# Reception
listen on all tls pki athome.tld 
# Sending with a mail client
listen on all port submission tls-require pki athome.tld auth <passwd> 

# ACTIONS 
action "sendthismail" relay 
action local_mail maildir alias <aliases>
action virtual_maildir maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}/Maildir" virtual <virtuals>

# In/Out
# Reception
# Message for virtual users
match from any for domain athome.tld action virtual_maildir
# Message for system users
match from any for local action local_mail

# Sending
match auth from any for any action "sendthismail"
match for any action "sendthismail"

There is almost nothing to change in this file, except domain name.

WAIIIIIT a minute! Tell me more!

Take a look at each lines:

First, there are generic instructions

Then, we define actions applied on envelopes.

At last, we apply actions according to envelope criterias.

If not mentioned, the rule match a local mail. In other case, we add "from any".

Finally, in order to label your outgoing messages with the appropriate domain name, add in /etc/mail/mailname your email's domain name. It's the domain mentioned in the MX record.

athome.tld

Now enable and start smtpd:

# rcctl enable smtpd
# rcctl restart smtpd

That's all for smtpd 😊.

Dovecot §

We will use Dovecot as IMAP server so you can read mails from a client like Thunderbird.

As usual, install dovecot with pkg_add:

# pkg_add dovecot

Now configure dovecot. Edit /etc/dovecot/local.conf:

# listen IPv4 and IPv6
listen = *, [::]

# imap
protocols = imap

# Encryption. Edit those lines according to your certificates.
ssl = yes
ssl_cert = </etc/ssl/athome.tld.crt
ssl_key = </etc/ssl/private/athome.tld.key
disable_plaintext_auth = yes

# where mails are stored. %d is domain, %n is username
mail_location = maildir:/var/vmail/%d/%n/Maildir

# essential since we edited persmission on /etc/mail/passwd
service auth {
    user = $default_internal_user
    group = _maildaemons
}

# Auth methodes
passdb {
    args = scheme=blf-crypt /etc/mail/passwd
    driver = passwd-file
}

# Mails are /var/vmail , belongs to _vmail
userdb {
    driver = static
    args = uid=_vmail gid=_vmail home=/var/vmail/%d/%n/ 
}

I left comments above to help you understand what is done here.

Adjust ssl_cert and ssl_key according to your certificates.

By the way, ssl configuration is already available in /etc/dovecot/conf.d/10-ssl.conf. It is supposed to help us with a script generating a self-signed certificate. However, you probably already have your own. You should comment lines in this file:

# file: /etc/dovecot/conf.d/10-ssl.conf
#ssl_cert = </etc/ssl/dovecotcert.pem
#ssl_key = </etc/ssl/private/dovecot.pem

Finally, reload mail daemons:

# rcctl enable dovecot
# rcctl start dovecot
# rcctl restart smtpd

Now you can use a mail client to read your messages.

Add a mail account §

Add a new line for the new account in /etc/mail/virtuals and /etc/mail/passwd then reload tables for smtpd (dovecot read them on the fly):

smtpctl update table virtuals
smtpctl update table passwd

Or # rcctl restart smtpd

Manage multiple domains §

You can host an email server and manage multiple domain names.

However, you should organize how you set this up.

Below are a few notes on how to achieve this.

smtpd changes for multi-domains §

I suggest to create a file containing every hosted domain, one per line. Let's call it /etc/mail/domains:

athome.tld
domain.tld
other.bar

Now, in /etc/mail/smtpd.conf you can write one line for multiple domains:

table domains "/etc/mail/domains"
...
match from any for domain <domains> action virtual_maildir

Take care of used TLS certificates. If you have a certificate for each domain, you can specify each of them in smtpd.conf. Make sure you have a default certificate in the end (*).

pki athome.tld key "/etc/ssl/private/athome.tld.key"
pki athome.tld cert "/etc/ssl/athome.tld.crt"
pki domain.tld key "/etc/ssl/private/domain.tld.key"
pki domain.tld cert "/etc/ssl/domain.tld.crt"
pki other.bar key "/etc/ssl/private/other.bar.key"
pki other.bar cert "/etc/ssl/other.bar.crt"
pki "*" key "/etc/ssl/private/athome.tld.key"
pki "*" cert "/etc/ssl/athome.tld.crt"
...
listen on all tls
...
listen on all port submission tls-require auth <passwd>

HOWEVER, you can use only one certificate matching multiple domains. To do so, use "alternative names" in acme-client configuration. It is absolutely valid and much easier to manage. If so, configure smtpd as if there was only one certificate.

dovecot changes to support multiple domains §

Dovecot will need some care to handle certificates for each domain. Add "local_name" sections in its configuration so it looks like this:

ssl = yes
ssl_cert = </etc/ssl/athome.tld.crt
ssl_key = </etc/ssl/private/athome.tld.key
# no plaintext
disable_plaintext_auth = yes

local_name domain.tld {
        ssl_cert = </etc/ssl/domain.tld.crt
        ssl_key = </etc/ssl/private/domain.tld.key
}
local_name other.bar {
        ssl_cert = </etc/ssl/other.bar.crt
        ssl_key = </etc/ssl/private/other.bar.key
}

Here also, a single certificate for multiple domains is much more easier to set up.

Redirecting mails §

As explained in man aliases(5), you can redirect mails to various destination.

As example, you host jwayne@athome.tld and want to redirect every mails received by jwayne to batman@wayne.com.

Edit /etc/mail/virtuals and add:

jwayne.athome.tld: batman@wayne.com

You can set redirections in /etc/mail/aliases too. For system users, you can use their login only:

root: jdoe
hostmaster: root
postmaster: root
webmaster:  bruce
jdoe: jdoe@domain.tld

Remember to restart smtpd so changes are considered:

# rcctl restart smtpd

Easy. 😎

How to configure you mail client ? §

To use your mail server with a client such as Thunderbird, use the parameters below:

DNS record for clients configuration (facultative) §

To help clients finding automatically how to configure parameters to use your mail server, you can add specific DNS records.

_submission._tcp.athome.tld  86400   IN  SRV 0 1 587 athome.tld
_imap._tcp.athome.tld        86400   IN  SRV 0 0 0   .
_imaps._tcp.athome.tld       86400   IN  SRV 0 1 993 athome.tld

Read rfc6186 about these records.

https://datatracker.ietf.org/doc/html/rfc6186

Do not loose mails: MX fields and backup §

If, for reasons, your server is offline for a few days or a week, mails could be lost. Normally, senders try again for a while if they couldn't deliver a message.

However, you can plan secondary mail servers to keep your messages if yours is unreachable. You only need:

It will keep messages for your server in queue and will deliver them as soon it gets back online.

On your side, add a new MX field in your DNS zone with a bigger weight. This field points to the secondary server:

@                     IN MX 10  athome.tld.
@                     IN MX 70  mail.friend.eu.

In the above example, your server is ligther (10) than the secondary (70). This means a sending server will try first to deliver on your server, then on the secondary if the first failed to answer.

On his side, your friend just has to add your domain as a backup. If he uses smtpd, then his /etc/mail/smtpd.conf should look like this:

action relaybackup relay backup mx "athome.tld" 
...
match from any for domain athome.tld action relaybackup

Of course, you should return the favor 😉.

Do not be considered as a spam (SPF, DKIM...) §

Some servers could consider your mails as spams. There are a few proofs of good faith you can setup if possible. They are not mandatory. Keep in mind that most spams comes from most well known servers (gmail, I'm looking at you) so don't be too hard on yourselves.

Reverse DNS §

Your ISP might let you configure a reverse DNS. As it suggests, a reverse DNS links your IP to your domain name.

Look in your ISP panel for reverse DNS, or ask them directly. They are responsible for this 😉.

If you can't and reaaaallly want a rDNS, you could rent a VPN and get a dedicated IP with a rDNS configured by the VPN provider. But in most cases, following next steps should be sufficient.

SPF §

SPF records show that only YOUR server is allowed to send mails for YOUR domain name. Since it's usually the server's admin who also deals with DNS record, it's a proof of good faith.

Add a DNS record of type SPF in your zone such as:

athome.tld.   SPF "v=spf1 a mx ~all"

Or use a TXT field if SPF is not available:

athome.tld. TXT "v=spf1 a mx ~all"

Above is a very simple example that works for most cases. Consider reading about SPF records if you want to fine-tune this record.

DKIM signing §

With a private key, your server will sign outgoing emails. In DNS records, you will publish a public key to let recipient check if it matches the signature from your server.

Ahem... Say that again?

Here we go. We will generate a private and a public key.

Private key is used to sign mails. It is "private" because you must be the only one able to add a signature to outgoind mails.

Public key displayed in DNS record -- viewable by all -- let one check signature authenticity. You can see it as a unique puzzle piece, the only one that can fit the puzzle.

We'll see two ways to sing outgoing messages: one with an smtpd extension, the other with dkimproxy. You also could to the same with rspamd, it is described later, following the same method to generate keys. Choose the one you prefer 😉.

Following commands below, you will create a directory for keys, set permissions on this folder and go inside before generating keys with openssl and set permissions on the private part:

# mkdir -p /etc/dkim/
# chmod 770 /etc/dkim/
# cd /etc/dkim/
# openssl genrsa -out private.key 2048               
# openssl rsa -in private.key -pubout -out public.key
# chmod 400 private.key

Add a DKIM or TXT field so anyone can check signature on your messages match what is published in DNS records.

Mails will receive a flag "dkimpubkey" when signed, it is used to identify the signature in DNS record.

Replace ... by the content of public.key:

# cat /etc/dkim/public.key

The record will look like this:

dkimpubkey._domainkey    IN TXT    ( "v=DKIM1; k=rsa; t=s;p=v+Fb...vhP/oB")

Since smtpd has filters support, you can sign your messages with port opensmtpd-filter-dkimsign:

# pkg_add opensmtpd-filter-dkimsign

Make sure the script can read keys generated as seen before:

# chown -R _dkimsign:_dkimsign /etc/dkim/

In /etc/mail/smtpd.conf, you add now a new filter with instructions on how to sign messages:

filter "dkimsign" proc-exec "filter-dkimsign \
    -d <domain> \
    -s <selector> \
    -k /etc/dkim/private.key" \
    user _dkimsign group _dkimsign

Replace <domain> with your domain name and <selector> by dkimpubkey: that's what we defined in the DNS field earlier.

Now, make sure outgoing mails are processed by this new filter. In /etc/mail/smtpd.conf, the line for outgoing messages now look like this:

listen on all port submission tls-require auth <passwd> filter "dkimsign"

As an alternative, you can use dkimproxy if you prefer to sign mails.

As usual:

# pkg_add dkimproxy

Make sure dkimproxy can read keys:

# chown -R _dkimproxy:_dkimproxy /etc/dkim/

Now configure dkimproxy to match you configuration (domain and DNS selector):

In /etc/dkimproxy_out.conf:

listen    127.0.0.1:10027
relay     127.0.0.1:10028
domain    athome.tld
signature dkim(c=relaxed)
signature domainkeys(c=nofws)
keyfile   /etc/dkim/private.key
selector  dkimpubkey

Of course, edit domain and selector according to your DNS record.

Now, tell smtpd to listen for incoming signed and to-send messages on port 10028. They will be tagged "DKIM". Then we can send mails with this tag. On the contrary, they are forwarded to dkimproxy on port 10027.

In /etc/mail/smtpd.conf:

listen on lo0 port 10028 tag DKIM   
...
match tag DKIM for any action "sendthismail"
match auth tag DKIM from any for any action "sendthismail"

...
action dkimproxy relay host smtp://127.0.0.1:10027 

...
match auth from any for any action dkimproxy
match for any action dkimproxy

Finally enable dkimproxy and restart smtpd:

# rcctl enable dkimproxy_out
# rcctl start dkimproxy_out
# rcctl restart smtpd

Check it works §

Follow instructions on mail-tester.com. You'll send a mail to a randomized recipient and get a score:

https://www.mail-tester.com/

../../mail-tester-en.png

You may read about dmarc and other advices if you want to fin tune your configuration. Remember your score is already better than most "big" provider. Last time I checked with a gmail address, I got 6.1/10... 😜

Blocking ISP: use external SMTP §

If your ISP restrict the use of smtp port (25), you can't send any email from your server. To fix this, you may:

However, you need another smtp provider. Put the necessary credentials to access this mail account in /etc/mail/secrets:

# echo "secret_id user:passphrase" > /etc/mail/secrets

Make sure permissions are appropriate, you don't want everyone to know your password:

# chmod 640 /etc/mail/secrets
# chown root:_smtpd /etc/mail/secrets

Then, edit /etc/mail/smtpd.conf so outgoing messages go through external mail server:

...
table secrets "/etc/mail/secrets"
...
listen on all...
...

action "relay" relay host smtp+tls://secret_id@smtp.example.com \
      auth <secrets> mail-from "@athome.tld"

...
match from any for any action "relay"

Some details:

In the end, reload smtpd.

rcctl restart smtpd

Read the example in the end of smtpd manpage to learn more.

https://man.openbsd.org/smtpd.conf

Avoid receiving spam: senderscore filter §

Senderscore keeps a database of mail server reputation.

https://www.senderscore.org/

A moderate reputation doesn't mean much, but a bad one means the server already has sent spams before.

You can use a filter in smtpd to recognize as spam incoming mails according to senderscore.

Install opensmtpd-filter-senderscore port.

Then, edit /etc/mail/smtpd.conf to add a filter:

filter senderscore \
         proc-exec "filter-senderscore -junkBelow 70 -slowFactor 2000"
...
listen on all tls pki athome.tld filter { senderscore }

Avoid receiving spams: spamassassin §

spamassassin is a well-known anti-spam. It is used by OpenBSD project with spamd for their mailing lists.

https://spamassassin.apache.org/

Quick setup using filter-spamassass §

You may want to use the filter opensmtpd-filter-spamassassin: install the port then read /usr/local/share/doc/pkg-readmes/opensmtpd-filter-spamassassin.

In a few words:

# pkg_add p5-Mail-SpamAssassin opensmtpd-filter-dkim
# rcctl enable spamassassin
# rcctl start spamassassin

(no need spampd here)

/etc/mail/smtpd.conf:

filter "spamassassin" proc-exec "filter-spamassassin"
[...]
listen on all tls pki chezmoi.tld.pki filter { spamassassin senderscore }

Using tags §

Let's see how to use it with smtpd using tags since the setup is interesting if you want to understand how things works.

Install appropriate ports:

# pkg_add p5-Mail-SpamAssassin spampd

Then, run spamassassin daemons. You may add a few flags to make sure it is running as we expect.

# rcctl enable spampd
# rcctl set spampd flags "--relayhost=127.0.0.1:10026"
# rcctl start spampd
# rcctl enable spamassassin
# rcctl start spamassassin

Every incoming mails now must be checked by spammassassin, which is listening on port 10025. Once they've been scanned, they will be delivered on port 10026 and tagged "SPAMASSASSIN" so we don't check them twice.

That's how it looks in /etc/mail/smtpd.conf:

...
# mails checked by spammassassin
listen on lo0 port 10026 tag SPAMASSASSIN
...
action spamassassin relay host smtp://127.0.0.1:10025 
...
# system mails
accept from local for local alias <aliases> deliver to maildir "~/Maildir"
# Mails scanned by SPAMASSASSIN
match tag SPAMASSASSIN from any for domain "athome.tld" action virtual_maildir
# not tagged mails, must be checked by spammassassin
match from any for domain "athome.tld" action spamassassin
...

Spams subject are modified by spammassassin.

You can automatically delete them, but it's a risk in case of false positive.

https://wiki.apache.org/spamassassin/DeletingAllMailsMarkedSpam

In order to save spams in a junk folder, modify smtpd's action so it checks for X-spam header:

# /etc/mail/smtpd.conf
action virtual_maildir maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}/Maildir" junk virtual <virtuals>

Avoid receiving spams: rspamd §

Rspamd is a very complete mail filter. It is not only an anti-spam, but can handle greylisting, DKIM...

https://rspamd.com/

It is also very fast and efficient. If you want to use it, you should read it's official documentation. For now, let's see how to use it with OpenBSD's smtpd as an antispam and for DKIM.

Install rspamd §

# pkg_add rspamd redis opensmtpd-filter-rspamd
# rcctl enable redis rspamd
# rcctl start redis rspamd

Add rspamd to /etc/mail/smtpd.conf §

Just add a new filter named "filter-rspamd" and use it for incoming messages.

filter rspamd proc-exec "filter-rspamd"

# filtre en reception
listen on all tls pki athome.tld \
    filter { rspamd }

DKIM with rspamd §

Since rspamd can handle DKIM signatures, you won't have to configure dkimproxy or another tool.

Create keys as described before and make sure they belong to _rspamd group.

# chown -R _rspamd:_rspamd /etc/dkim/

Remember to edit your DNS zone 😉.

Now create /etc/rspamd/local.d/dkim_signing.conf:

# If true, username does not need to contain matching domain
allow_username_mismatch = true;

path = "/etc/dkim/private.key";
selector = "dkimpubkey";

Then add a few lines in /etc/mail/smtpd.conf to sign outgoing messages:

filter rspamd proc-exec "filter-rspamd"
# Send and DKIM sign with rspamd
listen on all port submission tls-require pki athome.tld auth \
    filter { rspamd }

Greylisting §

Rspamd does greylisting by default. If you want to still use spamd instead, disable rspamd greylisting in /etc/rspamd/local.d/actions.conf:

greylist = none;

And /etc/rspamd/local.d/greylist.conf:

enabled = false;

Spamtraps §

You can use a spamtrap (like what spamd does) using the following lines in /etc/rspamd/local.d/spamtrap.conf:

action = "no action";
learn_spam = true;
map = file://$LOCAL_CONFDIR/maps.d/spamtrap.map;

enabled = true;

Then fill /etc/rspamd/maps.d/spamtrap.map with regular expressions of fake trap mail addresses:

/^trap@athome.tld$/
/^fake@athome.tld$/

Blacklists §

To do so, use multimap module.

https://rspamd.com/doc/modules/multimap.html

rspamd WebUI §

Configure admin following these instructions:

https://rspamd.com/doc/quickstart.html#setting-the-controller-password

Then, dig a SSH tunnel from your computer and open in a browser http://localhost:9999.

ssh -N -L 9999:127.0.0.1:11334 sshuser@athome.tld

Enjoy wonderfull charts 😄

Manage spams with dovecot §

You might want to help the anti-spam to learn if it missed spams or got wrong for some messages.

If you use dovecot and a mail client with IMAP protocol, you can move spams in "Junk" folder so the anti-spam recognize them in the future. On the contrary, moving a mail from "Junk" to "INBOX" folder indicates it is legitimate.

No matter if you use rspamd or spammassassin, we explain for both. 3 steps are necessary:

It is described in dovecot documentation (you should read 😉)

https://wiki2.dovecot.org/HowTo/AntispamWithSieve

First, let's install dovecot pigeonhole plugin:

# pkg_add dovecot-pigeonhole

Add imap_sieve in plugins list to enable it at the end of /etc/dovecot/local.conf:

protocol imap {
    mail_plugins = $mail_plugins imap_sieve
}

Then, still in dovecot's local.conf, configure sieve plugin so it run scripts according to where messages are moved.

plugin {
    sieve_plugins = sieve_imapsieve sieve_extprograms

    # When a message is moved in Junk folder; report as spam
    imapsieve_mailbox1_name = Junk
    imapsieve_mailbox1_causes = COPY
    imapsieve_mailbox1_before = file:/usr/local/lib/dovecot/sieve/report-spam.sieve

    # When a mail is removed from Junk folder: it is nor a spam 
    imapsieve_mailbox2_name = *
    imapsieve_mailbox2_from = Junk
    imapsieve_mailbox2_causes = COPY
    imapsieve_mailbox2_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve

    sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve

    sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
}

Now create two files in the directory /usr/local/lib/dovecot/sieve/.

The first is report-spam.sieve:

require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];

if environment :matches "imap.user" "*" {
  set "username" "${1}";
}

pipe :copy "learn-spam.sh" [ "${username}" ];

The second is report-ham.sieve:

require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];

if environment :matches "imap.mailbox" "*" {
  set "mailbox" "${1}";
}

if string "${mailbox}" "Trash" {
  stop;
}

if environment :matches "imap.user" "*" {
  set "username" "${1}";
}

pipe :copy "learn-ham.sh" [ "${username}" ];

Now reload dovecot and compile the above scripts:

# rcctl restart dovecot
# sievec /usr/local/lib/dovecot/sieve/report-spam.sieve
# sievec /usr/local/lib/dovecot/sieve/report-ham.sieve

As you may have noticed, the above files call learn-spam.sh and learn-ham.sh. Thoses scripts will be different wether you use rspamd or spammassassin since they don't learn spams/hams with the same command. Read the next part 😉.

Learning scripts for spamassassin §

Scripts below are stored in /usr/local/lib/dovecot/sieve/.

#!/bin/sh
#learn-spam.sh
/usr/local/bin/spamc -u ${1} -L spam -C report

#!/bin/sh
# learn-ham.sh
/usr/local/bin/spamc -u ${1} -L ham -C report

Learnin scripts for rspamd §

Scripts below are still in /usr/local/lib/dovecot/sieve/.

#!/bin/sh
#learn-spam.sh
exec /usr/local/bin/rspamc -d "${1}" learn_spam

#!/bin/sh
# learn-ham.sh
exec /usr/local/bin/rspamc -d "${1}" learn_ham

Scripts for both §

Scripts must be executables:

# chmod +x /usr/local/lib/dovecot/sieve/learn-*.sh

At last, reload dovecot:

# rcctl reload dovecot

Here you go, now your anti-spam will learn when you'll move messages in approriate folders.

Learn more with dovecot official documentation.

https://wiki2.dovecot.org/HowTo

Fight against spams before receiving mails: OpenBSD's spamd §

You can sort incoming spams into a Junk folder. But imagine you can annoy spammers 😁? That's what spamd can do, faking being a mail server and holding spammers so they can't deliver spams while it's talking to them veeery slowly.

How does spamd work ? §

You can let somewhere on the web a fake mail address: a spamtrap. Everyone sending a message to this address will be blacklisted and trapped.

In the meantime, new senders are greylisted. They're told to try again later. Every proper email server will try again. Spammers won't, so you don't get the spam. Others who try again correctly are put on a whitelist.

There is also a blacklist-only mode, trapping IPs from lists you already have.

However, this means senders do things correctly. That's is sadly not always true for commercial senders. This said, it is easy to put a server on a whitelist.

To keep track of good and bad senders, the command spamd-setup should be run periodically.

Everything is very well explained in spamd(8) manpage. You must read it since the following are just advices, not a complete howto.

https://man.openbsd.org/spamd

Set spamd up §

# rcctl enable spamd
# rcctl set spamd flags "-v -G 25:4:864"
# rcctl start spamd

Also enable spamlogd to keep track of servers already whitelisted and to whitelist servers you write to.

# rcctl enable spamlogd
# rcctl start spamlogd

spamd is running in front of smtpd (like a shield, or a janitor 😉). To do so, edit packet-filter configuration: spamd should redirect mails to smtpd if it is not a spam.

Add in /etc/pf.conf:

table <spamd-white> persist
table <nospamd> persist file "/etc/mail/nospamd"
pass in on egress proto tcp to any port smtp divert-to 127.0.0.1 port spamd
pass in on egress proto tcp from <nospamd> to any port smtp
pass in log on egress proto tcp from <spamd-white> to any port smtp

On line order:

Remember to reload pf:

# pfctl -ef /etc/pf.conf

It is necessary to reload spamd blacklist in pf. Edit root's crontab with "# crontab -e" then uncomment the line below:

~  *  *  *  *  /usr/libexec/spamd-setup

With ~, spamd-setup is run at a random schedule 😉.

Spamtrap with spamd §

Let somewhere on the web fake email addresses: spamtraps. Everyone writing to these addresses is a spammer and will be trapped. Of course, those addresses don't really exist!

To keep these spamtraps for bots only, you can add such html snippets on your website so they are invisible for human eyes:

<a href="mailto:trap@athome.tld"></a>

You can copy it on public pastebins too.

To teach spamd what are spamtraps, use:

# spamdb -T -a 'trap@athome.tld'

Use pre-existing black/white lists §

Some nice people gather IP lists and let everyone download them. To use such lists, edit /etc/mail/spamd.conf.

As example, the default configuration uses nixspam's list:

http://www.heise.de/ix/nixspam

Add in /etc/mail/spamd.conf the following lines:

all:\
        :nixspam

# Nixspam recent sources list.
# Mirrored from http://www.heise.de/ix/nixspam
nixspam:\
        :black:\
        :msg="Your address %A is in the nixspam list\n\
        See http://www.heise.de/ix/nixspam/dnsbl_en/ for details":\
        :method=http:\
        :file=www.openbsd.org/spamd/nixspam.gz

At the beginning, you can read all. Then are written lists you want to use and configured below, separated with ":".

Use an URL to download the list with spamd-setup automatically or a file already on your server.

bsdly's blacklist §

Peter N.M.Hansteen publish a blacklist, gathered with its own spamtraps.

http://www.bsdly.net/

Avoid to download it too often or you'll be put on a blacklist. Be kind with Peter's bandwidth 😉.

To get the list every hour past 20 minutes:

20 * * * *   /usr/local/sbin/bsdly-spamd

Here is bsdly-spamd script you should copy and make executable:

#!/bin/sh
# update bsdly.net traplist into DST
URL="https://www.bsdly.net/~peter/bsdly.net.traplist"
# alternative URL
#URL="https://home.nuug.no/~peter/bsdly.net.traplist"
DST=/var/db/bsdly.traplist

ftp -o "${DST}" "${URL}"

Then add the new list in /etc/mail/spamd.conf so spamd uses /var/db/bsdly.traplist file:

all:\
      :nixspam:bsdlyblack:

nixspam:\
      :black:\
      :msg="Your address %A is in the nixspam list\n\
      See http://www.heise.de/ix/nixspam/dnsbl_en/ for details":\
      :method=https:\
      :file=www.openbsd.org/spamd/nixspam.gz

bsdlyblack:\
      :black:\
      :msg="SPAM.  Your address %A has sent spam within the last 24 hours.  See http://www.bsdly.net/~peter/traplist.shtml for details.":\
      :method=file:\
      :file=/var/db/bsdly.traplist

uceprotect black and whitelists §

Below we describe how to periodically download lists provided by uceprotect. We will use rsync to reduce bandwidth usage.

http://www.uceprotect.net/en/index.php

Add a new cronjob to call a script downloading lists. Not too often or you'll be blacklisted.

10 * * * *     /usr/local/sbin/uceprotect-spamd 

/usr/local/sbin/uceprotect-spamd is the script below:

#!/bin/sh

RSYNC="/usr/bin/openrsync -a"
URLS="rsync-mirrors.uceprotect.net::RBLDNSD-ALL/dnsbl-1.uceprotect.net
rsync-mirrors.uceprotect.net::RBLDNSD-ALL/dnsbl-2.uceprotect.net
rsync-mirrors.uceprotect.net::RBLDNSD-ALL/ips.whitelisted.org"
OUT="/var/db/RBLDNSD-ALL/"
mkdir -p "${OUT}"

for URL in ${URLS}; do
      ${RSYNC} "${URL}" "${OUT}"
done

Load the lists in /etc/mail/spamd.conf. There are white and blacklists.

all:\
      :nixspam:bgp-spamd:bsdlyblack:\
      rbldnsd-1:rbldnsd-2:rbldnsd-white:

...

rbldnsd-1:\
      :black:\
      :msg="Your address %A is listed on UCEPROTECT-Level 1\n \
      see http://www.uceprotect.net/en":\
      :method=file:\
      :file=/var/db/RBLDNSD-ALL/dnsbl-1.uceprotect.net

rbldnsd-2:\
      :black:\
      :msg="Your address %A is listed on UCEPROTECT-Level 2\n \
      see http://www.uceprotect.net/en":\
      :method=file:\
      :file=/var/db/RBLDNSD-ALL/dnsbl-2.uceprotect.net

rbldnsd-white:\
      :white:\
      :method=file:\
      :file=/var/db/RBLDNSD-ALL/ips.whitelisted.org

Do not hesitate do donate to uceprotect.net for all their work 😄.

Common issues with greylisting §

Some big mail provider uses multiple servers to send mails. If you do greylisting, the IP trying to send an email from a provider might change between two delivery attemps. This means spamd can't recognize a legitimate server, never whitelist the IP and you never get the mail.

However, you can whitelists those domains if you want.

Create a file containing domains not to greylist: /etc/mail/nospamd_domains_list.txt.

gmail.com
hotmail.com
facebookmail.com
apple.com
microsoft.com
lists.openbsd.org
linkedin.com
freebsd.org
twitter.com
amazon.com
yahoo.com
yahoo.fr
live.fr
mail-out.ovh.net
mxb.ovh.net
gandi.net
laposte.net
protonmail.com

Add more domains if you need to.

Now create a script to get sending IP of above domains: /usr/local/sbin/generate-nospamd:

#!/bin/sh
# /usr/local/sbin/generate-nospamd

DOMAINS=/etc/mail/nospamd_domains_list.txt
WHITELIST=/etc/mail/nospamd

echo "#$(date)" > "$WHITELIST"
smtpctl spf walk < "${DOMAINS}" >> "$WHITELIST"
exit 0

Call this script daily with /etc/daily.local and reload nospamd table:

# /etc/daily.local
/usr/local/sbin/generate-nospamd
pfctl -t nospamd -T replace -f /etc/mail/nospamd

Blacklist only mode for spamd §

Read "man spamd" at section "BLACKLIST-ONLY MODE" so you can trap spammers from blacklists files without greylisting.

See spamd activity §

Run "spamdb" to see what's happening:

# spamdb
WHITE|62.4.1.33|||1462699174|1462699174|1465809574|1|0
GREY|182.70.43.24|abts-mum-dynamic-024.43.70.182.airtelbroadband.in|<Estella32@thunderguy.co.uk>|<toto@athome.tld>|1473409924|1473424324|1473424324|1|0
GREY|14.183.132.63|static.vnpt.vn|<Abby5@toddelliott.com>|<kiki@athome.tld>|1473410586|1473424986|1473424986|1|0

Read spamdb(8) manpage to learn what it means.

http://man.openbsd.org/spamdb

To translate times in human-readable format, run date with -r flag:

$ date -r 1462699174
    Sun May  8 11:19:34 CEST 2016

You can put on whitelist an IP with spamdb -a:

# spamdb -a "62.4.1.37"

Share spamd data between secondary servers §

Remember to keep spamd databases synchroniszd between your server and backups (secondary MX). Read about -Y and -y flags in spamd manpage.

https://man.openbsd.org/spamd#Y

Check everything works well §

To make sure your mail server works as expected, you can send an email to friends. however, this may be an issue since:

Luckily there are robots that can automatically answer. As example:

Or see the following websites:

http://www.mail-tester.com

https://dkimvalidator.com/results

https://mxtoolbox.com/

What if smtp server doesn't work as expected? §

You should look at smtpctl command to get details about what's happening.

As example, to have generic status:

# smtpctl show stats
control.session=1
mda.envelope=0
mda.pending=0
mda.running=0
mda.user=0
mta.connector=1
mta.domain=1
mta.envelope=1
mta.host=2
mta.relay=1
mta.route=1
mta.session=1
mta.source=1
mta.task=1
mta.task.running=0
queue.evpcache.load.hit=1675
queue.evpcache.size=2
queue.evpcache.update.hit=6
scheduler.delivery.ok=826
scheduler.delivery.tempfail=6
scheduler.envelope=2
scheduler.envelope.incoming=0
scheduler.envelope.inflight=1
scheduler.ramqueue.envelope=2
scheduler.ramqueue.message=2
scheduler.ramqueue.update=0
smtp.session=0
smtp.session.inet4=787
smtp.session.local=31
smtp.tls=0
uptime=777861
uptime.human=9d4m21s

To see awaiting envelopes:

# smtpctl show queue
1101f6e60c169eac|inet4|mta||0100015b3a046476-f7d7955a-5842-49af-927c-4fa677f311c6-000000@bounces.duolingo.com|deux@domaine.eu|deux@domaine.eu|1491327053|1491672653|0|5|pending|3154|Network error on destination MXs
1a2123e1c2b3e462|inet4|mta||gitlab@framasoft.org|deux+framagit@domaine.eu|deux+framagit@domaine.eu|1491333449|1491679049|1491333849|1|inflight|50|Network error on destination MXs

To see in real time what's happening:

# smtpctl monitor

And of course; read man smtpctl 😉.


Table of contents

Donate