OpenSMTPD and Dovecot under OpenBSD with MySQL support and SPAMD

This article is the continuation of my previous tutorial OpenSMTPD under OpenBSD with SSL/VirtualUsers/Dovecot. We'll use the same configuration and add some features so we can :

  • Use our domains, aliases, virtual users with a MySQL database (MariaDB under OpenBSD).
  • Deploy SPAMD with OpenSMTPD for a strong antispam solution.

Jump in!

OpenBSD

Integration of the MySQL database support :

We'll use the OpenBSD package system, check your /etc/pkg.conf file.

Setup of the MySQL support for OpenSMTPD & Dovecot :

# pkg_add opensmtpd-extras-mysql 
# pkg_add dovecot-mysql

We create our SQL database named « smtpd » :

# mysql -uroot -p -e 'create database smtpd'

We create our SQL user « opensmtpd » we give him the privileges on our SQL database and we set its password :

# mysql -uroot -p -e "GRANT ALL ON smtpd.* to 'opensmtpd'@'127.0.0.1' IDENTIFIED BY 'opensmtpdpass'"

We create the structure of our SQL database:

# vi smtpd.sql

DROP TABLE IF EXISTS valias;
DROP TABLE IF EXISTS vdomains;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS userinfo;

CREATE TABLE IF NOT EXISTS valias (
alias_id INT(8) NOT NULL AUTO_INCREMENT,
addr varchar(42) NOT NULL,
alias varchar(42) NOT NULL,
PRIMARY KEY (alias_id)
) ENGINE=InnoDB ;

CREATE TABLE IF NOT EXISTS vdomains (
domain_id INT(11) NOT NULL AUTO_INCREMENT,
domain VARCHAR(42) NOT NULL,
PRIMARY KEY (domain_id)
) ENGINE=InnoDB ;

CREATE TABLE IF NOT EXISTS userinfo (
user_id INT(11) NOT NULL AUTO_INCREMENT,
user VARCHAR(42) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
uid INT(42) DEFAULT 106,
gid INT(42) DEFAULT 107,
maildir VARCHAR(255) DEFAULT '/var/empty',
PRIMARY KEY (user_id)
) ENGINE=InnoDB ;

CREATE TABLE IF NOT EXISTS users (
user_id INT(8) NOT NULL AUTO_INCREMENT,
username TEXT NOT NULL,
domain TEXT NOT NULL,
mailbox TEXT NOT NULL,
password TEXT NULL,
home TEXT NOT NULL,
uid INTEGER NOT NULL,
gid INTEGER NOT NULL,
PRIMARY KEY (user_id)
) ENGINE=InnoDB ;

You can check this page for more informations.

