Installing Postfix on Fedora using MySQL

This article is written with the goal of building an email server, that will receive mail for users with virtual email accounts stored in a MySQL database, for multiple domains. Allow you to filter your mail with SpamAssassin and other filtering tools. Also relay all outbound email threw your ISP's SMTP server using your ISP username and password. And allow your users to use both IMAP or POP3 or webmail to read there email.

Besides setting up postfix you also need to make sure your firewall is open for port 25 in order to receive emails.

 This How-To was build with these specs.

''This is my notes on installing and setting up my mail server. Any usernames or passwords are not valid on my network.''

Installing Postfix with MySQL Support
MySQL support is NOT built into Fedora's Postfix RPM's, because of this, we ether need to build them ourselves (mine are on my yum repo) or grab them from atrpms.com.

Installing from Source
See:  Installing Postfix from Source

Configuring Postfix
mkdir /var/spool/virtualmailboxes/ echo "virtualmail:x:1000:1000::/var/spool/virtualmailboxes:/sbin/nologin" >> /etc/passwd echo "virtualmail:x:1000:" >> /etc/group chmod 700 /var/spool/virtualmailboxes/ chown -R virtualmail:virtualmail /var/spool/virtualmailboxes/

Postfix main.cf file
/etc/postfix/main.cf

These first options are mostly the defaults, but you should confirm they are correct. queue_directory = /var/spool/postfix command_directory = /usr/sbin daemon_directory = /usr/libexec/postfix The alias_maps refers to local accounts only. forward the root account to one of your virtual address and postmap the file postmap /etc/postfix/aliases alias_maps = hash:${config_directory}/aliases postfix is the default and the best answer. mail_owner = postfix This is the HELO/EHLO answer, make this your hosts full qualified domain name. myhostname = mail.example.com Your hosts domain name. mydomain = example.com The origin of this servers locally outbound domain name. myorigin = $mydomain What ethernet interfaces will be allowed to except email. inet_interfaces = all mydestination is for excepting email for the local agent. the following will not allow receiving of email for this domain, that will be covered by the virtual domain setup. mydestination = $myhostname, localhost.$mydomain, localhost local_recipient_map is the lookup tables with all addresses of local recipients (not virtual users). local_recipient_maps = $alias_maps message_size_limit (default: 10240000) in bytes, remember to give space for the messages headers also. message_size_limit = 32505856 Set the mynetworks_style to subnet so the mynetworks parameter will allow local "trusted" network users to uses the outbound smtpd function without logging in. mynetworks_style = subnet The list of "trusted" SMTP clients that have more privileges than "strangers". mynetworks = 192.168.1.0/24, 127.0.0.0/8 Reject messages that are not from vailed local/virtual senders (Not in alias/virtual_alias table) smtpd_reject_unlisted_sender = yes This is the section that allows you to block emails based on there connection info, the following options are safe. smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination, reject_rbl_client zen.spamhaus.org Now we need to setup the MySQL access files, the following files do not yet exist so once were done with this file we will need to create and populate them (see below). relay_domains = mysql:${config_directory}/mysql_relay_domains_maps.cf virtual_alias_maps = mysql:${config_directory}/mysql_virtual_alias_maps.cf virtual_mailbox_domains = mysql:${config_directory}/mysql_virtual_domains_maps.cf virtual_mailbox_maps = mysql:${config_directory}/mysql_virtual_mailbox_maps.cf virtual_mailbox_limit = mysql:${config_directory}/mysql_virtual_mailbox_limit_maps.cf smtpd_sender_login_maps = mysql:${config_directory}/mysql_virtual_sender_maps.cf This is the location of your virtual mailboxes, this directory needs to be owned by virtual_uid below. virtual_mailbox_base = /var/spool/virtualmailboxes The virtual uid and virtual gid is the user and group that will deliver the mail to the virtual_mailbox_base directory. virtual_minimum_uid= 1000 virtual_uid_maps = static:1000 virtual_gid_maps = static:1000
 * 1) Max Message Size 32505856 = 31 Mbytes

And that finishes the /etc/postfix/main.cf config file.

