GoWiki
Index
Sections
Search
Login
openbsd
Mail Server Deprecated
# OpenBSD Mail Server **DEPRECATED** This is an old version of the OpenBSD mail server. ## TODO https://poolp.org/posts/2019-09-14/setting-up-a-mail-server-with-opensmtpd-dovecot-and-rspamd/ ## Intro How to setup an OpenBSD 6.4 Mail Server with: * OpenSMTPD (mta) * Spamd (tarpit, greylisting) * spampd (spamassassin) * DKIM Proxy, * Kopano (webapp, z-push) not included are IMAP, POP3, CalDav. Maybe they will be added later. * Optional: bgp-spamd (black and whitelists), ClamAV (in and out scanning) Note: This is mostly a copy/past guide. It is still recommended to read the man-pages to 'understand' why and how things work. ## Mail-Flow ### Incoming mail (blacklisted) ``` pf -> relay to spamd (tarpit) ``` ### Incoming mail (greylisted) ``` pf -> relay to spamd (greylist handling) ``` ### Incoming mail (whitelisted) ``` pf -> relay to opensmtpd on lo0 -> relay untagged mail to spampd (spamassassin) on port 10025 -> return to opensmtpd on lo0 port 10026 and tag it SPAM_IN -> deliver to mda (kopano-dagent) ``` ### Outgoing mail ``` kopano (kopano-spooler) -> opensmtpd on lo0 -> relay untagged mail to dkimproxy on port 10027 -> return to opensmtpd on lo0 port 10028 and tag it DKIM_OUT -> relay out -> pf ``` ## SpamAssassin Install: `pkg_add p5-Mail-SpamAssassin spampd` Boot startup: ``` rcctl enable spampd rcctl enable spamassassin rcctl set spampd flags "--port=10025 --relayhost=127.0.0.1:10026 --tagall --aw" ``` ### Config Change Rewrite: `sed -i 's/^# \(rewrite_header.*\)/\1/' /etc/mail/spamassassin/local.cf` Permissions: `chown -R _spampd /var/spool/spamassassin` Start the daemons: ``` rcctl start spampd rcctl start spamassassin ``` Create the daily update cronjob: `echo "0 3 * * * /usr/local/bin/sa-update >/dev/null 2>&1" >> /var/cron/tabs/root` ## DKIM Proxy Install: `pkg_add dkimproxy opendkim` Boot startup: `rcctl enable dkimproxy_out` ### Config Note: replace `example.com` with your domain. Edit config: ``` sed -i 's/^domain.*/domain example.com/' /etc/dkimproxy_out.conf sed -i 's|^keyfile.*|keyfile /var/dkimproxy/default.private|' /etc/dkimproxy_out.conf sed -i 's/^selector.*/selector default/' /etc/dkimproxy_out.conf sed -i 's/^signature dkim.*/signature dkim(c=relaxed,a=rsa-sha256)/' /etc/dkimproxy_out.conf ``` Generate signing-key: ``` mkdir /var/dkimproxy opendkim-genkey -d example.com -D /var/dkimproxy -b 4096 chgrp _dkimproxy /var/dkimproxy/* chmod 640 /var/dkimproxy/* ``` Put the contents of `/var/dkimproxy/default.txt` in your DNS-Zone file. Start dkimproxy: `rcctl start dkimproxy_out` ## smtpd Boot startup: ``` rcctl enable smtpd rcctl set smtpd flags -v ``` ### Virtual Domains This file holds all virtual domains: ``` cat /etc/mail/vdomains example.com example.org ``` ### Virtual Users Create the virtual system user: `useradd -d /var/empty -s /sbin/nologin vmail` This file holds the virtual user mappings: ``` # cat /etc/mail/vusers john@example.com vmail @example.com john@example.com doe@example.org vmail hostmaster@example.org doe@example.org ``` ### smtpd.conf Replace `example.com` with your domain in `/etc/mail/smtpd.conf`. ``` smtp max-message-size 20MB pki mail.example.com cert "/etc/ssl/example.com.fullchain.pem" pki mail.example.com key "/etc/ssl/private/example.com.key" smtp ciphers ECDHE-RSA-CHACHA20-POLY1305:CHACHA20:ECDHE+AESGCM:ECDHE+AES listen on lo0 listen on lo0 port 10026 tag SPAM_IN listen on lo0 port 10028 tag DKIM_OUT listen on egress tls pki mail.example.com auth-optional listen on egress smtps pki mail.example.com auth-optional table aliases file:/etc/mail/aliases table vdomains file:/etc/mail/vdomains table vusers file:/etc/mail/vusers action "local" mbox alias <aliases> action "relay" relay action "relay_spampd" relay host smtp://127.0.0.1:10025 action "relay_dkim" relay host smtp://127.0.0.1:10027 action "kopano" mda "/usr/local/sbin/kopano-dagent -s %{dest}" virtual <vusers> user "_kopano" # tags match tag SPAM_IN for domain <vdomains> action "kopano" match tag DKIM_OUT for any action "relay" # local match for local action "local" match from any for domain <vdomains> action "relay_spampd" match for any action "relay_dkim" match !for domain <vdomains> reject ``` Restart smtpd: `rcctl restart smtpd` ## spamd Create custom spam files: ``` touch /etc/mail/{nospamd,spamd.black,custom.black} ``` Config: ``` all:\ :nixspam:custom_black: 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 custom_black:\ :black:\ :msg="Your address %A has been manually blacklisted":\ :method=file:\ :file=/etc/mail/custom.black: ``` Boot startup: Replace `example.com` with your domain. ``` rcctl enable spamd rcctl set spamd flags "-v -G 4:4:864 -K /etc/ssl/private/example.com.key -C /etc/ssl/example.com.fullchain.pem" rcctl enable spamlogd ``` Create `spamd-setup` cronjob: `echo "*/5 * * * * /usr/libexec/spamd-setup -b >/dev/null 2>&1" >> /var/cron/tabs/root` ### pf Rules ``` cat <<EOF>> /etc/pf.conf set limit table-entries 400000 smtp_ports = "{ smtp, 465 }" table <spamd> persist table <nospamd> persist file "/etc/mail/nospamd" table <spamd-white> persist pass in quick log on egress proto tcp \ from { <nospamd>, <spamd-white> } \ to any port $smtp_ports \ flags S/SA modulate state pass in quick proto tcp from <spamd> \ to egress port $smtp_ports \ rdr-to 127.0.0.1 port spamd pass in quick proto tcp from any \ to egress port $smtp_ports \ rdr-to 127.0.0.1 port spamd pass out log on egress proto tcp to any port $smtp_ports EOF pfctl -f /etc/pf.conf ``` Start spamd and spamlogd: ``` rcctl start spamd rcctl start spamlogd ``` ## MariaDB Basic config at [OpenBSD MariaDB](/wiki/OpenBSD+MariaDB) Create database: ``` mysql -u root -p CREATE DATABASE kopano CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; GRANT ALL ON kopano.* TO 'kopano'@'localhost' IDENTIFIED BY '<password>'; FLUSH PRIVILEGES; ``` ## Kopano Install: `pkg_add kopano-core kopano-mapi` Boot startup: `rcctl enable kopano` Make sure mysqld starts before kopano: `rcctl order mysqld` ### Config Change services to start: ``` perl -p -i -e 's/^(_pkg_scripts=.*)/#$1\n_pkg_scripts="kopano_server kopano_spooler kopano_monitor kopano_gateway"/' /etc/rc.d/kopano ``` Link kopano socket to chroot: ``` sed '/rc_cmd/d' /etc/rc.d/kopano_server cat <<'EOF'>> /etc/rc.d/kopano_server sock="/var/run/kopano/server.sock" www_sock="/var/www/run/server.sock" rc_start() { ${rcexec} "${daemon} ${daemon_flags} ${_bg}" exc=$? sleep 2 ln $sock $www_sock return $exc } rc_post() { rm -f $www_sock } rc_cmd $1 EOF ``` ``` /etc/kopano/server.cfg Set DB Credentials: mysql_user = kopano mysql_password = <password> mysql_socket = /var/run/mysql/mysql.sock ``` ``` /etc/kopano/gateway.cfg pop3_listen = pop3s_listen = imap_listen = imaps_listen = *:993 server_socket = file:///var/run/kopano/server.sock disable_plaintext_auth = yes ssl_private_key_file = /etc/ssl/private/example.com.key ssl_certificate_file = /etc/ssl/example.com.fullchain.pem log_file = /var/log/kopano/gateway.log ``` ``` /etc/kopano/search.cfg server_bind_name = file:///var/run/kopano/search.sock ``` Start kopano: `rcctl start kopano` Create a user: `kopano-admin -c "john@example.com" -f "John Doe" -e "john@example.com" -P` ## Kopano Webapp Install: `pkg_add kopano-webapp` Boot startup: `rcctl enable php70_fpm` ### Config `echo "extension=mapi.so" > /etc/php-7.0/kopano.ini` Don't expose php: `sed -i 's/^expose_php.*/expose_php = off/' /etc/php-7.0.ini` Change Server Socket: ``` sed -i 's|^.define("DEFAULT_SERVER".*|define("DEFAULT_SERVER", "file:///run/server.sock");|' /var/www/kopano-webapp/config.php ``` Start php-fpm: `rcctl start php70_fpm` ## Z-Push Install: `pkg_add z-push` ### Config Change Server Socket: ``` sed -i "s|^define('MAPI_SERVER'.*|define('MAPI_SERVER', 'file:///run/server.sock');|" /var/www/z-push/backend/kopano/config.php ``` ## httpd Basic config at [OpenBSD httpd](/wiki/OpenBSD+httpd) Kopano config: ``` connection { max request body 20971520 } root "/kopano-webapp" directory index "index.php" location "*.php" { fastcgi socket "/run/php-fpm.sock" } location "/Microsoft-Server-ActiveSync" { root "/z-push/index.php" fastcgi socket "/run/php-fpm.sock" } ``` # Optionals ## Kopano Passwd Plugin If not yet installed: `pkg_add unzip--` Install: ``` mkdir /var/www/kopano-webapp/plugins/passwd ftp -o /var/www/kopano-webapp/plugins/passwd-1.3.zip https://github.com/silentsakky/zarafa-webapp-passwd/releases/download/1.3/passwd-1.3.zip unzip -q /var/www/kopano-webapp/plugins/passwd-1.3.zip -d /var/www/kopano-webapp/plugins/passwd ``` Config: ``` sed -i "s|^define('PLUGIN_PASSWD_USER_DEFAULT_ENABLE'.*|define('PLUGIN_PASSWD_USER_DEFAULT_ENABLE', true);|" /var/www/kopano-webapp/plugins/passwd/config.php sed -i "s|^define('PLUGIN_PASSWD_LOGIN_WITH_TENANT'.*|define('PLUGIN_PASSWD_LOGIN_WITH_TENANT', true);|" /var/www/kopano-webapp/plugins/passwd/config.php ``` ## OpenBGPD Project: [bgp-spamd](http://bgp-spamd.net/) Boot startup: `rcctl enable bgpd` ### Config OpenBGPD config: ``` cat <<'EOF'> /etc/bgpd.conf spamdAS="65066" AS 65001 fib-update no # Mandatory, to not update the local routing table nexthop qualify via default # Make sure the nexthop is valid group "spamd-bgp" { remote-as $spamdAS multihop 64 export none # us.bgp-spamd.net neighbor 64.142.121.62 # eu.bgp-spamd.net neighbor 217.31.80.170 # IPv6 eu.bgp-spamd.net #neighbor 2a00:15a8:0:100:0:d91f:50aa:1 } deny to any deny from any allow from group "spamd-bgp" # 'match' is required, to remove entries when routes are withdrawn # This updates the <bgp-spamd-bypass> table in PF match from group "spamd-bgp" community $spamdAS:42 set pftable "bgp_spamd_bypass" match from group "spamd-bgp" community $spamdAS:666 set pftable "bgp_spamd" EOF chmod 600 /etc/bgpd.conf ``` Start OpenBGPD: `rcctl start bgpd` Add these options to your `/etc/pf.conf`: ``` set limit table-entries 400000 table <bgp_spamd_bypass> persist table <bgp_spamd> persist bgp_addrs = "{ 64.142.121.62, 217.31.80.170, 2a00:15a8:0:100:0:d91f:50aa:1 }" pass in on egress proto tcp from $bgp_addrs to any port bgp ``` Change your pf smtp rules to match these: ``` pass in quick log on egress proto tcp \ from { <bgp_spamd_bypass>, <nospamd>, <spamd-white> } \ to any port $smtp_ports \ flags S/SA modulate state pass in quick proto tcp from { <bgp_spamd>, <spamd> } \ to egress port $smtp_ports \ rdr-to 127.0.0.1 port spamd ``` Reload pf: `pfctl -f /etc/pf.conf` Edit the `/etc/mail/spamd.conf`: ``` # Add bgp-spamd to the all list: all:\ :nixspam:custom_black:bgp-spamd: # Add the bgp-spamd list: bgp-spamd:\ :black:\ :msg="Your address %A has sent mail to a spamtrap\n\ within the last 24 hours":\ :method=file:\ :file=/etc/mail/spamd.black: ``` Create the spamd-reload script: ``` cat <<'EOF'> /usr/local/sbin/spamd-update #!/bin/sh black="/etc/mail/spamd.black" custom="/etc/mail/custom.black" bgpctl show rib community 65066:666|tail -n+5| awk '{sub(/\/.*/, "", $1);print $1}'|uniq >${black} for ip in $(cat ${custom}); do /usr/sbin/spamdb -d ${ip} 2> /dev/null done /usr/libexec/spamd-setup -b EOF chmod 0744 /usr/local/sbin/spamd-update ``` Replace the `spamd-setup` cronjob from above with the new script and run it once manually: `/usr/local/sbin/spamd-update` ## Bypass Big Mailproviders Add this to your `/usr/local/sbin/spamd-update`: ``` smtpctl spf walk <<EOF | sort -u | pfctl -t trusted_bypass -T replace -f- 2>/dev/null _netblocks.google.com _netblocks2.google.com _netblocks3.google.com steampowered.com amazonses.com outlook.com _spf.apple.com EOF ``` Add the `trusted_bypass` table to your pf.conf: ``` table <trusted_bypass> persist pass in quick log on egress proto tcp \ from { <trusted_bypass>, <bgp_spamd_bypass>, <nospamd>, <spamd-white> } \ ... ``` ## Manual Spam-Reporting For the manual spam-reporting to work properly, you have to forward the desired SPAM-Mail as attachment to spamreport@example.com (replace `example.com` with your domain). The spamreport fake MDA (Mail Delivery Agent) will inspect the `Recieved:` mail header to find the sender IP-address. This IP-address will then be blacklisted in the custom blacklist. Add the `spamreport` system user: `useradd -d /var/empty -s /sbin/nologin spamreport` Create the spamreport MDA: ``` cat <<'EOF'> /usr/local/sbin/spamreport #!/bin/sh tmp=$(mktemp) blacklist="/etc/mail/custom.black" perl -ne 'print "$1\n" if/\[([12]?[0-9]{1,2}.*)\]/&&!/127/' >>${blacklist} sort -u -o ${tmp} ${blacklist} cp ${tmp} ${blacklist} rm -f ${tmp} EOF chmod 755 /usr/local/sbin/spamreport ``` Change blacklist permissions: ``` chgrp spamreport /etc/mail/custom.black chmod 664 /etc/mail/custom.black ``` Change your smtpd.conf to look like this (replace `example.com` with your domain): ``` action "spamreport" mda "/usr/local/sbin/spamreport" user "spamreport" match from local rcpt-to "spamreport@example.com" action "spamreport" ``` Restart smtpd: `rcctl restart smtpd` ## ClamAV *Optional* Install: `pkg_add clamav clamsmtp` Boot startup: ``` rcctl enable clamd ``` ### Config #### clamd ``` /etc/clamd.conf Comment out the 'Example' string: #Example Uncomment these options: TCPSocket 3310 TCPAddr 127.0.0.1 ``` #### freshclam ``` /etc/freshclam.conf Comment out the 'Example' string: #Example Change #DatabaseMirror db.XY.clamav.net to DatabaseMirror db.de.clamav.net ``` Create the update cronjob: `echo "20 * * * * /usr/local/bin/freshclam >/dev/null 2>&1" >> /var/cron/tabs/root` #### clamsmtpd Create two clamsmtpd configs: ``` cp /etc/clamsmtpd.conf /etc/clamsmtpd-in.conf cp /etc/clamsmtpd.conf /etc/clamsmtpd-out.conf ``` ``` /etc/clamsmtpd-in.conf Change to match these values: OutAddress: 10026 Listen: 127.0.0.1:10025 User: _clamav Add this option: ClamAddress: 127.0.0.1:3310 ``` ``` /etc/clamsmtpd-out.conf Change to match these values: OutAddress: 10028 Listen: 127.0.0.1:10027 User: _clamav Add this option: ClamAddress: 127.0.0.1:3310 ``` Create the two init scripts: ``` cat <<'EOF'> /etc/rc.d/clamsmtpd_in #!/bin/sh daemon="/usr/local/sbin/clamsmtpd -f /etc/clamsmtpd-in.conf" . /etc/rc.d/rc.subr rc_cmd $1 EOF ``` ``` cat <<'EOF'> /etc/rc.d/clamsmtpd_out #!/bin/sh daemon="/usr/local/sbin/clamsmtpd -f /etc/clamsmtpd-out.conf" . /etc/rc.d/rc.subr rc_cmd $1 EOF ``` `chmod 0755 /etc/rc.d/clamsmtpd_{in,out}` Enable the services: ``` rcctl enable clamsmtpd_in rcctl enable clamsmtpd_out ``` Run the initial freshclam: `/usr/local/bin/freshclam` Start the daemons: ``` rcctl start clamd rcctl start clamsmtpd_in rcctl start clamsmtpd_out ``` ## Backup ``` #!/bin/sh db="kopano" db_user="kopano" db_pw="<password>" set -A paths \ /etc/kopano \ /etc/mail \ /etc/pf.conf \ /etc/httpd.conf \ /etc/clamsmtpd-in.conf \ /etc/clamsmtpd-out.conf \ /etc/clamd.conf \ /etc/freshclam.conf \ /etc/ssl \ /etc/acme-client.conf \ /etc/bgpd.conf \ /etc/rc.conf.local \ /etc/rc.d/clamsmtpd_in \ /etc/rc.d/clamsmtpd_out \ /etc/my.cnf \ /etc/rc.d/kopano \ /etc/php-7.0/kopano.ini \ /etc/php-7.0.ini \ /var/db/clamav \ /var/db/spamassassin \ /var/db/spamd \ /var/db/kopano \ /var/www/kopano-webapp/config.php \ /var/www/z-push/backend/kopano/config.php \ /usr/local/sbin/spamd-update \ /usr/local/sbin/cron-acme-update \ /usr/local/sbin/spamreport \ /usr/local/sbin/watchdog \ /etc/mail/spamassassin/local.cf \ /var/cron/tabs/root \ /tmp/*.pf \ /tmp/${db}.sql for t in $(pfctl -s Tables); do pfctl -t ${t} -T show > /tmp/${t}.pf done mysqldump -u ${db_user} -p${db_pw} ${db} >/tmp/${db}.sql tar cfvz /root/mailserver_backup_$(date +%Y%m%d_%H%M%S).tgz ${paths[@]} rm /tmp/${db}.sql /tmp/*.pf ``` Restore backup: `tar xfpvz mailserver_backup<date>.tgz -C /` Restore pf tables: `for t in $(ls /tmp/*.pf); do pfctl -t ${t%.pf} -T add -f ${t}; done` Restore database: `mysql -u root -p kopano < /tmp/kopano.sql` # Bug fixes ## Watchdog ``` cat <<'EOF'>/usr/local/sbin/watchdog #!/bin/sh for p in $(rcctl ls failed|grep -v spampd); do rcctl start ${p} done z=$(ps aux|grep -E "_spampd.*Z"|grep -v grep) if [ "${z}" != "" ] || ! rcctl check spampd >/dev/null; then pkill -9 -f spampd rcctl start spampd fi EOF chmod 755 /usr/local/sbin/watchdog echo "*/5 * * * * /usr/local/sbin/watchdog >/dev/null 2>&1" >> /var/cron/tabs/root ``` # END