OpenBSD - OpenSMTPD
Page content
Running a Mailserver on OpenBSD …
Source
Requirements
- OpenBSD VM
- Public IP & FQDN
- no Portfilter from Hoster
- root permission
Packages
pkg_add opensmtpd-extras opensmtpd-filter-rspamd dovecot dovecot-pigeonhole redis rspamd-- opensmtpd-filter-senderscore
FQDN
export host="hostname"
export domain="domain.tld"
export fqdn="${host}.${domain}"
httpd.conf
f="/etc/httpd.conf"; test -f ${f} && cp ${f} "${f}-$(date +'%s')"
cat << EOF > ${f}
# added $(date)
server "${fqdn}" {
listen on * port 80
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
EOF
chown root:wheel ${f}; chmod 644 ${f}
pf.conf
allow Certain Ports for Any
cat << EOF >> /etc/pf.conf
# Full Access for Mail/Web, added $(date)
pass in log quick proto tcp from any to (self) port { 25 80 587 993 }
EOF
pfctl -nvf /etc/pf.conf && pfctl -f /etc/pf.conf
acme-client.conf
f="/etc/acme-client.conf"; test -f ${f} && cp ${f} "${f}-$(date +'%s')"
# Copy Example
cp /etc/examples/acme-client.conf /etc/
# Kill Last 10 Lines
sed -i -e :a -e '$d;N;2,6ba' -e 'P;D' /etc/acme-client.conf
# Append MyStuff
cat << EOF >> /etc/acme-client.conf
# added $(date)
domain ${fqdn} {
domain key "/etc/ssl/private/${fqdn}.key"
domain full chain certificate "/etc/ssl/${fqdn}.fullchain.pem"
sign with letsencrypt
}
EOF
chown root:wheel ${f}; chmod 644 ${f}
# Start Web, get Service
rcctl -f start httpd
acme-client -v ${fqdn}
rcctl stop httpd
Credentials
touch /etc/mail/credentials
chmod 0440 /etc/mail/credentials
chown _smtpd:_dovecot /etc/mail/credentials
Virtual Home
mkdir /var/vmail
useradd -c "Virtual Users Mail Account" -d /var/vmail -s /sbin/nologin -u 2000 -g =uid -L staff vmail
chown vmail:vmail /var/vmail
Create User
export user="user"; export pass=$(smtpctl encrypt PASSWORD)
echo "${user}@${domain}:${pass}:vmail:2000:2000:\
/var/vmail/${domain}/${user}::userdb_mail=maildir:\
/var/vmail/${domain}/${user}" \
>> /etc/mail/credentials
add Domains
f="/etc/mail/vdomains"; test -f ${f} && cp ${f} "${f}-$(date +'%s')"
cat << EOF > ${f}
${domain}
yourdomain.de
someotherdomains.de
EOF
chown _smtpd:wheel ${f}; chmod 640 ${f}
Virtual Users - Main Domain
f="/etc/mail/vusers"; test -f ${f} && cp ${f} "${f}-$(date +'%s')"
cat << EOF >> ${f}
#
# Domain: ${domain}
#
abuse@${domain}: root@${domain}
hostmaster@${domain}: root@${domain}
postmaster@${domain}: root@${domain}
webmaster@${domain}: root@${domain}
# vmails
root@${domain}: vmail
catchall@${domain}: vmail
# catchall
@${domain}: catchall@${domain}
EOF
chown _smtpd:wheel ${f}; chmod 640 ${f}
Virtual Users - additional Domains
echo -n "Name of additional Domain? "; read domain
echo "adding Domain: ${domain}
cat << EOF >> vusers
#
# Domain: ${domain}, added $(date)
#
abuse@${domain}: root@${domain}
hostmaster@${domain}: root@${domain}
postmaster@${domain}: root@${domain}
webmaster@${domain}: root@${domain}
# vmails
root@${domain}: vmail
catchall@${domain}: vmail
# catchall
@${domain}: catchall@${domain}
EOF
chown _smtpd:wheel ${f}; chmod 640 ${f}
secrets
if we wanna relay via some “relayhost” and have to authenticate there ..
echo "label username:password" >> secrets
chown root:_smtpd secrets
chmod 640 secrets
smtpd.conf
f="/etc/mail/smtpd.conf"; test -f ${f} && cp ${f} "${f}-$(date +'%s')"
cat << EOF > ${f}
# This is the smtpd server system-wide configuration file.
# installed: $(date)
pki "${fqdn}" cert "/etc/ssl/${fqdn}.fullchain.pem"
pki "${fqdn}" key "/etc/ssl/private/${fqdn}.key"
table aliases file:/etc/mail/aliases # ...
table secrets file:/etc/mail/secrets # For Relaying
table credentials passwd:/etc/mail/credentials # Users for Rainloop
table vdomains file:/etc/mail/vdomains # List all Domains
table vusers file:/etc/mail/vusers # User / Alias Mapping
# Filter potential spam with rspamd
filter "rspamd" proc-exec "/usr/local/libexec/smtpd/filter-rspamd"
# DNS BL
filter dnsbl proc-exec "filter-dnsbl -mv zen.spamhaus.org"
# Misc Filters
filter check_dyndns phase connect match rdns regex { '.*\.dyn\..*', '.*\.dsl\..*' } junk
filter check_rdns phase connect match !rdns disconnect "550 no rDNS available"
filter check_fcrdns phase connect match !fcrdns disconnect "550 no FCrDNS available"
filter "senderscore" proc-exec "/usr/local/libexec/smtpd/filter-senderscore -blockBelow 10 -junkBelow 70 -slowFactor 1000"
# To accept external mail, replace with: listen on all
listen on lo0
listen on egress tls pki "${fqdn}" hostname "${fqdn}" filter { check_dyndns, check_rdns, check_fcrdns, dnsbl, rspamd, senderscore }
listen on egress port 587 tls-require pki "${fqdn}" hostname "${fqdn}" auth <credentials> filter { check_dyndns, check_rdns, check_fcrdns, dnsbl, rspamd, senderscore }
# Where to store incoming emails based on the target user.
action "local_mail" mbox alias <aliases>
action "domain_vmail" maildir "/var/vmail/%{dest.domain}/%{dest.user}" virtual <vusers>
# Relay mails when they come from authorized clients.
action "outbound" relay host smtp+tls://label@relay-host.net:587 auth <secrets>
# Block Bad Boys
match mail-from "@spammer.org" for any reject
match mail-from "@spammer.org" for domain { "mydomain.net" "mydomain2.net" } reject
match mail-from "info@spammer.org" for any reject
match mail-from "user1@domain1.net" for rcpt-to regex "mydomain.*.net" reject
# Next, we match incoming emails.
match from any for domain <vdomains> action "domain_vmail"
# 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 self 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"
EOF
chown _smtpd:wheel ${f}; chmod 640 ${f}
Enable and Start Services
smtpd -n
rcctl enable smtpd rspamd redis
rcctl restart smtpd rspamd redis
Dovecot
cat << EOF >> /etc/login.conf
dovecot:\
:openfiles-cur=1024:\
:openfiles-max=2048:\
:tc=daemon:
EOF
cap_mkdb /etc/login.conf
usermod -L dovecot _dovecot
may need a reboot?
local.conf
f="/etc/dovecot/local.conf"; test -f ${f} && cp ${f} "${f}-$(date +'%s')"
cat << EOF > ${f}
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/${fqdn}.fullchain.pem
ssl_key = </etc/ssl/private/${fqdn}.key
userdb {
args = username_format=%u /etc/mail/credentials
driver = passwd-file
name =
}
protocol imap {
mail_plugins = " imap_sieve"
}
EOF
chown root:wheel ${f}; chmod 640 ${f}
fix
sed -i.bak "s#^ssl_cert = </etc/ssl/dovecotcert.pem#ssl_cert = </etc/ssl/${fqdn}.fullchain.pem#" /etc/dovecot/conf.d/10-ssl.conf
sed -i.bak "s#^ssl_key = </etc/ssl/private/dovecot.pem#ssl_key = </etc/ssl/private/${fqdn}.key#" /etc/dovecot/conf.d/10-ssl.conf
sieve scripts
cd /usr/local/lib/dovecot/sieve
ftp https://blog.stoege.net/scripts/sieve-scripts.tar.gz
tar xzf sieve-scripts.tar.gz
rm sieve-scripts.tar.gz
sievec report-ham.sieve
sievec report-spam.sieve
chmod 0755 sa-learn-ham.sh
chmod 0755 sa-learn-spam.sh
Start Service
rcctl enable dovecot redis rspamd
rcctl restart dovecot redis rspamd
Check Login
doveadm user ${user}@${domain}
doveadm auth login ${user}@${domain}
rspamd
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.key
set SFP Record via DNS
your.domain. IN TXT "v=spf1 a ip4:your-public-ip mx ~all"
dkim
default._domainkey.your.domain. IN TXT "v=DKIM1;k=rsa;p=[…public key…]"
dmarc
_dmarc.example.com. IN TXT "v=DMARC1;p=none;pct=100;rua=mailto:postmaster@example.com"
xxx
f="/etc/rspamd/local.d/dkim_signing.conf"; test -f ${f} && cp ${f} "${f}-$(date +'%s')"
cat << EOF > ${f}
domain {
example.com {
path = "/etc/mail/dkim/private.key";
selector = "default";
}
}
EOF
chown root:wheel ${f}; chmod 640 ${f}
restart
rcctl enable redis rspamd
rcctl restart redis rspamd
Any Comments ?
sha256: 7060cbf5c4d145e055b7077f1b57fa5ad7f320fa040cdad189776da247ea523b