Skip to content

Home Lab

Notes from my learning sessions

Menu
Menu

Configuring secure mail server using Postfix with Dovecot, Amavis, Spamassasin, Postgrey and OpenDKIM

Posted on January 11, 2020 by sandeep

Prerequisite : https://www.sandeeprao.net/preparing-for-configuring-secure-mail-server-using-postfix-dovecot-spamassassin-and-opendkim/

Install required packages

#apt install -y razor
#apt install -y amavisd-new dovecot-core dovecot-imapd dovecot-lmtpd dovecot-mysql dovecot-sieve haveged mailutils mariadb-server opendkim opendkim-tools p7zip postfix postfix-mysql postgrey spamassassin

Create require vmail user and group

#groupadd -g 5000 vmail
#useradd -g vmail -u 5000 vmail -d /var/vmail

Create mailbox directory

#mkdir -p /var/vmail 
#chown -R vmail:vmail /var/vmail

Secure mariadb (set password for root user, default answers) and create postfix database, user table and aliases table

root@vm:~# mysql_secure_installation
...
Set root password? [Y/n] Y
New password: 
Re-enter new password: 
Password updated successfully!
...
Remove anonymous users? [Y/n] Y
...
Disallow root login remotely? [Y/n] Y
...
Remove test database and access to it? [Y/n] Y
...
Reload privilege tables now? [Y/n] Y
 … Success!
Cleaning up…
All done!  If you've completed all of the above steps, your MariaDB
installation should now be secure.
Thanks for using MariaDB!


root@vm:~# mysql -uroot -p
Enter password: 
...
...
MariaDB [(none)]> CREATE DATABASE postfix;
Query OK, 1 row affected (0.000 sec)
MariaDB [(none)]> GRANT SELECT ON postfix.* TO postfix@127.0.0.1 IDENTIFIED BY 'postfix';
Query OK, 0 rows affected (0.000 sec)
MariaDB [(none)]> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.000 sec)
MariaDB [(none)]> USE postfix;
Database changed
MariaDB [postfix]> CREATE TABLE domains (
     ->   id int(11) NOT NULL auto_increment,
     ->   domain varchar(50) NOT NULL,
     ->   PRIMARY KEY (id)
     -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.013 sec)
MariaDB [postfix]> CREATE TABLE users (
     ->   id int(11) NOT NULL auto_increment,
     ->   domain int(11) NOT NULL,
     ->   password varchar(106) NOT NULL,
     ->   email varchar(100) NOT NULL,
     ->   PRIMARY KEY (id),
     ->   UNIQUE KEY email (email),
     ->   FOREIGN KEY (domain) REFERENCES domains(id) ON DELETE CASCADE
     -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.009 sec)
MariaDB [postfix]> CREATE TABLE aliases (
     ->   id int(11) NOT NULL auto_increment,
     ->   domain int(11) NOT NULL,
     ->   source varchar(100) NOT NULL,
     ->   destination varchar(100) NOT NULL,
     ->   PRIMARY KEY (id),
     ->   FOREIGN KEY (domain) REFERENCES domains(id) ON DELETE CASCADE
     -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.008 sec)
MariaDB [postfix]> exit
Bye
root@vm:~#

Update postfix configurations. Edit /etc/postfix/master.cf . I understand that smtpd_delay_reject is required for the combinations of restrictions I had applied.

submission inet n       -       y      -       -       smtpd
   -o smtpd_tls_security_level=encrypt
   -o content_filter=
amavis           unix    -       -       n       -       2       smtp
    -o smtp_send_xforward_command=yes
    -o smtp_tls_security_level=none
127.0.0.1:10025  inet    n       -       n       -       -       smtpd
   -o content_filter=

Update /etc/postfix/main.cf – Comment out all configurations and add the following. Note: 10.1.1.0 and 192.168.100.0 are two network subnets from where I connect to my server (mynetworks configuration – change it matching yours). Since this is public internet facing server, did not add smtpd_use_tls and smtp_use_tls.