Postfix Add On Config files
Now you need to create the following files, these file allow postfix to read from the MySQL table. /etc/postfix/mysql_relay_domains_maps.cf user = postfix password = postfix hosts = localhost dbname = postfix query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '1' and active = '1' /etc/postfix/mysql_virtual_alias_maps.cf user = postfix password = postfix hosts = localhost dbname = postfix query = SELECT goto FROM alias WHERE address='%s' AND active = 1 /etc/postfix/mysql_virtual_domains_maps.cf user = postfix password = postfix hosts = localhost dbname = postfix query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '0' and active = '1' /etc/postfix/mysql_virtual_mailbox_maps.cf user = postfix password = postfix hosts = localhost dbname = postfix query = SELECT maildir FROM mailbox WHERE username='%s' AND active = 1 /etc/postfix/mysql_virtual_sender_maps.cf user = postfix password = postfix hosts = localhost dbname = postfix query = SELECT goto FROM alias WHERE address='%s' AND active = 1

Building the BerkeleyDB Database files
Now we need to build some of the database files required from our setup above. /usr/bin/newaliases /usr/sbin/postmap /etc/postfix/sasl_passwd

Creating MySQL Databases and Permissions for Postfix
First we need to create the databases for Postfix.

As root run the following mysqladmin create postfix Now we need to create a user to use with Postfix. mysql grant all on postfix.* to postfix@"localhost" identified by "postfix"; Assuming both those commands finished successfully you can exit mysql. exit You have now added the needed databases and user and gave then the correct permissions. We will not add the Postfix tables now, since PostfixAdmin will add them during the set-up process.

TLS Support
For TLS Support add the following to /etc/postfix/main.cf. smtpd_tls_CAfile = /etc/postfix/cacert.pem smtpd_tls_cert_file = /etc/postfix/smtpd.pem smtpd_tls_key_file = /etc/postfix/postfix-key.pem smtpd_tls_loglevel = 1 smtpd_tls_security_level = may If you have a pre-setup pem key this should work with you only needing to change the smtpd_tls_cert_file parameter. If you don't have a pre-setup key then look at the TLS page for setup information.

SASL Authentication for outbound email
(notice it's smtp) The next-hop destination of non-local (to this server) mail. If your ISP requires you to use one of there relay servers, place it here. relayhost = [smtp.comcast.net]:587 smtp_sasl_auth = yes smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd And your ISP's username and password in /etc/postfix/sasl_passwd  :

PostfixAdmin
PostfixAdmin will allow you to add, delete and edit mailboxes and full domains. This web based app access you MySQL config for postfix and edits it directly.

Using Apache
yum install httpd mysql mysql-server php php-mysql php-mbstring subversion Now setup the programs to start up when the system start then start them. /sbin/chkconfig httpd on /sbin/chkconfig mysqld on /sbin/service httpd start /sbin/service mysqld start

Using Lighttpd
yum install lighttpd lighttpd-fastcgi mysql mysql-server php-cli php-imap php-mysql php-mbstring subversion To use Lighttpd you need to configure your lighttpd.conf file, uncomment the following lines. fastcgi.server            = ( ".php" =>                                ( "localhost" => (                                   "socket" => "/var/run/lighttpd/php-fastcgi.socket",                                    "bin-path" => "/usr/bin/php-cgi"                                  ) )                            ) Now setup the programs to run properly /sbin/chkconfig lighttpd on /sbin/chkconfig mysqld on /sbin/service lighttpd start /sbin/service mysqld start
 * /etc/lighttpd/lighttpd.conf

PostfixAdmin Download and Install
We will be using the Subversion version of Postfix Admin. First you need to deside where you would like your Postfix Admin install to live, mine will live under apache's root dirctory

With Apache
cd /var/www/html/ svn co https://postfixadmin.svn.sourceforge.net/svnroot/postfixadmin/trunk postfixadmin cd postfixadmin

With Lighttpd
cd /var/www/lighttpd/ svn co https://postfixadmin.svn.sourceforge.net/svnroot/postfixadmin/trunk postfixadmin cd postfixadmin Now you have the current version of Postfix Admin.

PostfixAdmin Config File

 * see: PostfixAdmin
 * /var/www/html/postfixadmin/config.inc.php or /var/www/lighttpd/postfixadmin/config.inc.php

General Options

TFirst thing to do is to tell postfix that you have configure the app. $CONF['configured'] = true; The full url of your postfix admin build $CONF['postfix_admin_url'] = 'https://mail.example.com/postfixadmin';

Database Setup

Next you need to setup your database connection. Since I'm using MySQL this is my setup. I think the setup is pretty self explanatory. $CONF['database_type'] = 'mysql'; $CONF['database_host'] = 'localhost'; $CONF['database_user'] = 'postfix'; $CONF['database_password'] = 'postfix'; $CONF['database_name'] = 'postfix'; $CONF['database_prefix'] = ''; The email address that all problems will be reported to and the sender of site wide messages. $CONF['admin_email'] = 'postmaster@example.com'; The location and port of the SMTP server for site messages. $CONF['smtp_server'] = 'localhost'; $CONF['smtp_port'] = '25'; In what way do you want the passwords to be crypted? $CONF['encrypt'] = 'md5'; Minimum length required for passwords. (Admins can set shorter passwords but users can't) $CONF['min_password_length'] = 7; The default aliases that need to be created for all domains. $CONF['default_aliases'] = array (    'postmaster' => 'postmaster@example.com', );

