OpenBSD Email Service
a free-email alternative
Root your Inbox :mailbox_with_mail:
By design, email message headers need to be public, for exchanges to happen. The body of the message can be encrypted by the user, if desired. Moreover, there is no way to prevent the host from having access to the virtual machine. Therefore, full disk encryption (at rest) may not be necessary.
Given our low memory requirements, and the single-purpose concept of email service, Roundcube or other web-based IMAP email clients should be on a different VPS.
Antivirus software users (usually) have the service running on their devices. ClamAV can easily be incorporated into this configuration, if affected by the types of malware it protects against, but will require around 1GB additional RAM (or another VPS).
Every email message is important, if properly delivered, for Bayes classification. At least 200 ham and 200 spam messages are required to learn what one considers junk (2000+ for best results). By default (change to use case), a rspamd score above 50% will send the message to Spam/. Moving messages in and out of Spam/ changes this score. After 95%, the message is flagged as "seen" and can be safely ignored.
spamd is effective at greylisting and stopping high volume spam, if it becomes a problem. It will be an option when IPv6 is supported, along with bgp-spamd. To build IP lists for greylisting, please use spfwalk with spf_fetch.
System mail is delivered to an alias mapped to a virtual user served by the service. This way, messages are guaranteed to be delivered via encrypted connection. It is not possible for real users to alias, nor mail
an external mail address with the default configuration.
e.g. puffy@mercury.example.com is wheel, with an alias mapped to (virtual) puffy@example.com, and user (puffy) can be different for each.
See Prerequisites and the Installation Guide for details.
Grab a copy of this repository, and put overrides in "Makefile.local" e.g.:
# Makefile.local
DOMAIN_NAME = example.com
VHOSTS_NAME = example.net \
example.org
PRIMARY = yes
PRIMARY_HOST = mercury
PRIMARY_IPv4 = 203.0.113.1
PRIMARY_IPv6 = 2001:0db8::1
BACKUP_HOST = hermes
BACKUP_IPv4 = 203.0.113.2
BACKUP_IPv6 = 2001:0db8::2
DKIM_SELECTOR = obsd
EGRESS = vio0
WHEEL_USER = puffy
REPLICATION_USER = dsync
VIRTUAL_USER = ${WHEEL_USER}
AUTOEXPUNGE = 30d
MAIL_QUOTA = 15G
MAX_MESSAGE_SIZE = 35M
FULL_SYNC_INTERVAL = 1h
UPGRADE = yes
make install
n.b. UPGRADE uses sdiff
side-by-side diff (with new on the right side)
Update virtual users credentials table src/etc/mail/passwd
using smtpctl encrypt
smtpctl encrypt
> secret
> $2b$...encrypted...passphrase...
vi src/etc/mail/passwd
> puffy@example.com:$2b$...encrypted...passphrase...::::::
smtpctl update table passwd
n.b.: user quota limit can be overriden from src/etc/mail/passwd
puffy@example.com:$2b$...encrypted...passphrase...::::::userdb_quota_rule=*:storage=7G
Review virtual domains aliasing table src/etc/mail/virtual
n.b. see Administration for virtual user and domain management
n.b. Without backup MX, leave BACKUP_HOST empty in "Makefile.local"
Dovecot Replication user "dsync" SSH limited to one "command" restricted in doas.conf
to match "dsync_remote_cmd". On primary and backup hosts
su - dsync
ssh-keygen
echo "command=\"doas -u vmail \${SSH_ORIGINAL_COMMAND#*}\" $(cat ~/.ssh/id_rsa.pub)" | \
ssh puffy@hermes.example.com "cat >> /home/dsync/.ssh/authorized_keys"
exit
Alternatively, OpenSSH certificates allow fine-grained control to local users and hosts. The force-command
is passed to ssh-keygen as certificate option (-O) instead of using "authorized_keys".
Update /home/dsync, on primary and backup hosts
chown -R root:dsync /home/dsync
chmod 750 /home/dsync/.ssh
chmod 640 /home/dsync/.ssh/{authorized_keys,id_*.pub,known_hosts}
chmod 400 /home/dsync/.ssh/{id_ecdsa,id_ed25519,id_rsa}
chown dsync /home/dsync/.ssh/{id_ecdsa,id_ed25519,id_rsa}
Update /root/.ssh/known_hosts
on primary and backup hosts
ssh -4 -i/home/dsync/.ssh/id_rsa -ldsync hermes.example.com "exit"
ssh -6 -i/home/dsync/.ssh/id_rsa -ldsync hermes.example.com "exit"
n.b.: MUA auto-configuration via Autoconfiguration and SRV Records for Locating Email Services
IMAP server: mercury.example.com (or hermes.example.com)
SMTP server: mercury.example.com (or hermes.example.com)
SMTP server: mercury.example.com (or hermes.example.com)
A DNS name server (from a registrar, a free service, VPS host, or self-hosted) is required, which allows editing the following record types: A, AAAA, MX, CNAME, SRV, CAA, SSHFP, TXT
n.b. see example zone
DNSSEC is recommended
Primary domain has record types A, and AAAA for each MX subdomain with the relays' IPv4, and IPv6
mercury.example.com 86400 IN A 203.0.113.1
mercury.example.com 86400 IN AAAA 2001:0db8::1
Each IPv4 and IPv6 has record type PTR with the MX subdomain (reverse DNS configured on VPS host)
...6 IN PTR mercury.example.com
Verify:
dig +short mercury.example.com a
> 203.0.113.1
dig +short -x 203.0.113.1
> mercury.example.com.
dig +short mercury.example.com aaaa
> 2001:0db8::1
dig +short -x 2001:0db8::1
> mercury.example.com.
Primary and virtual domains have identical records type MX with priority and relay hostnames
example.com 86400 IN MX 10 mercury.example.com
example.com 86400 IN MX 20 hermes.example.com
Primary and virtual domains have identical records type CNAME for autoconfig subdomain pointing to Autoconfiguration server
autoconfig.example.com 86400 IN CNAME mercury.example.com
Primary and virtual domains have identical records type CNAME for wkd subdomain pointing to Web Key Server
wkd.example.com 86400 IN CNAME mercury.example.com
Primary domain has record type SRV with primary WKD subdomain
_openpgpkey._tcp.example.com 86400 IN SRV 0 0 443 wkd.example.com
Each virtual domain has record type SRV with virtual WKD subdomain
_openpgpkey._tcp.example.net 86400 IN SRV 0 0 443 wkd.example.net
Primary and virtual domains have identical records type SRV for simple MUA auto-configuration
_submission._tcp.example.com 86400 IN SRV 0 1 465 mercury.example.com
_submission._tcp.example.com 86400 IN SRV 5 1 587 mercury.example.com
_imaps._tcp.example.com 86400 IN SRV 0 1 993 mercury.example.com
_imap._tcp.example.com 86400 IN SRV 0 0 0 .
_pop3s._tcp.example.com 86400 IN SRV 0 0 0 .
_pop3._tcp.example.com 86400 IN SRV 0 0 0 .
Primary and virtual domains have identical records type CAA with letsencrypt.org as the only CA allowed to issue certificates
example.com 86400 IN CAA 128 issue "letsencrypt.org"
example.com 86400 IN CAA 128 issuewild ";"
Print the SSHFP fingerprint resource record on each hostname
ssh-keygen -r mercury.example.com
Primary domain has records type SSHFP for each MX subdomain with a fingerprint of the server's public key
mercury.example.com 86400 IN SSHFP 1 1 2...
mercury.example.com 86400 IN SSHFP 1 2 5...
mercury.example.com 86400 IN SSHFP 3 1 6...
mercury.example.com 86400 IN SSHFP 3 2 8...
mercury.example.com 86400 IN SSHFP 4 1 7...
mercury.example.com 86400 IN SSHFP 4 2 a...
Primary and virtual domains have identical records type TXT with primary domain SPF data
example.com 86400 IN TXT "v=spf1 mx:example.com -all"
Primary domain has record type TXT for each MX subdomain, to return receipt notifications (DSNs and MDNs), with relays' IP SPF data
mercury.example.com 86400 IN TXT "v=spf1 a -all"
hermes.example.com 86400 IN TXT "v=spf1 a -all"
Generate a private and public key
mkdir -p /etc/ssl/dkim/private
chmod 750 /etc/ssl/dkim/private
Signers SHOULD use RSA keys of at least 2048 bits. -- https://tools.ietf.org/html/rfc8301#section-3.2
(umask 337; openssl genrsa -out /etc/ssl/dkim/private/private.key 2048)
openssl rsa -in /etc/ssl/dkim/private/private.key -pubout -out /etc/ssl/dkim/public.key
chown -R _rspamd:_dkimproxy /etc/ssl/dkim/private
Widely used DNS configuration software places a practical limit on key sizes, because the software only handles a single 256-octet string in a TXT record, and RSA keys significantly longer than 1024 bits don't fit in 256 octets. -- https://tools.ietf.org/html/rfc8301#section-1
Multiple strings in a single DNS record are useful in constructing records that would exceed the 255-byte maximum length of a string within a single TXT RR record. -- https://tools.ietf.org/html/rfc4408#section-3.1.3
Generate the TXT-DATA field for DKIM TXT record
echo "v=DKIM1; k=rsa; p=$(sed -e '1d' -e '$d' /etc/ssl/dkim/public.key | tr -d '\n')"
Alternatively
(umask 337; rspamadm dkim_keygen -d example.com -s 'obsd' -k /etc/ssl/dkim/private/private.key -b 2048 -t rsa) > /etc/ssl/dkim/public.key
Primary and virtual domains have identical records type TXT for selector._domainkey subdomain with DKIM public key
obsd._domainkey.example.com 86400 IN TXT ( "v=DKIM1; k=rsa; "
"p=abcd"
"ef"
) ;
Primary and virtual domains have record type TXT for _dmarc subdomain with DMARC policy data
_dmarc.example.com 86400 IN TXT "v=DMARC1; p=reject; pct=100; rua=mailto:dmarcreports@example.com"
Primary domain has record type TXT for _mta-sts subdomain with MTA-STS reporting data
_mta-sts.example.com 86400 IN TXT "v=STSv1; id=20190515085700Z;"
Each virtual domain has record type CNAME for _mta-sts subdomain pointing to MTA-STS TXT record
_mta-sts.example.net 86400 IN CNAME _mta-sts.example.com
Primary and virtual domains have identical records type CNAME for mta-sts subdomain pointing to MTA-STS policy
mta-sts.example.com 86400 IN CNAME mercury.example.com
Primary and virtual domains have records type TXT for _smtp._tls subdomain with TLS reporting data
_smtp._tls.example.com 86400 IN TXT "v=TLSRPTv1;rua=mailto:tlsreports@example.com"
requires DNSSEC and manual server key rotation using the procedure form rfc7671
Print each relay's TLSA resource record
pkg_add ldns-utils
ldns-dane create mercury.example.com 443 3 1 1
ldns-dane create hermes.example.com 443 3 1 1
Primary domain has records type TLSA for each tlsa._dane MX subdomain
tlsa._dane.mercury.example.com 86400 IN TLSA 3 1 1 e3b0c44298fc1c14...
tlsa._dane.hermes.example.com 86400 IN TLSA 3 1 1 f2c0d55309cf2d25...
Primary domain has records type CNAME for each _service._tcp MX subdomain pointing to TLSA RR
_993._tcp.mercury.example.com 86400 IN CNAME tlsa._dane.mercury.example.com
_587._tcp.mercury.example.com 86400 IN CNAME tlsa._dane.mercury.example.com
_443._tcp.mercury.example.com 86400 IN CNAME tlsa._dane.mercury.example.com
_25._tcp.mercury.example.com 86400 IN CNAME tlsa._dane.mercury.example.com
_993._tcp.hermes.example.com 86400 IN CNAME tlsa._dane.hermes.example.com
_587._tcp.hermes.example.com 86400 IN CNAME tlsa._dane.hermes.example.com
_443._tcp.hermes.example.com 86400 IN CNAME tlsa._dane.hermes.example.com
_25._tcp.hermes.example.com 86400 IN CNAME tlsa._dane.hermes.example.com
#caesonia:matrix.org (deadish)
Contributions welcome, fork
Hosted by Open Source Collective 501c6, contribute
This project exists thanks to all the people who contribute.
Thank you to all our backers! :pray: [Become a backer]
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]