Edit /etc/postfix/main.cf (Final contents of edited file below in my case – except for change in domain name)

#Retained
biff = no
append_dot_mydomain = no
readme_directory = no
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.1.2.0/24 10.1.3.0/24 10.1.4.0/24 10.1.5.0/24 192.168.100.0/24
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated permit_auth_destination defer_unauth_destination

#Added or modified existing
smtp_bind_address = 0.0.0.0
alias_maps = hash:/etc/aliases
compatibility_level = 2
mailbox_size_limit = 0
myhostname = mail.example.com
recipient_delimiter = +
inet_protocols = ipv4
smtpd_banner = $myhostname ESMTP
content_filter = amavis:[127.0.0.1]:10024
message_size_limit = 20480000
non_smtpd_milters = inet:[127.0.0.1]:12301
smtpd_milters=inet:[127.0.0.1]:12301
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_invalid_hostname, reject_non_fqdn_hostname, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_sender_domain, r$
smtpd_sasl_auth_enable = yes
smtpd_sasl_path = private/auth
smtpd_sasl_type = dovecot
smtpd_tls_CApath = /etc/ssl/certs
smtpd_tls_cert_file = /etc/letsencrypt/live/example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/example.com/privkey.pem
smtpd_tls_eecdh_grade = strong
smtpd_tls_security_level = may
smtp_header_checks = regexp:/etc/postfix/header_checks
smtp_mime_header_checks = regexp:/etc/postfix/header_checks
smtp_tls_CApath = /etc/ssl/certs
smtp_tls_cert_file = $smtpd_tls_cert_file
smtp_tls_key_file = $smtpd_tls_key_file
smtp_tls_security_level = may
smtp_use_tls = yes
tls_preempt_cipherlist = yes
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_emails.cf, mysql:/etc/postfix/mysql_virtual_alias_maps.cf
virtual_gid_maps = static:5000
virtual_mailbox_base = /var/vmail
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_mailbox_domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_uid_maps = static:5000

Update Mariadb credentials for the postfix operations

Edit /etc/postfix/mysql_virtual_mailbox_domains.cf

user = postfix 
password = postfix 
hosts = 127.0.0.1 
dbname = postfix 
query = SELECT 1 FROM domains WHERE domain='%s'

Edit /etc/postfix/mysql_virtual_mailbox_maps.cf

user = postfix 
password = postfix 
hosts = 127.0.0.1 
dbname = postfix 
query = SELECT 1 FROM users WHERE email='%s'

Edit /etc/postfix/mysql_virtual_alias_maps.cf

user = postfix
password = postfix
hosts = 127.0.0.1
dbname = postfix
query = SELECT destination FROM aliases WHERE source='%s'

Edit /etc/postfix/mysql_virtual_alias_emails.cf

user = postfix
password = postfix
hosts = 127.0.0.1
dbname = postfix
query = SELECT email FROM users WHERE email='%s'

Prevent outgoing mails from leaking privacy related information like internal IP addresses or the mail client’s name.

Edit /etc/postfix/header_checks

/^Received:.*with ESMTP/        IGNORE
/^X-Mailer:/                    IGNORE
/^User-Agent:/                  IGNORE
/^Mime-Version:/                IGNORE

Generate Postfix map

postmap /etc/postfix/header_checks

Generate alias map

newaliases

Restart Postfix service

systemctl restart postfix

Update Dovecot configuration – Add the following at the end of configuraiton file

listen = *
mail_location = maildir:/var/vmail/%d/%n/
protocols = imap lmtp
ssl = required
ssl_cert = < /etc/letsencrypt/live/example.com/fullchain.pem
ssl_key = < /etc/letsencrypt/live/example.com/privkey.pem
ssl_prefer_server_ciphers = yes

namespace inbox {
  inbox = yes
  location =
  separator = /

  mailbox Drafts {
    auto = subscribe
    special_use = \Drafts
  }

