Install and configure Postfix with Cyrus-SASL + Cyrus-IMAP + MySQL on FreeBSD

$FreeBSDMall: doc/en_US.ISO8859-1/articles/vmail/article.sgml,v 1.4 2006/07/10 03:52:42 loader Exp $

This HOWTO has been tested on 7.0-Current.


Table of Contents
1 MySQL
2 pam-mysql
3 Postfix Admin
4 Cyrus-SALS2
5 Postfix
6 Courier IMAP
7 Testing
8 TLS
9 Troubleshooting

1 MySQL

1.1 Install MySQL

# cd /usr/ports/databases/mysql50-server/
# make install clean

Edit rc.conf to enable MySQL and specify a directory for database files:

# vi /etc/rc.conf
mysql_enable="YES"
mysql_dbdir="/usr/local/mysql"

1.2 Configure MySQL

Import DATABASE_MYSQL.TXT into MySQL:

# /usr/local/etc/rc.d/mysql-server start (1)
% mysql -u root < DATABASE_MYSQL.TXT (2)
(1)
Run mysqld
(2)
DATABASE_MYSQL.TXT is the part of mail/postfixadmin, we will install this port later.

2 pam-mysql

2.1 Install pam-mysql

# cd /usr/ports/security/pam-mysql/
# make install clean

2.2 Configure pam-mysql

# ln -sf /usr/local/lib/pam_mysql.so /usr/lib/pam_mysql.so
# echo auth required pam_mysql.so user=postfix passwd=postfix \
host=localhost db=postfix table=mailbox usercolumn=username \
passwdcolumn=password crypt=1 sqllog=0 debug > /etc/pam.d/smtp
# echo account sufficient pam_mysql.so user=postfix passwd=postfix \
host=localhost db=postfix table=mailbox usercolumn=username \
passwdcolumn=password crypt=1 sqllog=0 debug >> /etc/pam.d/smtp
# echo auth sufficient pam_unix.so debug >> /etc/pam.d/smtp
# echo account sufficient pam_unix.so debug >> /etc/pam.d/smtp

3 Postfix Admin

3.1 Install Apache

# cd /usr/ports/www/apache13-modssl
# make install clean
# vi /etc/rc.conf
apache_enable="YES"

3.2 Install PHP5

# cd /usr/ports/lang/php5
# make -DBATCH WITH_APACHE=1 install clean
# vi /usr/local/etc/apache/httpd.conf
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps

<IfModule mod_dir.c>
  DirectoryIndex index.html index.php
</IfModule>

3.3 Install Postfix Admin

# cd /usr/ports/mail/postfixadmin
# make -DBATCH WITH_MYSQL=1 install clean

Note: apache needs restarting when a new PHP module is installed or /usr/local/etc/apache/httpd.conf is modified:

# /usr/local/etc/rc.d/apache.sh restart

3.4 Configure Postfix Admin

# ln -s /usr/local/www/postfixadmin /usr/local/www/data/postfixadmin
# vi /usr/local/www/postfixadmin/config.inc.php

Change

$CONF['maxquota'] = '10';

// Quota
// When you want to enforce quota for your mailbox users set this to 'YES'.
$CONF['quota'] = 'NO';

To

$CONF['maxquota'] = '100(1)';

// Quota
// When you want to enforce quota for your mailbox users set this to 'YES'.
$CONF['quota'] = 'YES(2)';
(1)
Set the default quota when adding a new account (in MB). This value will work with the Postfix VDA patch.
(2)
Enable “soft quota” when adding a new account.

Then point a web browser to postfixadmin: http://yourdomain.com/postfixadmin/admin/index.php

Figure 1. Add a new domain

Figure 2. Add a new account


4 Cyrus-SALS2

4.1 Install Cyrus-SASL2

# cd /usr/ports/security/cyrus-sasl2
# make WITH_MYSQL=/usr/local/ install clean
# cd /usr/ports/security/cyrus-sasl2-saslauthd
# make install clean
# vi /etc/rc.conf
saslauthd_enable="YES"
saslauthd_flags="-r(1) -a pam"
(1)
saslauthd(8):Combine the realm with the login (with an '@' sign in between). e.g. login: “foo” realm: “bar” will get passed as login: “foo@bar”. Note that the realm will still be passed, which may lead to unexpected behavior.

4.2 Configure Cyrus-SASL2

# vi /usr/local/lib/sasl2/smtpd.conf
pwcheck_method: saslauthd
log_level: 3
mech_list: plain login cram digest

5 Postfix

5.1 Install Postfix