First, we generate our password with Blowfish (remember it's OpenBSD !) for our users :

# smtpctl encrypt my-personnal-password 
$2b$10$nNLSbqFDLlLx9JRyxvhjI.FVzTgEEqVyEjYHf108S7cA49cqED6da

We create our tables and we include our datas :

# vi users.sql

DELETE FROM valias;
DELETE FROM vdomains;
DELETE FROM userinfo;

INSERT INTO valias VALUES('','lina@cagedmonster.net', 'lina');

INSERT INTO vdomains VALUES('','cagedmonster.net');

INSERT INTO userinfo 
VALUES('','lina','$2b$10$JiGuncDXUcT2/hS/7Ty4KubsbuCjO0zsRhOQ7isFPCEcSLnIb.JMi',default,default,default);

We push everything to our database :

# mysql -uroot -p smtpd < smtpd.sql 
# mysql -uroot -p smtpd < users.sql 
# mysql -uroot -p -e 'FLUSH PRIVILEGES'

Time to configure OpenSMTPD :

# vi /etc/mail/smtpd.conf 

pki mail.cagedmonster.net certificate "/etc/ssl/server.crt" 
pki mail.cagedmonster.net key "/etc/ssl/private/server.key" 

table vdomains mysql:/etc/mail/mysql.conf
table vusers mysql:/etc/mail/mysql.conf
table userinfo mysql:/etc/mail/mysql.conf
table credentials mysql:/etc/mail/mysql.conf

listen on lo0 listen on egress port 25 tls pki mail.cagedmonster.net 
listen on egress port 587 tls-require pki mail.cagedmonster.net auth <credentials>

accept from any for domain <vdomains> virtual <vusers> userbase <userinfo> deliver to lmtp "/var/dovecot/lmtp" 
accept from local for any relay

The configuration is similar to the file of our previous tutorial but with a difference : we included the link of the mysql tables.

We create our mysql.conf file and configure it :

# vi /etc/mail/mysql.conf 

host 127.0.0.1
username opensmtpd
password opensmtpdpass
database smtpd

query_credentials SELECT user,password FROM userinfo WHERE user=?; 

query_domain SELECT domain FROM vdomains WHERE domain=? LIMIT 1; 

query_userinfo SELECT uid,gid,maildir FROM userinfo where user=?; 

query_alias SELECT alias FROM valias WHERE addr=?;

Configuration of Dovecot.conf :

# vi /etc/dovecot.conf 

ssl = yes 
!include conf.d/auth-sql.conf.ext 
ssl_cert = </etc/ssl/server.crt 
ssl_key = </etc/ssl/private/server.key 
userdb { 
        driver = static 
        args = uid=virtmail gid=virtmail home=/var/virtmail/%u 
       } 

mail_location = maildir:/var/virtmail/%u 
mail_uid = virtmail 
mail_gid = virtmail 

passdb { 
        driver = sql 
        args = /etc/dovecot/dovecot-sql.conf.ext 
       } 

protocols = lmtp imap 
base_dir = /var/dovecot/ 
postmaster_address = postmaster@cagedmonster.net

Configuration of auth-sql.conf.ext

# vi /etc/dovecot/conf.d/auth-sql.conf.ext  

passdb { 
        driver = sql 
        args = /etc/dovecot/dovecot-sql.conf.ext 
        } 
userdb { 
        driver = sql 
        args = /etc/dovecot/dovecot-sql.conf.ext 
        }

Configuration of dovecot-sql.conf.ext

# vi /etc/dovecot/dovecot-sql.conf.ext 

driver = mysql 

connect = host=127.0.0.1 dbname=smtpd user=opensmtpd password=opensmtpdpass

default_pass_scheme = BLF-CRYPT 

password_query = \ 
  SELECT user, password \ 
  FROM userinfo WHERE user = '%u'

The password is set with BLOWFISH, we need to specify it.

Restart our services :

# rcctl restart smtpd dovecot

Time to test if everything is working as we want :

We send a mail...

$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mail.cagedmonster.net ESMTP OpenSMTPD
HELO FRIEND
250 mail.cagedmonster.net Hello FRIEND [127.0.0.1], pleased to meet you
MAIL FROM:<test@tutorialoflinasovereign.com>
250 2.0.0: Ok
RCPT TO:<lina@cagedmonster.net>
250 2.1.5 Destination address valid: Recipient ok
DATA
354 Enter mail, end with "." on a line by itself
It's a test and I hope it will work !
.
250 2.0.0: 408391e3 Message accepted for delivery
QUIT
221 2.0.0: Bye
Connection closed by foreign host.

We check the logs :

$ cat /var/log/maillog
Aug 20 18:25:41 mail smtpd[65810]: e31eedb08a3c2f01 smtp event=message msgid=408391e3 from=<test@tutorialoflinasovereign.com> to=<lina@cagedmonster.net> size=241 ndest=1 proto=SMTP
Aug 20 18:25:41 mail smtpd[65810]: 0000000000000000 mda event=delivery evpid=408391e380f6c1e2 from=<test@tutorialoflinasovereign.com> to=<lina@cagedmonster.net> user=lina method=lmtp delay=13s result=Ok stat=Delivered

See if our mail is "physicaly" here :

# cat /var/virtmail/lina/new/1471710341.M714123P83824.mail.cagedmonster.net,S\=536,W\=548\:2,

Return-Path: <test@tutorialoflinasovereign.com>
Delivered-To: lina
Received: from mail.cagedmonster.net
by mail.cagedmonster.net (Dovecot) with LMTP id 9Dn0KYWEuFdwRwEA8Z+lxA
for <lina>; Sat, 20 Aug 2016 18:25:41 +0200
Return-Path: test@tutorialoflinasovereign.com
Delivered-To: lina@cagedmonster.net
Received: from FRIEND (localhost [127.0.0.1])
by mail.cagedmonster.net (OpenSMTPD) with SMTP id 408391e3
for <lina@cagedmonster.net>;
Sat, 20 Aug 2016 18:25:30 +0200 (CEST)
It's a test and I hope it will work ! 

Next : SPAMD !

OpenSMTPD & SPAMD :

SPAMD is a service simulating a fake SMTP server and relying on strict compliance with RFC to determine whether the server delivering a mail is a spammer or not.
Messages pass through PackFilter to SPAMD and are redirected if the SMTP server in front is valid. To do this, SPAMD is based on three (3) types of lists:

  1. Blacklist: Servers recognized as SPAMMERS, the received messages will be delayed as long as possible in order to handicap the pest in terms of time and resources. At the end of the allotted time, a message will be sent and the message will never be transmitted to our real mail server.
  2. Greylist: In this case, SPAMD has not yet determined whether the SMTP server was a spammer or not. It will therefore rely on the workings of the RFC to send a temporary error to the remote server. If it is a genuine SMTP server, the RFC wants a new attempt to be made within a fairly short time (between about twenty minutes and four hours) until the message is delivered until a given time (about 5 days in general). If the RFC is respected, SPAMD will put the server on whitelist.
  3. Whitelist: The server is recognized as authentic and reliable, it does not pass through SPAMD and is relayed directly to our OPENSMTPD server.

Configuration of SPAMD :

Enable SPAMD & SPAMLOGD at system startup :

 # rcctl enable spamd spamlogd

Configuration of SPAMD flags :

SPAMD offers a large number of features, we will use a few to be as penalizing as possible for SPAMMERS :

 # rcctl set spamd flags -4 -G25:4:864 -h cagedmonster.net -l127.0.0.1 -S60 -s1 -v -w1
  • -4 Basic disqualification: "451 Temporary failure, please try again later. "
  • -G25: 4: 744: This is the parameter to test a server in Greylist. In this case, the server has between 25 minutes and 4 hours to make a new attempt to send to Whitelist. Once in Whitelist the host will be kept for a period of 744 hours, ie a month of 31 days.
  • -h: Domains valid, they will not be tested
  • -l127.0.0.1: SPAMD only listens to local
  • -S60: will wait for SMTP servers in Blacklist & Greylist for 60 seconds. This parameter is configurable between 1 and 90 seconds
  • -s1: Sets the number of characters accepted by SPAMD per seconds.
  • -v: Allows us to log what the remote server will send us, always informative
  • -w1: the ultimate option for slowing down the remote host. Slower the window of sending data when connecting, causing spammers to lose a considerable amount of time.

PS: It is also possible to add the -n OpenSMTPD option to change the default header: 220 cagedmonster.net ESMTP spamd IP-based SPAM blocker; Mon Aug 22 11:29:00 2016

Configuration of PacketFilter :

# vi /etc/pf.conf

table <spamd-white> persist

pass in log on egress inet proto tcp from <spamd-white> to egress port smtp keep state rdr-to lo0
pass in log on egress inet proto tcp from !<spamd-white> to egress port smtp keep state rdr-to lo0 port spamd

# pfctl -f /etc/pf.conf

If the remote host is in we immediately redirect to our SMTP server, otherwise we pass it through the SPAMD filter. I like to use the expression "egress" because it refers directly to the interfaces managing the default routes.

Configuration of SPAMD :

# vi /etc/spamd.conf

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

# crontab -e

0       *       *       *       *       sleep $((RANDOM \% 1800)) && /usr/libexec/spamd-setup

Start SPAMD & SPAMLOGD :

# rcctl start spamd spamlogd

Need help ?