  mailbox "Sent Messages" {
    auto = subscribe
    special_use = \Sent
  }

  mailbox Junk {
    auto = subscribe
    special_use = \Junk
  }

  mailbox "Deleted Messages" {
    auto = subscribe
    special_use = \Trash
  }

  mailbox Archive {
    auto = subscribe
    special_use = \Archive
  }

  mailbox Notes {
    auto = subscribe
  }
}

passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}

userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/vmail/%d/%n
}

service auth {
  unix_listener /var/spool/postfix/private/auth {
    group = postfix
    mode = 0660
    user = postfix
  }

  unix_listener auth-userdb {
    mode = 0600
    user = vmail
  }

  user = dovecot
}

service auth-worker {
  user = vmail
}

service imap-login {
  inet_listener imap {
    port = 0
  }
  inet_listener imaps {
    port = 993
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    group = postfix
    mode = 0600
    user = postfix
  }
}

protocol lmtp {
  postmaster_address = postmaster@example.com
  hostname = example.com
  mail_plugins = sieve
}

plugin {
  sieve_before = /etc/dovecot/spam.sieve
}

Update Mariadb configurations :

Edit /etc/dovecot/dovecot-sql.conf.ext

driver = mysql
connect = host=127.0.0.1 dbname=postfix user=postfix password=postfix
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM users WHERE email='%u';

Edit /etc/dovecot/spam.sieve

require ["fileinto", "imap4flags"];

if header :contains "X-Spam-Flag" "YES" {
  setflag "\\Seen";
  fileinto "Junk";
  stop;
}

Compile Sieve spam rules

sievec /etc/dovecot/spam.sieve

Restart Dovecot service

systemctl restart dovecot

Antispam configurations :

Update Greylisting whitelist

wget -O /etc/postgrey/whitelist_clients https://raw.githubusercontent.com/schweikert/postgrey/master/postgrey_whitelist_clients

Restart post-grey service

systemctl restart postgrey

Edit /etc/opendkim.conf (comment out all existing configurations and add the following at the end of configuraiton file)

AutoRestart             Yes
AutoRestartRate         10/1h
UMask                   002
Syslog                  yes
SyslogSuccess           Yes
LogWhy                  Yes

Canonicalization        relaxed/simple

ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts           refile:/etc/opendkim/TrustedHosts
KeyTable                refile:/etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable

Mode                    sv
PidFile                 /var/run/opendkim/opendkim.pid
SignatureAlgorithm      rsa-sha256

UserID                  opendkim:opendkim
Socket                  inet:12301@localhost

Create OpenDKIM config directory and

mkdir -p /etc/opendkim

Update /etc/opendkim/TrustedHosts file

127.0.0.1
localhost

Edit /etc/opendkim/KeyTable Note : mail2019 is the selector opted by me, it can be any string, ensure that you use the same in all places.

mail2019._domainkey.example.com  example.com:mail2019:/etc/opendkim/keys/example.com/mail2019.private

Edit /etc/opendkim/SigningTable

*@example.com mail2019._domainkey.example.com

Create Domain Key directory

mkdir -p /etc/opendkim/keys/example.com

Generate Domain Key

opendkim-genkey -s mail2019 -d example.com -D /etc/opendkim/keys/example.com

Set permissions on Domain Key

chown opendkim:opendkim /etc/opendkim/keys/example.com/mail2019.private
chmod 0400 /etc/opendkim/keys/example.com/mail2019.private

Restart OpenDKIM service

systemctl restart opendkim

Get DKIM record from /etc/opendkim/keys/example.com/mail2019.txt and create a new DNS record from it (TXT Entry – in DNS management)

Host = mail2019_.domainkey
TXT value =  (starting from "v=" and upto "p=xxxxxx" )

Add amavis user to vmail group

usermod -aG vmail amavis

Edit /etc/amavis/conf.d/50-user Note : Yet to understand the spam scoring and related configuration. With -999 value for sa_tag_level_deflt almost all legit mails were marked as spam and moved to Spam folder. So with some little understanding have put values here for my current needs. It may not be recommended, but satisfies my needs – Check for the specific values your requirement needs.