# cd /usr/ports/mail/postfix/
# make POSTFIX_OPTIONS="SASL2(1) TLS(2) MySQL(3) VDA(4)" install clean
(1)
For SMTP authentication, and we also need to install the ports security/cyrus-sasl2 and security/cyrus-sasl2-saslauthd.
(2)
For encrypted SMTP connections, see this section.
(3)
Enable MySQL map lookups
(4)
Apply the VDA patch for “soft quota”

Figure 3. install Postfix

===>  Installing for postfix-2.2.10_1,1
===>   postfix-2.2.10_1,1 depends on shared library: sasl2.2 - found
===>   postfix-2.2.10_1,1 depends on shared library: pcre.0 - found
===>   postfix-2.2.10_1,1 depends on shared library: mysqlclient.15 - found
Added group "postfix".
maildrop:*:126:
You already have a group "maildrop", so I will use it.
Added user "postfix".
You need user "postfix" added to group "mail".
Would you like me to add it [y]? (1)
Done.
... ... ...
... ... ...
Would you like to activate Postfix in /etc/mail/mailer.conf [n]?(2)
(1)
Answer “y” for adding user postfix:postfix.
(2)
Answer “y” to set Postfix as the default MTA.
# vi /etc/rc.conf
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"

postfix_enable="YES"

This is optional:

# vi /etc/periodic.conf
daily_clean_hoststat_enable="NO"
daily_status_mail_rejects_enable="NO"
daily_status_include_submit_mailq="NO"
daily_submit_queuerun="NO"

We need to modify some postfix basic options in /usr/local/etc/postfix/main.cf

myhostname = build1.unknown.com
myorigin = $mydomain
inet_interfaces = all
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
mynetworks = 192.168.1.131, 127.0.0.0/8
home_mailbox = Maildir/

And add our virtual and SMTP settings into /usr/local/etc/postfix/main.cf

virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql_virtual_alias_maps.cf
virtual_gid_maps = static:1001
virtual_mailbox_base = /usr/local/virtual
virtual_mailbox_domains = mysql:/usr/local/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_limit = 51200000
virtual_mailbox_maps = mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 1001
virtual_transport = virtual
virtual_uid_maps = static:1001
# Additional for quota support
virtual_create_maildirsize = yes
virtual_mailbox_extended = yes
virtual_mailbox_limit_maps = mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
virtual_mailbox_limit_override = yes
#virtual_maildir_limit_message = Sorry, the user's maildir has overdrawn his diskspace quota, please try 
again later.
virtual_overquota_bounce = yes

broken_sasl_auth_clients = yes
smtpd_recipient_restrictions = 
  permit_mynetworks,
  permit_sasl_authenticated,
  reject_non_fqdn_hostname,
  reject_non_fqdn_sender,
  reject_non_fqdn_recipient,  
  reject_unauth_destination,
  reject_unauth_pipelining,   
  reject_invalid_hostname,
  reject_rbl_client opm.blitzed.org,
  reject_rbl_client list.dsbl.org,
  reject_rbl_client bl.spamcop.net,
  reject_rbl_client sbl-xbl.spamhaus.org
smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain = $myhostname
smtpd_sasl_security_options = noanonymous

Create these following files:

# vi /usr/local/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'
# vi /usr/local/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
# vi /usr/local/etc/postfix/mysql_virtual_domains_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s'
#optional query to use when relaying for backup MX
#query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '0' and active = '1'
# vi /usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT quota FROM mailbox WHERE username='%s'
# vi /usr/local/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

Next we need to create a “vmail” user, and start Postfix

# pw groupadd vmail -g 1001
# pw useradd vmail -u 1001 -g vmail -h - -d /usr/local/virtual -s /usr/sbin/nologin -c "Virtual Mail System"
# mkdir /usr/local/virtual
# chown -R vmail:vmail /usr/local/virtual
# /usr/local/etc/rc.d/postfix start

6 Courier IMAP

6.1 Install Courier IMAP

# cd /usr/ports/mail/courier-imap
# make install clean

We don't need IPV6 support, so remove it, and select MySQL support.

Figure 4. install courier-imap

# vi /etc/rc.conf
courier_authdaemond_enable="YES"
courier_imap_pop3d_enable="YES"
courier_imap_imapd_enable="YES"

6.2 configure courier-imap

# vi /usr/local/etc/authlib/authdaemonrc

We need to remove unused auth modules:

##NAME: authmodulelist:2
#
# The authentication modules that are linked into authdaemond.  The
# default list is installed.  You may selectively disable modules simply
# by removing them from the following list.  The available modules you
# can use are: authuserdb authvchkpw authpam authldap authmysql authpgsql

