acme-client and the httpd server on OpenBSD)opensmtpd and dovecot)
example.com and local usersrspamd + Sieve rules to train the antispam)No fancy webmail.
This tutorial is heavily inspired by the vultr tutorial and an old article about let’s encrypt on openbsd.
This is very straightforward.
httpd has to be configured to point to the right directory for acme challenges for your domainsacme-client has to know your domainsThat’s it.
httpd.conf
# $OpenBSD: httpd.conf,v 1.20 2018/06/13 15:08:24 reyk Exp $
server "example.com" {
alias "www.example.com"
alias "mail.example.com"
alias "team.example.com"
listen on * port 80
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
location "/special/*" {
root "/htdocs/"
}
location * {
block return 302 "https://$HTTP_HOST$REQUEST_URI"
}
}
server "example.com" {
alias "www.example.com"
alias "mail.example.com"
alias "team.example.com"
listen on * tls port 443
root "/htdocs/example.com"
tls {
certificate "/etc/ssl/example.com.fullchain.pem"
key "/etc/ssl/private/example.com.key"
}
location "/pub/*" {
directory auto index
}
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
acme-client.conf
#
# $OpenBSD: acme-client.conf,v 1.2 2019/06/07 08:08:30 florian Exp $
#
authority letsencrypt {
api url "https://acme-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-privkey.pem"
}
authority letsencrypt-staging {
api url "https://acme-staging-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-staging-privkey.pem"
}
domain example.com {
alternative names { www.example.com mail.example.com team.example.com }
domain key "/etc/ssl/private/example.com.key"
domain full chain certificate "/etc/ssl/example.com.fullchain.pem"
sign with letsencrypt
}
In this example, I want example.com and three aliases. Here are the DNS records:
example.com IN 1200 A 10.0.0.1
www.example.com IN 1200 A 10.0.0.1
team.example.com IN 1200 A 10.0.0.1
mail.example.com IN 1200 A 10.0.0.1
example.com IN 1200 MX 10 mail
You can also put CNAME instead of A records for team and www.
You can have free domain names on netlib.re!
Lastly, we want to create acme and tls folders:
mkdir /var/www/acme
mkdir -p /etc/ssl/acme/private /etc/acme
chmod 0700 /etc/ssl/acme/private /etc/acmeThen we can restart httpd and launch acme-client:
rcctl restart httpd
acme-client example.com
# Second restart, now the web server has the right certificate.
rcctl restart httpdpkg_add opensmtpd-extras opensmtpd-filter-rspamd dovecot dovecot-pigeonhole rspamd redis
This tutorial uses an user vmail to handle all virtual users mails. vmail has the uid 2000, the gid 2000, and its home directory will be the root for virtual users maildirs: /var/vmail/.
touch /etc/mail/credentials
chmod 0440 /etc/mail/credentials
chown _smtpd:_dovecot /etc/mail/credentials
useradd -c "Virtual Users Mail Account" -d /var/vmail -s /sbin/nologin -u 2000 -g =uid -L staff vmail
mkdir /var/vmail
chown vmail:vmail /var/vmailIn our setup, mail users will be either system users or virtual ones. The list of virtual users and their credentials need to be configured: this tutorial use a simple file to that end.
Each client needs to be authenticated before sending emails. Passwords need to be processed before being put in a simple text file.
smtpctl encrypt PASSWORD
# Example: smtpctl encrypt chocolate
# $2b$09$/aUucPECRPTasFpNZcgpEe.0TTSiJ9UXqhF4uIzFvlLr220nBxuMqThis way, passwords can be put in the credentials file.
The /etc/mail/credentials file needs to be formatted, almost as /etc/passwd:
<email>:<password>:<user>:<uid>:<gid>:<maildir-path>::userdb_mail=maildir:<maildir-path>
Parameters user, uid and gid are related to the system user that will handle all virtual users mails: vmail. Also, maildir-path will be into the vmail home directory.
Here is an example for the user joe.satriani for the domain example.com.
joe.satriani@example.com:$2b$09$/aUucPECRPTasFpNZcgpEe.0TTSiJ9UXqhF4uIzFvlLr220nBxuMq:vmail:2000:2000:/var/vmail/example.com/joe.satriani::userdb_mail=maildir:/var/vmail/example.com/joe.satriani
/etc/mail/virtuals defines the valid email addresses (and aliases) for our default domain example.com.
abuse@example.com: joe.satriani@example.com
hostmaster@example.com: joe.satriani@example.com
postmaster@example.com: joe.satriani@example.com
webmaster@example.com: joe.satriani@example.com
joe.satriani@example.com: vmail
steve.vai@example.com: vmail
john.petrucci@example.com: vmail
Before presenting the configuration file, here is a quick overview of the smtpd configuration parameters.
pki indicates certificate and key paths for a domain.table references lists:
listen directive indicates:
pki to usefilter executes an application on a mail
action describes what to do with a message
match uses an action based on the processed message (origin, destination)
domain_mail action for emails for the domain example.comlocal_mail action for emails coming from local addressNext step: the configuration file. I allow a machine in my domain (192.168.0.200) to send emails with this mail server as a relay.
The configuration file I use.
# $OpenBSD: smtpd.conf,v 1.14 2019/11/26 20:14:38 gilles Exp $
# This is the smtpd server system-wide configuration file.
# See smtpd.conf(5) for more information.
pki "mail.example.com" cert "/etc/ssl/example.com.fullchain.pem"
pki "mail.example.com" key "/etc/ssl/private/example.com.key"
table aliases file:/etc/mail/aliases
table credentials passwd:/etc/mail/credentials
table virtuals file:/etc/mail/virtuals
# Filter potential spam with rspamd
filter "rspamd" proc-exec "/usr/local/libexec/smtpd/filter-rspamd"
# To accept external mail, replace with: listen on all
#
#listen on socket
listen on lo0
listen on "192.168.0.100" \
tls pki "mail.example.com" \
hostname "mail.example.com" filter "rspamd"
# Authorize people to send messages from our server
listen on "192.168.0.100" port submission \
tls-require pki "mail.example.com" \
hostname "mail.example.com" \
auth <credentials> filter "rspamd"
# Where to store incoming emails based on the target user.
action "local_mail" mbox alias <aliases>
action "domain_mail" \
maildir "/var/vmail/example.com/%{dest.user}" \
virtual <virtuals>
# Relay mails when they come from authorized clients.
action "outbound" relay
# Next, we match incoming emails.
# When the mail comes from any place for our domain, it triggers the "domain_mail" action.
match from any for domain "example.com" action "domain_mail"
# When the mail comes from and for a local user it triggers the "local_mail" action.
match from local for local action "local_mail"
# HEADS UP: Authorize forwarding emails for a local machine
match from src "192.168.0.200" for any action "outbound"
# HEADS UP: Authorize forwarding emails for a local machine
match from local for any action "outbound"
match auth from any for any action "outbound"The setup is simple: smtpd listens to local connections (on lo0) and on another interface (with the IP address 192.168.0.100). TLS is used along with the mail.example.com PKI for incoming connections (either for submissions or not). Incoming connections are filtered with rspamd and have mail.example.com as the provided host name. Submissions need to pass authentication based on the /etc/mail/credentials file set earlier.
This configuration allows any local or authenticated user to send mails to any domain. Besides the generic configuration in my setup, I authorized the forwarding of mails coming from a specific IP address in my local network (192.168.0.200).
# First, try the syntax.
doas smtpd -n
# Then run the service.
doas rcctl restart smtpdMails will be transfered in the /var/vmail/example.com/<username>/new directory.
Since dovecot opens a lot of files, it is preferable to create a login class for the service.
dovecot:\
:openfiles-cur=1024:\
:openfiles-max=2048:\
:tc=daemon:
# Ensure new login class is taken into account.
cap_mkdb /etc/login.conf
# Change the dovecot login class.
usermod -L dovecot _dovecotFile /etc/dovecot/local.conf
auth_mechanisms = plain
first_valid_uid = 2000
first_valid_gid = 2000
mail_location = maildir:/var/vmail/%d/%n
mail_plugin_dir = /usr/local/lib/dovecot
managesieve_notify_capability = mailto
managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext imapsieve vnd.dovecot.imapsieve
mbox_write_locks = fcntl
mmap_disable = yes
namespace inbox {
inbox = yes
location =
mailbox Archive {
auto = subscribe
special_use = \Archive
}
mailbox Drafts {
auto = subscribe
special_use = \Drafts
}
mailbox Junk {
auto = subscribe
special_use = \Junk
}
mailbox Sent {
auto = subscribe
special_use = \Sent
}
mailbox Trash {
auto = subscribe
special_use = \Trash
}
prefix =
}
passdb {
args = scheme=CRYPT username_format=%u /etc/mail/credentials
driver = passwd-file
name =
}
plugin {
imapsieve_mailbox1_before = file:/usr/local/lib/dovecot/sieve/report-spam.sieve
imapsieve_mailbox1_causes = COPY
imapsieve_mailbox1_name = Junk
imapsieve_mailbox2_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve
imapsieve_mailbox2_causes = COPY
imapsieve_mailbox2_from = Junk
imapsieve_mailbox2_name = *
sieve = file:~/sieve;active=~/.dovecot.sieve
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve
sieve_plugins = sieve_imapsieve sieve_extprograms
}
protocols = imap sieve
service imap-login {
inet_listener imaps {
port = 993
}
}
service managesieve-login {
inet_listener sieve {
port = 4190
}
inet_listener sieve_deprecated {
port = 2000
}
}
ssl_cert = </etc/ssl/example.com.fullchain.pem
ssl_key = </etc/ssl/private/example.com.key
userdb {
args = username_format=%u /etc/mail/credentials
driver = passwd-file
name =
}
protocol imap {
mail_plugins = " imap_sieve"
}
As of today (september 2020), there still is a bug in Dovecot: the ssl_cert and ssl_key settings do not get overridden in the local.conf file. We need to so we have to comment these parameters in the file /etc/dovecot/conf.d/10-ssl.conf.
To train rspamd, we need to provide sieve scripts. Training is done by moving emails into and out the junk folder. These files are located at /usr/local/lib/dovecot/sieve.
This archive contains the 4 files you need:
report-ham.sieve and report-spam.sievesa-learn-ham.sh and sa-learn-spam.shOnce untar in /usr/local/lib/dovecot/sieve:
sievec report-ham.sieve
sievec report-spam.sieve
chmod 0755 sa-learn-ham.sh
chmod 0755 sa-learn-spam.shThen we can start the dovecot daemon.
rcctl enable dovecot
rcctl start dovecotFinally, we can verify the setup by requesting informations about a client.
doveadm user joe.satriani@example.comAnd verify that the user can log in.
doveadm auth login joe.satriani@example.comFor rspamd, we need:
doas su
mkdir /etc/mail/dkim
cd /etc/mail/dkim
openssl genrsa -out private.key 1024
openssl rsa -in private.key -pubout -out public.key
chmod 0440 private.key
chown root:_rspamd private.keyOf course, you have to put your own public IPv4 (or IPv6) address.
example.com. IN TXT "v=spf1 a ip4:192.168.0.100 mx ~all"
default._domainkey.example.com. IN TXT "v=DKIM1;k=rsa;p=[…public key…]"
_dmarc.example.com. IN TXT "v=DMARC1;p=none;pct=100;rua=mailto:postmaster@example.com"
Then we need to create the /etc/rspamd/local.d/dkim_signing.conf file.
domain {
example.com {
path = "/etc/mail/dkim/private.key";
selector = "default";
}
}
Now, we have all the scripts, configuration files and services up and running except redis and rspamd.
doas rcctl enable redis rspamd
doas rcctl start redis rspamd