use strict;

$smtp_connection_cache_on_demand = 0;
$smtp_connection_cache_enable = 0;

@bypass_spam_checks_maps = (
   \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);

$sa_tag_level_deflt  = 2.9;
$sa_tag2_level_deflt = 2.9;
$sa_kill_level_deflt = 5.0;
$sa_dsn_cutoff_level = 2.9;
$final_banned_destiny     = D_BOUNCE; 
$final_spam_destiny       = D_PASS;   
$final_bad_header_destiny = D_BOUNCE; 

$undecipherable_subject_tag=undef;
@lookup_sql_dsn = (
  ['DBI:mysql:database=postfix;host=127.0.0.1;port=3306',
   'postfix',
   'postfix']);

$sql_select_policy = 'SELECT domain FROM domains WHERE CONCAT("@",domain) IN (%k)';

1;

Restart Amavis service

systemctl restart amavis

Configure Spamassasin – download and extract samples (Note the URL – last part file name is dynamic – check before you use it).

cd /tmp
wget http://untroubled.org/spam/2019-08.7z
p7zip -d 2019-08.7z
chown -R amavis:amavis 2019/
cd -

Make Spamassassin learn from spam samples to populate the local ham/spam database

su - amavis -c 'sa-learn --progress --spam /tmp/2019/'

Add cronjobs for Spamassassin (run crontab -e)

@hourly su amavis -c 'sa-learn --ham /var/vmail/*/*/cur/'
@hourly su amavis -c 'sa-learn --spam /var/vmail/*/*/.Junk/cur/'

Initialize Razor

su - amavis -c 'razor-admin -create'
su - amavis -c 'razor-admin -register'
su - amavis -c 'razor-admin -discover'

Domains, Mailboxes & Aliases

Create domain mailbox directory

su vmail -c 'mkdir -p -m 0770 /var/vmail/example.com'

Add domain entry to MySQL database [For now this mail server will serve for one domain only 🙂 ]

INSERT INTO `postfix`.`domains` (`id` ,`domain`) VALUES ('1', 'example.com');

Add mailbox entry to MySQL database [Create users as you need – For me for now only one is required]

INSERT INTO `postfix`.`users` (`id`, `domain`, `password` , `email`) VALUES ('1', '1', ENCRYPT('<PASSWORD>', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), 'admin@example.com');

Initialize mailbox directory (optional)

doveadm mailbox create -u admin@example.com INBOX

Configurations done – Secure mail server is up and running. Use the following information for client side configurations

IMAP: Server: datachronicles.net
Port: 993
Encryption: SSL/TLS

SMTP:Server: datachronicles.net
Port: 587
Encryption: STARTTLS

1 thought on “Configuring secure mail server using Postfix with Dovecot, Amavis, Spamassasin, Postgrey and OpenDKIM”

  1. Pingback: Adding ClamAV Anti-virus checks to existing Postfix, Amavis+Spamassasin configuration – Online Notes

Comments are closed.

Recent Posts

  • Openstack Xena on Ubuntu 20.04 – Cinder
  • Preparing custom Debian 11 MATE image
  • Setup Ubuntu 20.04 repository mirror server
  • Preparing custom Debian 11 server cloud image
  • Complile Linux Kernel (on Debian 11)
  • Openstack Xena – Test Home Lab
  • Openstack Xena on Ubuntu 20.04 – Horizon
  • Openstack Xena on Ubuntu 20.04 -Home Lab
  • Openstack Xena on Ubuntu 20.04 – Neutron
  • Openstack Xena on Ubuntu 20.04 – Nova

Archives

  • April 2022
  • March 2022
  • February 2022
  • December 2021
  • October 2021
  • September 2021
  • October 2020
  • February 2020
  • January 2020
  • December 2019
© 2023 Home Lab | Powered by Minimalist Blog WordPress Theme