Mailbox Setup

If you want to store the mailboxes per domain set this to 'YES'. (Example: /usr/local/virtual/example.com/username@example.com $CONF['domain_path'] = 'YES'; If you don't want to have the domain in your mailbox set this to 'NO'. (Example: /usr/local/virtual/example.com/username) $CONF['domain_in_mailbox'] = 'YES'; Default Domain Values. Specify your default values below. Quota in MB. $CONF['aliases'] = '0'; $CONF['mailboxes'] = '0'; $CONF['maxquota'] = '512';

Quota Setup

If you wish to enforce quota for your mailbox users set this to 'YES'. $CONF['quota'] = 'YES'; // You can either use '1024000' or '1048576' $CONF['quota_multiplier'] = '1024000';

Transport Setup

If you want to define additional transport options for a domain set this to 'YES'. $CONF['transport'] = 'YES'; If you want to define additional transport options put them in array below. $CONF['transport_options'] = array (    'virtual',  // for virtual accounts     'local',    // for system accounts     'relay'     // for backup mx ); Transport default $CONF['transport_default'] = 'virtual';

Alias Control

Postfix Admin inserts an alias in the alias table for every mailbox it creates. The reason for this is that when you want catch-all and normal mailboxes to work you need to have the mailbox replicated in the alias table. If you want to take control of these aliases as well set this to 'YES'. $CONF['alias_control'] = 'YES';

Alias Control for admins. $CONF['alias_control_admin'] = 'YES';

Welcome Message

This message is send to every newly created mailbox. $CONF['welcome_text'] = <<<EOM Hi, Welcome to your new account. EOM; Misc Options

When creating mailboxes, check that the domain-part of the address is legal by performing a name server look-up. $CONF['emailcheck_resolve_domain']='YES';

PostfixAdmin Webbased Setup
Next go to the URL that you have set in PostfixAdmin, and go to the setup page. http://mail.example.com/postfixadmin/setup.php This page will setup the database for you and also test your system to confirm its all running correctly.

Installing Dovecot
First you need to download the lastest dovecot release from http://dovecot.org/download.html and untar it. wget http://dovecot.org/releases/1.1/dovecot-1.1.1.tar.gz tar -xzf dovecot-1.1.1.tar.gz cd dovecot-1.1.1 To setup the environment (the "--enable-header-install" option is for Dovecot-SASL). ./configure --with-mysql --enable-header-install echo $? Now to compile the program make echo $? And to install it make install echo $? Were going to leave the dovecot daemon down for now until we setup the full program.

Configuring Dovecot
protocols = imap login_user = postfix listen = ssl_listen = ssl_disable = no ssl_cert_file = /etc/postfix/smtpd.pem ssl_key_file = /etc/postfix/smtpd.pem auth_cache_size = 128 auth_cache_ttl = 600 mail_debug = no mail_location = maildir:/var/spool/virtualmailboxes/%d/%u/imap/:INBOX=/var/spool/virtualmailboxes/%d/%u/: INDEX=/var/spool/virtualmailboxes/%d/%u/imap/index/ protocol imap { listen = *:143 mail_plugins = quota imap_quota } protocol lda { postmaster_address = postmaster@mattrude.com hostname = samantha.mattrude.com mail_plugins = cmusieve mail_plugins = quota sieve_global_path = /var/spool/sieve/dovecot.sieve mail_plugin_dir = /usr/local/lib/dovecot/lda auth_socket_path = /usr/local/var/run/dovecot/auth-master } namespace private { separator = / prefix = inbox = yes } auth default { mechanisms = plain login userdb sql { args = /etc/dovecot-mysql.conf }  passdb sql { args = /etc/dovecot-mysql.conf }  socket listen { master { path = /var/run/dovecot/auth-master user = virtualmail group = virtualmail }    client { path = /var/spool/postfix/private/auth mode = 0660 user = postfix group = postfix }  } } dict { } plugin sql { quota = maildir:storage=10240 } driver = mysql connect = dbname=postfix user=postfix host=localhost password=postfix default_pass_scheme = PLAIN-MD5 password_query = SELECT password, username AS user FROM mailbox WHERE username = '%u' user_query = SELECT maildir, 1000 AS uid, 1000 AS gid, CONCAT('maildir:storage=', ROUND( mailbox.quota / 1024 ) ) AS quota FROM mailbox WHERE username = '%u' AND active = '1' Now since we compiled from source, you need to link the config files you just created to /usr/local/etc/ directory. ln -s /usr/local/etc/dovecot.conf /etc/dovecot.conf ln -s /usr/local/etc/dovecot-mysql.conf /etc/dovecot-mysql.conf
 * /etc/dovecot.conf
 * 1) Dovecot configuration file
 * 1) The below needs to be put on a single line, not two like is shown here.
 * 1) 10 MB Default Mailbox Size Limit
 * 1)  quota = maildir:ignore=Trash
 * 2)  quota = maildir:ignore=Junk
 * /etc/dovecot-mysql.conf
 * 1) The below needs to be put on a single line, not two like is shown here.

