2021.10.07 - Postfix setup, with DKIM and Dovecot
Setting up Postfix on EL8 with DKIM signing and Dovecot for POP and SASL
- Home
- Intro
- Getting Started
- Configuring Postfix (main.cf)
- Configuring Postfix (master.cf)
- Configuring Postfix (Other Files)
- Configuring Amavis
- Configuring Dovecot
- Configuring Postgrey
- Finishing Up
Intro
There's alot to setting up a fully functional email server, and a bunch of differant ways to do it. Since I'm very much stuck in the early 2010's I like Postfix, Amavis, Clamav, Dovecot, Procmail and POP/IMAP. But I still want to follow all the current email best practices like DKIM signing for emails. In a world were most people just use Google Apps or Office 365 this puts me in a wierd place. If you also find yourself in that wierd place then this is the guide for you. Needless to say I got information for this from a bunch of different places. I'll list them below. If you are just looking to setup one part of my crazy system then you might be better off just reading that guide:
- https://www.linuxbabe.com/redhat/run-your-own-email-server-centos-postfix-smtp-server
Basic getting started with Postfix on EL8 and setting up an email server in general - https://www.linuxbabe.com/redhat/install-dovecot-centos-enable-tls-encryption
Setting up Dovecot (including using it for SASL Auth!) and getting your server a free SSL Cert - https://www.linuxbabe.com/mail-server/amavis-clamav-centos-8-rhel-8
Setting up Amavis and Clamav for Spam filtering and Virus scanning. - https://kb.kolabenterprise.com/guides/configure-dkim-signing-and-verification-using-amavis
Configuring Amavis to sign outgoing emails with DKIM
Getting Started
First you'll need to install all the things:
yum install postfix alpine postgrey postfix-pcre dovecot amavis clamav clamav-lib clamav-data clamav-update
(yes I'm stubbornly sticking to yum instead of dnf; I know they are the same command) So this will install Postfix, Alpine (cli email reader), Postgrey (for greylisting), Amavis and Clamav. If you get an error about missing Perl packages then you'll probably need to enable the powertools repo: yum config-manager --enable powertools. Once everything is installed just start at the top and work your way down configuring things. First, Postfix.
Configuring Postfix (main.cf)
So Postfix has a main.cf and master.cf file where most of the config goes. There's also a bunch of other files I'll be adding to the /etc/postfix directory. First, starting with main.cf:
myhostname = mail.5i5.org
mydomain = 5i5.org
myorigin = $myhostname
inet_interfaces = all
inet_protocols = ipv4 # This one is important if you don't have IPv6 connectivity
mydestination = $myhostname, $mydomain
mynetworks = /etc/postfix/networks
virtual_maps = hash:/etc/postfix/virtual
mailbox_command = /usr/bin/procmail
body_checks_size_limit = 51200
body_checks = regexp:/etc/postfix/body_checks
header_checks = regexp:/etc/postfix/header_checks
mime_header_checks = regexp:/etc/postfix/mime_header_checks
smtpd_banner = $myhostname ESMTP $mail_name
# the whole TLS CONFIGURATION section at the end of the file is deleted as I'll be replacing that
# Settings to enable SASL Auth with Dovecot in Postfix (still need to conf Dovecot)
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtp_sasl_security_options = noanonymous
# I choose to put all my spam filtering under smtpd_recipient_restrictions.
# This means the email is only filtered once the whole message has been recevied
# If you are concerned about server resources you might want to break this up.
smtpd_recipient_restrictions =
permit_mynetworks,
sleep 5,# in theory helps slow down people trying to bruteforce SASL passwords
permit_sasl_authenticated,
reject_unauth_destination,
check_recipient_access hash:/etc/postfix/rcpt,
check_sender_access hash:/etc/postfix/mailfrom,
### USE /etc/postfix/postgrey_whitelist_recipients to bypass greylisting for some accounts
reject_invalid_hostname,
reject_non_fqdn_hostname,
check_helo_access hash:/etc/postfix/helo_access,
reject_non_fqdn_recipient,
reject_non_fqdn_sender,
reject_unknown_sender_domain,
reject_unknown_recipient_domain,
reject_rbl_client psbl.surriel.com=127.0.0.2,
reject_rbl_client b.barracudacentral.org=127.0.0.2,
reject_rbl_client xbl.spamhaus.org=127.0.0.4,
reject_rhsbl_reverse_client multi.surbl.org=127.0.0.[2..254],
reject_rhsbl_helo multi.surbl.org=127.0.0.[2..254],
reject_rhsbl_sender multi.surbl.org=127.0.0.[2..254],
reject_unknown_reverse_client_hostname,
check_policy_service unix:postgrey/socket,
check_client_access pcre:/etc/postfix/default_filter.pcre
smtpd_relay_restrictions =
permit_mynetworks,
sleep 5,
permit_sasl_authenticated,
reject_unauth_destination
smtpd_data_restrictions = reject_unauth_pipelining
smtpd_helo_required = yes
non_fqdn_reject_code = 404 # Changed from the default of 504 in case of DNS issues
disable_vrfy_command = yes
# These next lines impose limits on servers connecting, these numbers don't seem to cause any issues,
# but could further optimized if you wanted to
smtpd_soft_error_limit = 3
smtpd_hard_error_limit = 3
smtpd_client_connection_count_limit = 6
smtpd_client_connection_rate_limit = 10
smtpd_client_event_limit_exceptions = $mynetworks
anvil_rate_time_unit = 120s
# These two lines help to avoid hitting rate limits on popular free email services
smtp_destination_concurrency_limit = 1
smtp_destination_rate_delay = 10s
# Just two small last settings:
smtpd_proxy_options = speed_adjust # Helps keep Amavis happy
content_filter = amavisfeed:[127.0.0.1]:10023 # Feeds any local email into Amavis for DKIM signing
# Remote email that passes the spam checks is feed into Amavis via the check_client_access line
Of course it's not required but encrypting communications is just a good practice now-a-days and free SSL Certs are so easy to get. I've started using acme.sh to get free Certs from Let's Encrypt. For a server just doing email and nothing else you can get a quick cert with a
command like this:
acme.sh --force --days 90 --issue --server letsencrypt --preferred-chain "ISRG Root X1" --cert-file /root/mail.example.com.cer --key-file /root/mail.example.com.key
--fullchain-file /root/mail.example.com.fc.cer --standalone -d mail.example.com
With an SSL cert here's how to enable it in postfix:
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtp_tls_cert_file = /root/mail.example.com.cer
smtp_tls_key_file = /root/mail.example.com.key
smtp_tls_CAfile = /root/mail.example.com.fc.cer
smtp_tls_session_cache_database = btree:/var/lib/postfix/smtp_tls_session_cache
smtp_tls_ciphers = export
smtp_tls_protocols = !SSLv2
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1
smtpd_tls_cert_file = /root/mail.example.com.cer
smtpd_tls_key_file = /root/mail.example.com.key
smtpd_tls_CAfile = /root/mail.example.com.fc.cer
smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_tls_session_cache
smtpd_tls_eecdh_grade = strong
smtpd_tls_ciphers = medium
smtpd_tls_protocols = !SSLv2, !SSLv3
Configuring Postfix (master.cf)
Master.cf controls all the port and network connections that Postfix uses. We'll be enabling an alt port 587 (submission) and 465 (smtps) to receive email on and setting up some ports for Amavis.
submission inet n - n - - smtpd
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,permit_mynetworks,reject
-o content_filter=amavisfeed:[127.0.0.1]:10023
smtps inet n - n - - smtpd
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,permit_mynetworks,reject
-o content_filter=amavisfeed:[127.0.0.1]:10023
amavisfeed unix - - n - 2 lmtp
-o lmtp_data_done_timeout=1200
-o lmtp_send_xforward_command=yes
-o disable_dns_lookups=yes
-o max_use=20
127.0.0.1:10025 inet n - n - - smtpd
-o content_filter=
-o smtpd_delay_reject=no
-o smtpd_client_restrictions=permit_mynetworks,reject
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_data_restrictions=reject_unauth_pipelining
-o smtpd_end_of_data_restrictions=
-o smtpd_restriction_classes=
-o mynetworks=127.0.0.0/8
-o smtpd_error_sleep_time=0
-o smtpd_soft_error_limit=1001
-o smtpd_hard_error_limit=1000
-o smtpd_client_connection_count_limit=0
-o smtpd_client_connection_rate_limit=0
-o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters,no_address_mappings
-o local_header_rewrite_clients=
-o smtpd_milters=
-o local_recipient_maps=
-o relay_recipient_maps=
Configuring Postfix (Other Files)
There's a handful of other files in the /etc/postfix directory that I'll change/create:
/etc/postfix/body_checks:
~^[[:alnum:]+/]{60,}$~ OK # First skip over base 64 encoded text to save CPU cycles.
# Some obvious spam email to dump:
/how to purchase bitcoin/ Reject Spam line 1
/how to buy bitcoin/ Reject Spam line 2
/I hacked this mailbox/ Reject Spam line 3
/if you do not know how to do this, write to Google/ Reject Spam line 4
/Your account has been hacked by me/ Reject Spam line 5
/how to replenish btc wallet/ Reject Spam line 6
/how to transfer money to a bitcoin wallet/ Reject Spam line 7
/how to make a payment on a bitcoin wallet/ Reject Spam line 8
/how to send money to btc wallet/ Reject Spam line 9
/etc/postfix/default_filter.pcre:
/./ FILTER amavisfeed:[127.0.0.1]:10024
/etc/postfix/helo_access:
# Stop some obvious spammers:
example.com REJECT E-mail error, please contact your ISP
mail.example.com REJECT E-mail error, please contact your ISP
/etc/postfix/mailfrom:
# Again, stop some obvious spammers (they would probably get caught by SPF)
support@example.com Reject You cannot send out email as support
support@mail.example.com Reject You cannot send out email as support
info@example.com Reject You do not own this address
info@mail.example.com Reject You do not own this address
postmaster@mail.example.com Reject You do not own this address
postmaster@example.com Reject You do not own this address
/etc/postfix/mime_header_checks:
# Block undesirable email attachements
/name=[^>]*\.(js|exe|vbs|pif|scr|bat|cmd|cpl|dll|rar|arc|arj|zoo|ade|adp|app|bas|bat|chm|cmd|cpl|crt|emf|exe|fxp|grp|hlp|hta|inf|ins|isp|cab|jse|lnk|mda|mde|mdw|mdt|mdz|msc|msi|msp|mst|ops|pcd|pif|prg|reg|scr|sct|shb|shs|vb|vbe|vbs|wmf|wsc|wsf|wsh)/ REJECT We are sorry, but due to complaints we are no longer able to accept emails with this kind of attachment.
/etc/postfix/networks:
# List of networks that are permitted unrestricted access to Postfix
127.0.0.1/32,
192.168.200.0/24
/etc/postfix/rcpt:
# List of email addresses to do minimal checking on
support@example.com OK
postmaster@example.com OK
/var/scripts/viup:
#!/bin/bash
# Handy script to run after changing a postfix/amavis/postgrey config file
# to rebuild/refresh everything
echo "Rebuilding Transport Database"
/usr/sbin/postmap /etc/postfix/transport
echo "Rebuilding Local Virual Database"
/usr/sbin/postmap /etc/postfix/virtual
echo "Rebuilding Denied Senders Database"
/usr/sbin/postmap /etc/postfix/mailfrom
echo "Rebuilding Rejected Server Access Map Database"
/usr/sbin/postmap /etc/postfix/rcpt
/usr/sbin/postmap /etc/postfix/helo_access
echo "Rebuilding Alias Database"
newaliases
sleep 1
service amavisd restart
service postgrey restart
sleep 1
/usr/sbin/postfix reload
/etc/aliases:
# Most of this file is probably fine, just set someone to get root's email
root: support
Configuring Amavis
There's a ton of options for Amavis, I'll be focusing on what needs to be changed to enable spam filtering, virus scanning, and DKIM signing. First file to edit is /etc/amavisd/amavisd.conf
$mydomain = 'example.com';
$QUARANTINEDIR = "/var/virusmails";
$helpers_home = "$MYHOME/var";
@mynetworks = qw( 127.0.0.0/8 [::1] [FE80::]/10 [FEC0::]/10 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 {add other IPs here} );
$inet_socket_port = [10022,10023,10024];
$policy_bank{'MYNETS'} = { bypass_spam_checks_maps => [1], bypass_banned_checks_maps => [1], originating => 1, os_fingerprint_method => undef, };
$interface_policy{'10023'} = 'SUBMISSION';
$policy_bank{'SUBMISSION'} = {bypass_spam_checks_maps => [1], bypass_banned_checks_maps => [1], originating => 1, smtpd_discard_ehlo_keywords => ['8BITMIME'], };
$policy_bank{'ORIGINATING'} = { originating => 1, allow_disclaimers => 1, warnbadhsender => 1, forward_method => 'smtp:[127.0.0.1]:10027', smtpd_discard_ehlo_keywords => ['8BITMIME'], bypass_spam_checks_maps => [1], bypass_banned_checks_maps => [1], terminate_dsn_on_notify_success => 0, };
$sa_tag_level_deflt = -999;
$virus_admin = '';
$spam_admin = '';
$mailfrom_notify_admin = "root\@$mydomain";
$mailfrom_notify_recip = "root\@$mydomain";
$mailfrom_notify_spamadmin = "root\@$mydomain";
$myhostname = 'example.com';
$notify_method = 'smtp:[127.0.0.1]:10025';
$forward_method = 'smtp:[127.0.0.1]:10025';
$final_bad_header_destiny = D_PASS;
$bad_header_quarantine_method = undef;
To setup DKIM there's a couple of commands to run to prep directories and generate the key:
mkdir /etc/amavisd/dkim/
chown root:amavis /etc/amavisd/dkim/
chmod 750 /etc/amavisd/dkim/
amavisd -c /etc/amavisd/amavisd.conf genrsa /etc/amavisd/dkim/example.com.dkim2021.pem 4096
Once the key is generated add these lines to the /etc/amavisd/amavisd.conf:
dkim_key(
'example.com',
'dkim2021',
'/etc/amavisd/dkim/example.com.dkim2021.pem'
);
dkim_key(
'mail.example.com',
'dkim2021',
'/etc/amavisd/dkim/example.com.dkim2021.pem'
);
@dkim_signature_options_bysender_maps = (
{
"." => {
a => 'rsa-sha256',
c => 'relaxed/simple',
ttl => 10*24*3600
}
}
);
There's also some records you'll need to put in the DNS. To generate them run this command:
amavisd -c /etc/amavisd/amavisd.conf showkeys example.com
The TXT record that it generates is going to look weird, it's just because the TXT record is so long, it's fine, enter it as displayed (assuming you are using BIND). Then to check to see if it
works run:
amavisd -c /etc/amavisd/amavisd.conf testkeys example.com
The last thing to do is add some custom weights to the spamassassin config (/etc/mail/spamassassin/local.cf):
required_hits 6.5
header RCVD_IN_WPBL eval:check_rbl('wpbl-lastexternal','db.wpbl.info.','127.0.0.2')
describe RCVD_IN_WPBL Listed in db.wpbl.info
tflags RCVD_IN_WPBL net
score RCVD_IN_WPBL 5.0
score BAYES_00 0.0
score BAYES_05 0.0
score SPF_PASS 0.0
score SPF_FAIL 6.0
score SPF_SOFTFAIL 1.0
score SPF_HELO_PASS 0.0
score SPF_HELO_FAIL 6.0
score SPF_HELO_SOFTFAIL 0.0
score LOTS_OF_MONEY 0.0
score RDNS_NONE 5.0
score URIBL_BLACK 6.5
score URIBL_DBL_SPAM 6.0
score RP_MATCHES_RCVD -0.001
score MISSING_HEADERS 3.0
score FH_FAKE_RCVD_LINE_B 2.0
score ADVANCE_FEE_3_NEW 1.0
score RCVD_IN_SORBS_SPAM 0.4
score FORGED_MUA_MOZILLA 0
score DKIM_VALID -0.5
score URI_TRY_3LD 0.0
score BITCOIN_BOMB 6.5
score BITCOIN_EXTORT_01 6.5
score BITCOIN_DEADLINE 6.5
score BITCOIN_MALWARE 6.5
score BITCOIN_PAY_ME 6.5
score BITCOIN_SPAM_07 6.5
score BITCOIN_SPAM_08 6.5
trusted_networks 192.168.0.0/16 10.0.0.0/8
internal_networks 192.168.0.0/16 10.0.0.0/8
Configuring Dovecot
So we'll be configuring Dovecot for POP3/POP3s, IMAP and SASL Auth. I only use IMAP for the local webmail so no need to waste encryption on it, only the local server can connect to it. There's a bunch of different files so just like with postfix I'll call out the file and what I changed in it.
/etc/dovecot/dovecot.conf:
protocols = imap pop3
listen = *
/etc/dovecot/conf.d/10-auth.conf:
disable_plaintext_auth = no # if you dont need this for legacy then PLEASE set to yes
auth_username_format = %Ln
auth_mechanisms = plain login
/etc/dovecot/conf.d/10-logging.conf:
auth_verbose = yes
auth_verbose_passwords = plain
auth_debug_passwords = yes
/etc/dovecot/conf.d/10-mail.conf:
mail_location = mbox:~:INBOX=/var/spool/mail/%u
mail_privileged_group = mail
first_valid_uid = 500
mbox_read_locks = dotlock
mbox_write_locks = dotlock
/etc/dovecot/conf.d/10-master.conf:
service imap-login {
inet_listener imap {
port = 143
}
service pop3-login {
inet_listener pop3 {
port = 110
}
inet_listener pop3s {
port = 995
ssl = yes
}
}
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}
/etc/dovecot/conf.d/10-ssl.conf:
ssl = yes
ssl_cert = </root/mail.example.com.fc.cer
ssl_key = </root/mail.example.com.key
ssl_dh = </etc/dovecot/dh.pem #Generate this file using 'openssl dhparam -out /etc/dovecot/dh.pem 4096'
ssl_min_protocol = TLSv1
ssl_prefer_server_ciphers = yes
/etc/dovecot/conf.d/15-mailboxes.conf:
auto = create # add to both mailbox Drafts, Trash and Sent
/etc/dovecot/conf.d/20-pop3.conf:
pop3_lock_session = no
pop3_uidl_format = %08Xv%08Xu
pop3_logout_format = del=%d/%m, size=%s
Configuring Postgrey
Greylisting was a great spam filtering tool when it came out. Now I'm not really sure how much of a difference it makes (other than slowing down the delivery of wanted emails). I'm leaving it configured, but it could be easily turned off in the /etc/postfix/main.cf file. There's just one file to change for postgrey:
/etc/sysconfig/postgrey:
POSTGREY_DELAY="--delay=59"
POSTGREY_OPTS="--lookup-by-subnet --auto-whitelist-clients=1"
Finishing Up
All that's left to do is adjust any firewall rules (at least you'll want to let port 25 and 110 through, probably also 465, 587 and 995). Then start all the services:
service clamav-freshclam start
service postgrey start
service amavisd start
service dovecot start
service postfix start
Assuming everything started ok then do some testing and if everything checks out just set the services to start automatically on boot (I like using ntsysv for that). MOST IMPORTANTLY: reboot the server after you've done all this and make sure that it still works and you didn't miss enabling auto start on anything!
-Nick