authmodulelist="authuserdb authvchkpw authpam authldap authmysql authpgsql"

##NAME: authmodulelistorig:3
#
# This setting is used by Courier's webadmin module, and should be left
# alone

authmodulelistorig="authuserdb authvchkpw authpam authldap authmysql authpgsql"

Only “authmysql” for these options:

##NAME: authmodulelist:2
#
# The authentication modules that are linked into authdaemond.  The
# default list is installed.  You may selectively disable modules simply
# by removing them from the following list.  The available modules you
# can use are: authuserdb authvchkpw authpam authldap authmysql authpgsql

authmodulelist="authmysql"

##NAME: authmodulelistorig:3
#
# This setting is used by Courier's webadmin module, and should be left
# alone

authmodulelistorig="authmysql"
# vi /usr/local/etc/authlib/authmysqlrc
#MYSQL_SERVER            localhost
#MYSQL_PORT              3306
MYSQL_SOCKET            /tmp/mysql.sock
MYSQL_USERNAME          postfix
MYSQL_PASSWORD          postfix
MYSQL_OPT               0
MYSQL_DATABASE          postfix
MYSQL_USER_TABLE        mailbox
MYSQL_CRYPT_PWFIELD     password
MYSQL_UID_FIELD         '1001'
MYSQL_GID_FIELD         '1001'
MYSQL_LOGIN_FIELD       username
MYSQL_HOME_FIELD        '/usr/local/virtual'
MYSQL_NAME_FIELD        name
MYSQL_MAILDIR_FIELD     maildir
# /usr/local/etc/rc.d/courier-authdaemond start
# /usr/local/etc/rc.d/courier-imap-imapd.sh start
# /usr/local/etc/rc.d/courier-imap-pop3d.sh start

7 Testing

7.1 SMTP

# telnet mailserver 25
Trying 192.168.1.132...
Connected to test.freebsdmall.com.
Escape character is '^]'.
220 test.freebsdmall.com ESMTP Postfix
ehlo test
250-test.freebsdmall.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH LOGIN PLAIN
250-AUTH=LOGIN PLAIN
250 8BITMIME
auth plain auth plain AHRlc3QxQHRlc3QuY29tAHRlc3Q=(1)
235 Authentication successful
quit
221 Bye
Connection closed by foreign host.
(1)
Username is “test1@test.com” and password is “test”
% perl -MMIME::Base64 -e 'print encode_base64("\0test1\@test.com\0test")'
AHRlc3QxQHRlc3QuY29tAHRlc3Q=

7.2 IMAP

unfinished


7.3 POP3

unfinished


8 TLS

8.1 Generating a self-verified certficate

Create Your Own Certificate Authority

# openssl genrsa -des3 -out ca.key 1024
Generating RSA private key, 1024 bit long modulus
........++++++
.++++++
e is 65537 (0x10001)
Enter pass phrase for ca.key:
Verifying - Enter pass phrase for ca.key:

Create CA certificate (ca.crt) and sign it with the CA's private key (ca.key)

# openssl req -new -x509 -days 365 -key ca.key -out ca.crt
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:CA
Locality Name (eg, city) []:Brentwood
Organization Name (eg, company) [Internet Widgits Pty Ltd]:FreeBSDMall
Organizational Unit Name (eg, section) []:FreeBSDMall
Common Name (eg, YOUR name) []:freebsdmall.com
Email Address []:loader@freebsdmall.com

Create an unsigned certificate (this is for SSL enabling your service)

Create a certificate (server.pem) and a certificate signing request or CSR (req.pem).

# openssl req -new -nodes -out req.pem -keyout server.pem
Generating a 1024 bit RSA private key
..++++++
...........++++++
writing new private key to 'server.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:CA
Locality Name (eg, city) []:Brentwood
Organization Name (eg, company) [Internet Widgits Pty Ltd]:FreeBSDMall
Organizational Unit Name (eg, section) []:FreeBSDMall
Common Name (eg, YOUR name) []:freebsdmall.com
Email Address []:loader@freebsdmall.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:password
An optional company name []:

Note: -nodes option means that your key is sitting unencrypted on your hard disk. That may pose some security risk.

Sign Your Certificate With Your CA's Key Using your CA's private key (ca.key) and certificate (ca.crt) sign the CSR (req.pem) and create a signed certificate (signed_req.pem)