Setting up Dovecot's SASL Authentication
When we use Dovecot SASL authentication we are only going to use it on the submission port (587). To do this add the following to your /etc/postfix/main.cf smtpd_sasl_type = dovecot smtpd_sasl_path = /var/spool/postfix/private/auth smtpd_sasl_security_options = noanonymous Note that we didn't add smtpd_sasl_auth_enable to the main.cf file, this is so we can only enable SASL authentication on the submission port (587). Are you going to allow Outlook on your network? broken_sasl_auth_clients = yes
 * /etc/postfix/main.cf
 * 1) SASL Support

So now in the /etc/postfix/master.cf add or uncomment the following. submission inet n      -       n       -       -       smtpd -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING

Setting up Spamassassin to Postfix's Mail Processing
In /etc/postfix/master.cf, add -o content_filter=spamassassin smtp     inet  n       -       -       -       -       smtpd -o content_filter=spamassassin spamassassin unix -    n       n       -       -       pipe flags=Rq user=nobody argv=/usr/bin/spamc -u ${recipient} -e /usr/sbin/sendmail \ -oi -f ${sender} ${recipient}
 * 1) service type  private unpriv  chroot  wakeup  maxproc command + args
 * 2)               (yes)   (yes)   (yes)   (never) (50)
 * 1)               (yes)   (yes)   (yes)   (never) (50)
 * Then, at the end of the file, you must tell postfix what the content filter "spamassassin" should do. Add the following lines:

Setting up the Clam Anti-Virus system with ClamSMTP
First in /etc/postfix/main.cf, add: content_filter = scan:127.0.0.1:10025 receive_override_options = no_address_mappings Then in /etc/postfix/master.cf, near the bottom, add: scan     unix  -       -       n       -       16      smtp -o smtp_send_xforward_command=yes 127.0.0.1:10026 inet n -       n       -       16      smtpd -o content_filter= -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks -o smtpd_helo_restrictions= -o smtpd_client_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks_style=host -o smtpd_authorized_xforward_hosts=127.0.0.0/8
 * 1) AV scan filter (used by content_filter)
 * 1) For injecting mail back into postfix from the filter

Setting up SPF filter for inbound mail
Were going to use python-postfix-policyd-spf 0.7.1 from openspf.org. wget http://www.openspf.org/blobs/pypolicyd-spf-0.7.1.tar.gz tar -xzf pypolicyd-spf-0.7.1.tar.gz cd pypolicyd-spf-0.7.1 ./setup.py build cd build/scripts-2.5/ cp -f policyd-spf /usr/local/bin/policyd-spf cd ../lib cp -f policydspfsupp.py /usr/lib/python2.5/policydspfsupp.py cd ../../ mkdir /etc/python-policyd-spf/ cp -f policyd-spf.conf /etc/python-policyd-spf/policyd-spf.conf cp -f policyd-spf.1 /usr/share/man/man1/policyd-spf.1 cp -f policyd-spf.conf.5 /usr/share/man/man5/policyd-spf.conf.5 Next we need to update the main.cf file /etc/postfix/main.cf

As close to the bottom of your smtpd_recipient_restrictions as you can but above any DNSBL add the following. smtpd_recipient_restrictions = check_policy_service unix:private/policy policy_time_limit = 3600 Now we need to setup the master.cf file /etc/postfix/master.cf

Add the following to your master.cf file, somewhere near the bottom. policy    unix  -       n       n       -       -       spawn user=nobody argv=/usr/local/bin/policyd-spf Next lets reload postfix postfix reload

