Postfix Tutorial
Contact | Services | LoginPostfix, Courier/POP, SASL & Spamassassin - with MySQL admin on Ubuntu LTS 20.04 Focal Fossa
1. DNS. Create an MX record in the primary zone file for the domains, with an A record pointing to the IP address of the mail server. You can proceed with the following installation and configuration, but your server will not test for your domain until your name server(s) are announcing DNS for it. Make sure you can see the A record IP address before testing the Postfix configuration:
dig mail.example-domain.com @name-server-for-example-domain.com
2. Install the following packages:
sudo apt install apache2 php mysql-server php libapache2-mod-php php-mysql postfix mailutils courier-pop courier-imap vim courier-authlib-mysql libsasl2-2 libsasl2-modules libsasl2-modules-sql postfix-mysql sasl2-bin libpam-mysql
3. Configure postfix - enter the following at the command line:
sudo postconf -e 'home_mailbox = Maildir/'
sudo postconf -e 'mailbox_command = '
sudo postconf -e 'mydestination = localhost.localdomain, localhost'
sudo postconf -e 'mynetworks = 127.0.0.0/8,'
sudo postconf -e 'inet_interfaces = all'
sudo postconf -e 'inet_protocols = all'
sudo postconf -e 'mailbox_command = '
sudo postconf -e 'mydestination = localhost.localdomain, localhost'
sudo postconf -e 'mynetworks = 127.0.0.0/8,
sudo postconf -e 'inet_interfaces = all'
sudo postconf -e 'inet_protocols = all'
4. Restart Apache2
/etc/init.d/apache2 reload
5. Configure phpMyAdmin - add this line to the end of /etc/apache2/apache2.conf:
Include /etc/phpmyadmin/apache.conf
… then access phpMyAdmin at :
http://mailserver-domain-name/phpmyadmin/
6. Create the MySQL database 'mail':
mysql -u root -p
CREATE DATABASE `mail` DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_0900_ai_ci;
USE `mail`;
7. Create the following tables on the MySQL database 'mail':
virtual_aliases
virtual_mailbox_domains
virtual_users
… using the following SQL (insert directly into phpMyAdmin):
-- ----------------------------------------------------------------
-- Table structure for table `virtual_aliases`
-- ----------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `virtual_aliases` (
`id` int(10) unsigned NOT NULL auto_increment,
`alias` varchar(255) collate utf8_unicode_ci default NULL,
`virtual_user_email` text collate utf8_unicode_ci,
PRIMARY KEY (`id`),
FULLTEXT KEY `aliases` (`alias`,`virtual_user_email`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Postfix virtual recipient aliases' AUTO_INCREMENT=1 ;
-- ----------------------------------------------------------------
-- Table structure for table `virtual_mailbox_domains`
-- ----------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `virtual_mailbox_domains` (
`id` int(10) unsigned NOT NULL auto_increment,
`domain` varchar(255) collate utf8_unicode_ci default NULL,
PRIMARY KEY (`id`),
FULLTEXT KEY `domains` (`domain`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Postfix virtual domains' AUTO_INCREMENT=1 ;
-- ----------------------------------------------------------------
-- Table structure for table `virtual_users`
-- ----------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `virtual_users` (
`id` int(11) unsigned NOT NULL auto_increment,
`username` varchar(255) collate utf8_unicode_ci NOT NULL default '0',
`userrealm` varchar(255) collate utf8_unicode_ci NOT NULL,
`userpassword` varchar(255) collate utf8_unicode_ci NOT NULL,
`auth` tinyint(1) default '1',
`active` tinyint(1) default '1',
`email` varchar(255) collate utf8_unicode_ci NOT NULL default '',
`virtual_uid` smallint(5) default '1007',
`virtual_gid` smallint(5) default '1007',
`virtual_mailbox` varchar(255) collate utf8_unicode_ci default NULL,
PRIMARY KEY (`id`),
FULLTEXT KEY `recipient` (`email`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='SMTP AUTH and virtual mailbox users' AUTO_INCREMENT=1 ;
8. Create MySQL User for Postfix. Run the SQL Query below (either from phpMyAdmin or Terminal), replacing
CREATE USER 'postfix'@'localhost' IDENTIFIED BY '';
GRANT USAGE ON * . * TO 'postfix'@'localhost'>';
GRANT SELECT, INSERT, UPDATE ,DELETE, CREATE, DROP, RELOAD, SHUTDOWN,
PROCESS, FILE, REFERENCES, INDEX, ALTER ON * . * TO 'postfix'@'localhost'>';
FLUSH PRIVILEGES;
GRANT USAGE ON * . * TO 'postfix'@'localhost'>';
GRANT SELECT, INSERT, UPDATE ,DELETE, CREATE, DROP, RELOAD, SHUTDOWN,
PROCESS, FILE, REFERENCES, INDEX, ALTER ON * . * TO 'postfix'@'localhost'>';
FLUSH PRIVILEGES;
9. Grant user privileges. The postfix user should not be able to alter or create tables, but should be restricted to read-only (SELECT) access. Run the SQL Query below (either from phpMyAdmin or Terminal), replacing
GRANT USAGE ON *.* TO 'postfix'@'localhost' IDENTIFIED BY PASSWORD
'';
GRANT SELECT ON mail.virtual_aliases TO 'postfix'@'localhost';
GRANT SELECT ON mail.virtual_users TO 'postfix'@'localhost';
GRANT SELECT ON mail.virtual_mailbox_domains TO 'postfix'@'localhost';
FLUSH PRIVILEGES;
GRANT SELECT ON mail.virtual_aliases TO 'postfix'@'localhost';
GRANT SELECT ON mail.virtual_users TO 'postfix'@'localhost';
GRANT SELECT ON mail.virtual_mailbox_domains TO 'postfix'@'localhost';
FLUSH PRIVILEGES;
10. Postfix Configuration Files. Create the /etc/postfix/sql directory, then create the following 6 files inside the sql directory, changing
a) Create the /etc/postfix/sql directory:
mkdir /etc/postfix/sql
b) Create the file sender_check.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_users
hosts = 127.0.0.1
query = SELECT email FROM virtual_users WHERE email ='%s' and active=1
c) Create the file virtual_alias_maps.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_aliases
select_field = virtual_user_email
where_field = alias
hosts = 127.0.0.1
d) Create the file virtual_gid_maps.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_users
select_field = virtual_gid
where_field = email
hosts = 127.0.0.1
e) Create the file virtual_mailbox_domains.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_mailbox_domains
select_field = domain
where_field = domain
hosts = 127.0.0.1
f) Create the file virtual_mailbox_recipients.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_users
select_field = virtual_mailbox
where_field = email
additional_conditions = and active = '1'
hosts = 127.0.0.1
g) Create the file virtual_uid_maps.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_users
select_field = virtual_uid
where_field = email
hosts = 127.0.0.1
h) Apply appropriate persmissions of the sql directory:
chown postfix /etc/postfix/sql
chgrp root /etc/postfix/sql
chmod 500 /etc/postfix/sql
mkdir /etc/postfix/sql
b) Create the file sender_check.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_users
hosts = 127.0.0.1
query = SELECT email FROM virtual_users WHERE email ='%s' and active=1
c) Create the file virtual_alias_maps.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_aliases
select_field = virtual_user_email
where_field = alias
hosts = 127.0.0.1
d) Create the file virtual_gid_maps.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_users
select_field = virtual_gid
where_field = email
hosts = 127.0.0.1
e) Create the file virtual_mailbox_domains.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_mailbox_domains
select_field = domain
where_field = domain
hosts = 127.0.0.1
f) Create the file virtual_mailbox_recipients.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_users
select_field = virtual_mailbox
where_field = email
additional_conditions = and active = '1'
hosts = 127.0.0.1
g) Create the file virtual_uid_maps.cf inside the sql directory, with the contents:
user = postfix
password =
dbname = mail
table = virtual_users
select_field = virtual_uid
where_field = email
hosts = 127.0.0.1
h) Apply appropriate persmissions of the sql directory:
chown postfix /etc/postfix/sql
chgrp root /etc/postfix/sql
chmod 500 /etc/postfix/sql
11. SASL Configuration. Create the /etc/postfix/sasl directory, and apply appropriate permissions to it:
a) Create the /etc/postfix/sasl directory if it does not already exist (otherwise change directory to it):
mkdir /etc/postfix/sasl
( cd /etc/postfix/sasl )
b) Create the smtpd.conf file inside the sasl directory, with the following contents:
log_level: 1
pwcheck_method: saslauthd
mech_list: plain login
allow_plaintext: true
#auxilary plugin properties
#auxprop_plugin: sql
#sql_engine: mysql
#sql_hostnames: 127.0.0.1
#sql_user: postfix
#sql_passwd:
#sql_database: mail
#sql_select: select userpassword as %p from virtual_users where username = '%u' AND active = 1
c) Apply appropriate permissions:
adduser postfix sasl
chmod -R g+wrx /etc/postfix/sasl/
mkdir /etc/postfix/sasl
( cd /etc/postfix/sasl )
b) Create the smtpd.conf file inside the sasl directory, with the following contents:
log_level: 1
pwcheck_method: saslauthd
mech_list: plain login
allow_plaintext: true
#auxilary plugin properties
#auxprop_plugin: sql
#sql_engine: mysql
#sql_hostnames: 127.0.0.1
#sql_user: postfix
#sql_passwd:
#sql_database: mail
#sql_select: select userpassword as %p from virtual_users where username = '%u' AND active = 1
c) Apply appropriate permissions:
adduser postfix sasl
chmod -R g+wrx /etc/postfix/sasl/
12. Configure authdaemonrc. Edit /etc/courier/authdaemonrc - and change: authmodulelist='
13. Configure authmysqlrc. Edit the /etc/courier/authmysqlrc and change to the following (Change the
MYSQL_SERVER 127.0.0.1
MYSQL_USERNAME postfix
MYSQL_PASSWORD
MYSQL_DATABASE mail
MYSQL_USER_TABLE virtual_users
MYSQL_LOGIN_FIELD email
#MYSQL_CRYPT_PWFIELD crypt
MYSQL_CLEAR_PWFIELD userpassword
MYSQL_MAILDIR_FIELD virtual_mailbox
MYSQL_HOME_FIELD '/var/spool/virtual_mailboxes'
MYSQL_UID_FIELD 1007
MYSQL_GID_FIELD 1007
Comment out MYSQL_CRYPT_PWFIELD and MYSQL_NAME_FIELD options
MYSQL_USERNAME postfix
MYSQL_PASSWORD
MYSQL_DATABASE mail
MYSQL_USER_TABLE virtual_users
MYSQL_LOGIN_FIELD email
#MYSQL_CRYPT_PWFIELD crypt
MYSQL_CLEAR_PWFIELD userpassword
MYSQL_MAILDIR_FIELD virtual_mailbox
MYSQL_HOME_FIELD '/var/spool/virtual_mailboxes'
MYSQL_UID_FIELD 1007
MYSQL_GID_FIELD 1007
Comment out MYSQL_CRYPT_PWFIELD and MYSQL_NAME_FIELD options
… there must be NO SPACES / TABS / ANYTHING at beginning or end of line.
This is how courier connects to mysql - it should effectively replicate the information in the /etc/postfix/sql/virtual_mailbox_recipients.cf file - mysql username, table and field names for mysql, etc.
14. Restart the daemons for changes to take effect:
/etc/init.d/courier-authdaemon restart
/etc/init.d/courier-pop restart
/etc/init.d/postfix restart
/etc/init.d/courier-pop restart
/etc/init.d/postfix restart
15. Enter MySQL Data. Enter you user data through phpMyAdmin (or alternative GUI) in the following format:
INSERT INTO `virtual_aliases` (`id`, `alias`, `virtual_user_email`) VALUES
('', 'postmaster@example-domain.com', 'forward-to-this-user@example-domain.com'),
('', 'abuse@example-domain.com', 'forward-to-this-user@example-domain.com');
INSERT INTO `virtual_mailbox_domains` (`id`, `domain`) VALUES
('', 'example-domain.com'),
('', 'another-domain.com');
INSERT INTO `virtual_users` (`id`, `username`, `userrealm`, `userpassword`, `auth`, `active`, `email`, `virtual_uid`, `virtual_gid`, `virtual_mailbox`) VALUES
('', 'username-1', 'mail.example-domain.com', 'password', 1, 1, 'username-1@example-domain.com', 1007, 1007, 'example-domain.com/username-1/'),
('', 'username-2', 'mail.another-domain.com', 'password', 1, 1, 'username-2@another-domain.com', 1007, 1007, 'anotherdomain.com/username-2/');
('', 'postmaster@example-domain.com', 'forward-to-this-user@example-domain.com'),
('', 'abuse@example-domain.com', 'forward-to-this-user@example-domain.com');
INSERT INTO `virtual_mailbox_domains` (`id`, `domain`) VALUES
('', 'example-domain.com'),
('', 'another-domain.com');
INSERT INTO `virtual_users` (`id`, `username`, `userrealm`, `userpassword`, `auth`, `active`, `email`, `virtual_uid`, `virtual_gid`, `virtual_mailbox`) VALUES
('', 'username-1', 'mail.example-domain.com', 'password', 1, 1, 'username-1@example-domain.com', 1007, 1007, 'example-domain.com/username-1/'),
('', 'username-2', 'mail.another-domain.com', 'password', 1, 1, 'username-2@another-domain.com', 1007, 1007, 'anotherdomain.com/username-2/');
16. Configure /etc/postfix/main.cf :
# MySQL Setup for Virtual Mailbox Domains as per postfix-book.com
virtual_mailbox_base = /var/spool/virtual_mailboxes
virtual_mailbox_domains = mysql:/etc/postfix/sql/virtual_mailbox_domains.cf
virtual_uid_maps = mysql:/etc/postfix/sql/virtual_uid_maps.cf
virtual_gid_maps = mysql:/etc/postfix/sql/virtual_gid_maps.cf
virtual_mailbox_maps = mysql:/etc/postfix/sql/virtual_mailbox_recipients.cf
virtual_alias_maps = mysql:/etc/postfix/sql/virtual_alias_maps.cf
# SASL authentication
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_authenticated_header = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =
virtual_mailbox_base = /var/spool/virtual_mailboxes
virtual_mailbox_domains = mysql:/etc/postfix/sql/virtual_mailbox_domains.cf
virtual_uid_maps = mysql:/etc/postfix/sql/virtual_uid_maps.cf
virtual_gid_maps = mysql:/etc/postfix/sql/virtual_gid_maps.cf
virtual_mailbox_maps = mysql:/etc/postfix/sql/virtual_mailbox_recipients.cf
virtual_alias_maps = mysql:/etc/postfix/sql/virtual_alias_maps.cf
# SASL authentication
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_authenticated_header = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =
17. Create the file: /etc/pam.d/smtp … with the contents:
auth required pam_mysql.so user=postfix passwd= host=127.0.0.1 db=mail table=virtual_users usercolumn=email passwdcolumn=userpassword crypt=0 verbose=1
account sufficient pam_mysql.so user=postfix passwd= host=127.0.0.1 db=mail table=virtual_users usercolumn=email passwdcolumn=userpassword crypt=0 verbose=1
account sufficient pam_mysql.so user=postfix passwd=
18. Create the file : /etc/default/saslauthd ... with the contents:
START=yes
MECHANISMS='pam'
MECH_OPTIONS=''
THREADS=5
OPTIONS='-c -m /var/spool/postfix/var/run/saslauthd -r'
MECHANISMS='pam'
MECH_OPTIONS=''
THREADS=5
OPTIONS='-c -m /var/spool/postfix/var/run/saslauthd -r'
19. Create the directories :
mkdir -p /var/spool/postfix/var/run/saslauthd/
mkdir /var/spool/virtual_mailboxes
mkdir /var/spool/virtual_mailboxes
20. Add /etc/passwd to vuser:x:1007:1008::/home/vuser:/bin/sh
groupadd vuser -g 1008
useradd -m vuser -u 1007 -g 1008
chown -R vuser: /var/spool/virtual_mailboxes/
useradd -m vuser -u 1007 -g 1008
chown -R vuser: /var/spool/virtual_mailboxes/
21. DomainKeys Identified Mail (DKIM)
Install OpenDKIM (which replaced the dkim-filter package in Ubuntu as of 2011-12-13):
sudo apt install opendkim
Configure opendkim using instructions from http://opendkim.org/opendkim-README
22. Create restartmail
vi ~/.bashrc
alias restartmail='/etc/init.d/courier-authdaemon restart; /etc/init.d/courier-pop restart; /etc/init.d/postfix restart; /etc/init.d/saslauthd restart'
alias restartmail='/etc/init.d/courier-authdaemon restart; /etc/init.d/courier-pop restart; /etc/init.d/postfix restart; /etc/init.d/saslauthd restart'
Then restart the various daemons using restartmail as a handy alias to restart courier, postfix, and saslauthd
23. Test. Insert your domains, users and aliases into the MySQL tables. You will need to send a message to each user account, before the account will work, as this is what creates the /var/spool/ directory for that user. Also, make sure on your email client that you enter 'username@example-domain.com' as the username and not just 'username'.
If you have any problems, try increasing logging to a higher, more verbose level, and then check the error logs.
Add -v to 'smtpd' in /etc/postfix/master.cf
for verbose logging.
/etc/postfix/sasl/smtpd.conf log_level: 5
verbose courier debugging: /etc/courier/authdaemonrc DEBUG_LOGIN=2
Running the saslauthd as a foreground process attached to the terminal (so you get all login attempts displayed directly to terminal): saslauthd -d -a pam -c -m /var/spool/postfix/var/run/saslauthd -r
To test an account: testsaslauthd -s smtp -f /var/spool/postfix/var/run/saslauthd/mux -u -p
/etc/postfix/sasl/smtpd.conf log_level: 5
verbose courier debugging: /etc/courier/authdaemonrc DEBUG_LOGIN=2
Running the saslauthd as a foreground process attached to the terminal (so you get all login attempts displayed directly to terminal): saslauthd -d -a pam -c -m /var/spool/postfix/var/run/saslauthd -r
To test an account: testsaslauthd -s smtp -f /var/spool/postfix/var/run/saslauthd/mux -u
24. Spamassassin. Once you have Postfix up and running, as an option you can filter spam using this configuration of Spamassassin.
a) Install the packages:
sudo apt-get install spamassassin spamc
b) Create a specific user and group for spamassassin:
groupadd -g 5001 spamd
useradd -u 5001 -g spamd -s /sbin/nologin -d /var/lib/spamassassin spamd
mkdir /var/lib/spamassassin
chown spamd:spamd /var/lib/spamassassin
c) Configure /etc/default/spamassassin
ENABLED=1
SAHOME='/var/lib/spamassassin/'
OPTIONS='--create-prefs --max-children 5 --username spamd --helper-home-dir ${SAHOME} -s ${SAHOME}spamd.log'
PIDFILE='${SAHOME}spamd.pid'
d) Configure /etc/spamassassin/local.cf
rewrite_header Subject [***** SPAM _SCORE_ *****]
required_score 2.0
#to be able to use _SCORE_ we need report_safe set to 0
#If this option is set to 0, incoming spam is only modified by adding some 'X-Spam-' headers and no changes will be made to the body.
report_safe 0
# Enable the Bayes system
use_bayes 1
use_bayes_rules 1
# Enable Bayes auto-learning
bayes_auto_learn 1
# Enable or disable network checks
skip_rbl_checks 0
use_razor2 0
use_dcc 0
use_pyzor 0
e) Start spamassassin with /etc/init.d/spamassassin start
f) Configure Postfix /etc/postfix/master.cf
Change the line:
smtp inet n - - - - smtpd
… to …
smtp inet n - - - - smtpd
-o content_filter=spamassassin
g) Add the following at the end of /etc/init.d/spamassassin start
spamassassin unix - n n - - pipe
user=spamd argv=/usr/bin/spamc -f -e
/usr/sbin/sendmail -oi -f ${sender} ${recipient}
h) Reload Postfix with restartmail
sudo apt-get install spamassassin spamc
b) Create a specific user and group for spamassassin:
groupadd -g 5001 spamd
useradd -u 5001 -g spamd -s /sbin/nologin -d /var/lib/spamassassin spamd
mkdir /var/lib/spamassassin
chown spamd:spamd /var/lib/spamassassin
c) Configure /etc/default/spamassassin
ENABLED=1
SAHOME='/var/lib/spamassassin/'
OPTIONS='--create-prefs --max-children 5 --username spamd --helper-home-dir ${SAHOME} -s ${SAHOME}spamd.log'
PIDFILE='${SAHOME}spamd.pid'
d) Configure /etc/spamassassin/local.cf
rewrite_header Subject [***** SPAM _SCORE_ *****]
required_score 2.0
#to be able to use _SCORE_ we need report_safe set to 0
#If this option is set to 0, incoming spam is only modified by adding some 'X-Spam-' headers and no changes will be made to the body.
report_safe 0
# Enable the Bayes system
use_bayes 1
use_bayes_rules 1
# Enable Bayes auto-learning
bayes_auto_learn 1
# Enable or disable network checks
skip_rbl_checks 0
use_razor2 0
use_dcc 0
use_pyzor 0
e) Start spamassassin with /etc/init.d/spamassassin start
f) Configure Postfix /etc/postfix/master.cf
Change the line:
smtp inet n - - - - smtpd
… to …
smtp inet n - - - - smtpd
-o content_filter=spamassassin
g) Add the following at the end of /etc/init.d/spamassassin start
spamassassin unix - n n - - pipe
user=spamd argv=/usr/bin/spamc -f -e
/usr/sbin/sendmail -oi -f ${sender} ${recipient}
h) Reload Postfix with restartmail
References
https://help.ubuntu.com/community/PostfixBasicSetupHowtohttp://www.postfix.org/STANDARD_CONFIGURATION_README.html#backup
http://www.debuntu.org/postfix-and-pamassassin-how-to-filter-spam
Powered by Embode