# openssl x509 -req -CA ca.crt -CAkey ca.key -days 365 -in req.pem -out signed_req.pem -CAcreateserial
Signature ok
subject=/C=US/ST=CA/L=Brentwood/O=FreeBSDMall/OU=FreeBSDMall/CN=freebsdmall.com/emailAddress=loader@freebsdmall.com
Getting CA Private Key
Enter pass phrase for ca.key:

Concatenate the signed request with certificate to produce a certificate that can be loaded into your web/mail server

# cat signed_req.pem >> server.pem

View the fingerprint of a certificate:

# cp /usr/src/crypto/openssl/apps/cert.pem .
# openssl x509 -subject -dates -fingerprint -in cert.pem
subject= /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=Eric the Young
notBefore=Sep  9 03:41:26 1997 GMT
notAfter=Oct  9 03:41:26 1997 GMT
MD5 Fingerprint=C9:28:DB:4A:35:76:47:EE:2F:A4:7C:89:16:66:64:91
-----BEGIN CERTIFICATE-----
MIIBoDCCAUoCAQAwDQYJKoZIhvcNAQEEBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV
BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD
VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw05NzA5MDkwMzQxMjZa
Fw05NzEwMDkwMzQxMjZaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNVBAMT
DkVyaWMgdGhlIFlvdW5nMFEwCQYFKw4DAgwFAANEAAJBALVEqPODnpI4rShlY8S7
tB713JNvabvn6Gned7zylwLLiXQAo/PAT6mfdWPTyCX9RlId/Aroh1ou893BA32Q
sggwDQYJKoZIhvcNAQEEBQADQQCU5SSgapJSdRXJoX+CpCvFy+JVh9HpSjCpSNKO
19raHv98hKAUJuP9HyM+SUsffO6mAIgitUaqW8/wDMePhEC3
-----END CERTIFICATE-----

The short can look as follows:

# openssl req -new -nodes -out req.pem -keyout key.pem
Generating a 1024 bit RSA private key
...........++++++
...++++++
writing new private key to 'key.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:CA
Locality Name (eg, city) []:Brentwood
Organization Name (eg, company) [Internet Widgits Pty Ltd]:FreeBSDMall
Organizational Unit Name (eg, section) []:FreeBSDMall
Common Name (eg, YOUR name) []:freebsdmall.com
Email Address []:loader@freebsdmall.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:password
An optional company name []:
# openssl rsa -in key.pem -out server.pem
writing RSA key
# openssl x509 -in req.pem -out ca-cert -req -signkey server.pem -days 999
Signature ok
subject=/C=US/ST=CA/L=Brentwood/O=FreeBSDMall/OU=FreeBSDMall/CN=freebsdmall.com/emailAddress=loader@freebsdmall.com
Getting Private key
# cat ca-cert >> server.pem
# cp server.pem /usr/local/etc/postfix

8.2 Postfix

# vi /usr/local/etc/postfix/main.cf
smtpd_use_tls = yes
smtpd_tls_auth_only = no
smtpd_tls_loglevel = 3
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 600s
smtpd_tls_cert_file = /usr/local/etc/postfix/server.pem
smtpd_tls_key_file = $smtpd_tls_cert_file
smtpd_tls_dcert_file = /usr/local/etc/postfix/server.pem
smtpd_tls_dkey_file = $smtpd_tls_dcert_file
#smtpd_tls_CApath = /usr/local/etc/postfix
#tls_random_source = dev:/dev/urandom
# /usr/local/etc/rc.d/postfix restart

8.3 Courier-IMAP

8.3.1 imapd-ssl

# vi /usr/local/etc/courier-imap/imapd-ssl

change

TLS_CERTFILE=/usr/local/share/courier-imap/imapd.pem

to

TLS_CERTFILE=/usr/local/etc/postfix/server.pem
# vi /etc/rc.conf
courier_imap_imapd_ssl_enable="YES"
# /usr/local/etc/rc.d/courier-imap-imapd-ssl.sh start

8.3.2 pop3d-ssl

# vi /usr/local/etc/courier-imap/pop3d-ssl

change

TLS_CERTFILE=/usr/local/share/courier-imap/pop3d.pem

to

TLS_CERTFILE=/usr/local/etc/postfix/server.pem
# vi /etc/rc.conf
courier_imap_pop3d_ssl_enable="YES"
# /usr/local/etc/rc.d/courier-imap-pop3d-ssl.sh start

9 Troubleshooting

Jul  7 17:49:11 tank postfix/smtpd[86964]: fatal: open database /etc/aliases.db: No such file or directory

This, and other documents, can be downloaded from ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

For questions about FreeBSD, read the documentation before contacting <questions@FreeBSD.org>.
For questions about this documentation, e-mail <doc@FreeBSD.org>.