Monit Inclusion
If you are running Monit then you can add the following to your /etc/monitrc file then reload Monit (monit reload). The following dose asumme a lot, mostly that you followed the above example nearly to the tee. Be careful with Monit so you don't crash a running program. check device root with path / if space > 80 % THEN ALERT check process clamav with pidfile /var/run/clamav/clamd.pid group email start program = "/etc/init.d/clamav start" stop program = "/etc/init.d/clamav stop" if 50 restarts within 52 cycles then timeout if cpu usage > 80% for 5 cycles then alert if mem usage > 80% for 5 cycles then alert check process clamsmtp with pidfile /var/run/clamav/clamsmtpd.pid group email depends on clamav start program = "/etc/init.d/clamsmtpd start" stop program = "/etc/init.d/clamsmtpd stop" if 50 restarts within 52 cycles then timeout if cpu usage > 80% for 5 cycles then alert if mem usage > 80% for 5 cycles then alert check process dovecot with pidfile /usr/local/var/run/dovecot/master.pid group email depends on mysql start program = "/etc/init.d/dovecot start" stop program = "/etc/init.d/dovecot stop" if 50 restarts within 52 cycles then timeout if cpu usage > 80% for 5 cycles then alert if mem usage > 80% for 5 cycles then alert check process freshclam with pidfile /var/run/clamav/freshclam.pid group email depends on clamav start program = "/etc/init.d/freshclam start" stop program = "/etc/init.d/freshclam stop" if 50 restarts within 52 cycles then timeout if cpu usage > 80% for 5 cycles then alert if mem usage > 80% for 5 cycles then alert check process postfix with pidfile /var/spool/postfix/pid/master.pid group email depends on mysql start program = "/etc/init.d/postfix start" stop program = "/etc/init.d/postfix stop" if 50 restarts within 52 cycles then timeout if cpu usage > 80% for 5 cycles then alert if mem usage > 80% for 5 cycles then alert check process saslauthd with pidfile /var/run/saslauthd/saslauthd.pid group email depends on postfix start program = "/etc/init.d/saslauthd start" stop program = "/etc/init.d/saslauthd stop" if 50 restarts within 52 cycles then timeout if cpu usage > 80% for 5 cycles then alert if mem usage > 80% for 5 cycles then alert check process spamd with pidfile /var/run/spamd.pid group email depends on mysql start program = "/etc/init.d/spamassassin start" stop program = "/etc/init.d/spamassassin stop" if 50 restarts within 52 cycles then timeout if cpu usage > 80% for 5 cycles then alert if mem usage > 80% for 5 cycles then alert check process mysql with pidfile /var/run/mysqld/mysqld.pid group email start program = "/etc/init.d/mysqld start" stop program = "/etc/init.d/mysqld stop" if failed port 3306 then restart if 50 restarts within 52 cycles then timeout if cpu usage > 80% for 5 cycles then alert if mem usage > 80% for 5 cycles then alert check file aliases path /etc/postfix/aliases group email if changed sha1 checksum then exec "/usr/bin/newaliases" if failed permission 0644 then exec "/bin/chmod 0644 /etc/postfix/aliases" if failed uid 0 then exec "/bin/chown 0 /etc/postfix/aliases" if failed gid 0 then exec "/bin/chown 0:0 /etc/postfix/aliases" check file main.cf path /etc/postfix/main.cf group email if failed permission 0644 then exec "/bin/chmod 0644 /etc/postfix/main.cf" if failed uid 0 then exec "/bin/chown 0 /etc/postfix/main.cf" if failed gid 0 then exec "/bin/chown 0:0 /etc/postfix/main.cf" check file master.cf path /etc/postfix/master.cf group email if failed permission 0644 then exec "/bin/chmod 0644 /etc/postfix/master.cf" if failed uid 0 then exec "/bin/chown 0 /etc/postfix/master.cf" if failed gid 0 then exec "/bin/chown 0:0 /etc/postfix/master.cf" check file sasl_passwd path /etc/postfix/sasl_passwd group email if changed sha1 checksum then exec "/usr/sbin/postmap /etc/postfix/sasl_passwd && /usr/sbin/postfix reload" if failed permission 0644 then exec "/bin/chmod 0644 /etc/postfix/sasl_passwd" if failed uid 0 then exec "/bin/chown 0 /etc/postfix/sasl_passwd" if failed gid 0 then exec "/bin/chown 0:0 /etc/postfix/sasl_passwd" check file transport path /etc/postfix/transport group email if changed sha1 checksum then exec "/usr/sbin/postmap /etc/postfix/transport && /usr/sbin/postfix reload" if failed permission 0644 then exec "/bin/chmod 0644 /etc/postfix/transport" if failed uid 0 then exec "/bin/chown 0 /etc/postfix/transport" if failed gid 0 then exec "/bin/chown 0:0 /etc/postfix/transport" check directory clamav_virus_dir path "/tmp/clamav" group email start program = "/bin/mkdir /tmp/clamav" if failed permission 0700 then exec "/bin/chmod 0700 /tmp/clamav" if failed uid 509 then exec "/bin/chown 509 /tmp/clamav" if failed gid 509 then exec "/bin/chown 509:509 /tmp/clamav" check directory postfix_config_dir path "/etc/postfix" group email if failed permission 0755 then exec "/bin/chmod 0755 /etc/postfix" if failed uid 0 then exec "/bin/chown 0 /etc/postfix" if failed gid 0 then exec "/bin/chown 0:0 /etc/postfix" check directory virtualmailboxes_dir path "/var/spool/virtualmailboxes" group email start program = "/bin/mkdir /var/spool/virtualmailboxes" if failed permission 0700 then exec "/bin/chmod 777 /var/spool/virtualmailboxes && /bin/chmod -R u=rwX,go-rwx /var/spool/virtualmailboxes/" if failed uid 1000 then exec "/bin/chown -R 1000 /var/spool/virtualmailboxes/" if failed gid 1000 then exec "/bin/chown -R 1000:1000 /var/spool/virtualmailboxes/"
 * /etc/monitrc

Testing Postfix

 * http://www.abuse.net/relay.html
 * http://www.mxtoolbox.com/diagnostic.aspx