Links
Tags
apache
armenia
books
bsd
c
c++
chips
cinema
concurrency
cooking
database
dragonfly
erlang
filesystem
freebsd
fun
hardware
java
javascript
json
languages
linux
lyric
mac_osx
mail
math
misc
music
personal
poems
presentation
programming
python
references
ruby
rubyjs
scm
software
spiking_neural_net
study
sysadm
sysarch
technology
testing
travel
virtualization
web
wee
windows
Nowadays, most modern mail clients like Evolution or Mozilla-Mail include a full-featured SMTP client to send email to a central mail-relay (usually your providers’ one). And they usually support SASL authentification and encryption over SSL/TLS, as your provider will (hopefully) not accept mail for relay by everyone. But other email clients still exist - the famous mutt for example - that do not come with a SMTP client at all.
Installation
Build Postfix with Cyrus-SASLv2 and SSL/TLS support.
Configuration
Edit /usr/local/etc/postfix/main.cf where you add the following lines:
relayhost = mailhost
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps=static:username:password
smtp_use_tls = yes
sender_canonical_maps=hash:/usr/local/etc/postfix/sender_canonical
Replace mailhost with your provider’s mail server, username and password with your own mail account settings.
Further create the sender_canonical file with following content:
@your.localhost.domain your-real-address
On my system that looks this way:
@michael.neumann.all mneumann@ntecs.de
This will rewrite the envelope address from mneumann@michael.neumann.all (that’s my local address) to mneumann@ntecs.de (that’s my remote address). You still need to build the hash database:
postmap /usr/local/etc/postfix/sender_canonical
Finally, make sure the main.cf file is not world readable (chmod 640), as it contains your password!
Notes
If you are your own provider (I am), and you’re using Postfix as your mail-relay, make sure that the permit_sasl_authentificated directive comes before any of the reject_xxx directives. This makes live easier with mail programs that produce mails with missing headers.
Acknowledgements: This howto relies heavily on Kirby Menzel and Lucas Peet’s great Postfix+Courier-IMAP+MySQL for multiple domains HOWTO (kirb.insanegenius.net/postfix.html). In the underlying howto, I list the steps I’ve used for my own setup. It has an additional section about SMTP authentification using Cyrus SASL (and Mysql) and some more information not found in the above howto.
Required Software
- (FreeBSD 4.8)
- Postfix
- Courier-IMAP
- MySQL
- OpenSSL (SSL/TLS)
- Cyrus SASL v2
Installing Mysql Database
cd /usr/ports/databases/mysql40-server
make install BUILD_OPTIMIZED=yes
make clean
A group mysql and user mysql has been added automatically, as well as a startup script /usr/local/etc/rc.d/mysql-server.sh.
After starting the mysqld server, modify the password with:
/usr/local/bin/mysqladmin -u root password 'new-password'
When calling mysqladmin later, pass the -p option, which will ask you for the current password, otherwise the access will be denied.
TIP: use security/apg, the automated password generator, to create a password.
Installing postfix
cd /usr/ports/mail/postfix
make
(choose "Cyrus SASLv2", "SSL and TLS" and "MySQL map lookups" as options,
then in SASLv2 menu, choose "MySQL password Authentification")
make install
make clean
Note that this will additionally install the Mysql-323 client libraries (if you’ve installed MySQL 4.x).
Modify /etc/mail/mailer.conf to use Postfix, or let the install program do that for you (make install will ask you).
For automatic startup when system starts, disable sendmail in /etc/rc.conf:
sendmail_enable="NONE"
Then make the following symbolic link:
cd /usr/local/etc/rc.d
ln -s /usr/local/sbin/postfix postfix.sh
Important: The MySQL server should be started before postfix (rename mysql-server.sh into 100.mysql-server.sh or something alike; number go before characters).
Then create /etc/periodic.conf with following content:
daily_clean_hoststat_enable="NO"
daily_status_mail_rejects_enable="NO"
daily_status_include_submit_mailq="NO"
daily_submit_queuerun="NO"
This will disable some Sendmail-specific daily maintenance routines.
Check /etc/mail/aliases, then run newaliases, otherwise Postfix will not be able to deliver mail locally (unless you setup virtual domains etc.) Local mail will be usually delivered to /var/mail.
You can disable sending email locally (using sendmail) by setting the following option in main.cf.
mydestination =
But make sure you know what you are doing!
Setting up the Mysql tables
mysql -p -u root
create database maildb;
CREATE TABLE transport (
domain varchar(128) NOT NULL,
transport varchar(128) NOT NULL,
UNIQUE KEY domain (domain)
) TYPE=MyISAM;
CREATE TABLE users (
id varchar(128) NOT NULL,
address varchar(128) NOT NULL,
clear varchar(128) NOT NULL,
crypt varchar(128) NOT NULL,
name varchar(128) NOT NULL default '',
uid smallint(5) unsigned NOT NULL default 5000,
gid smallint(5) unsigned NOT NULL default 5000,
home varchar(128) NOT NULL,
domain varchar(128) NOT NULL,
maildir varchar(255) NOT NULL,
quota integer unsigned NOT NULL,
imapok tinyint(3) unsigned NOT NULL default '1',
PRIMARY KEY (id),
UNIQUE KEY id (id),
UNIQUE KEY address (address),
KEY id_2 (id),
KEY address_2 (address)
) TYPE=MyISAM;
CREATE TABLE virtual (
address varchar(255) NOT NULL,
goto varchar(255) NOT NULL,
UNIQUE KEY address (address)
) TYPE=MyISAM;
GRANT SELECT
ON maildb.*
TO maildb_user@localhost
IDENTIFIED BY '****chose a password here****'
;
The user maildb_user will be used by both Postfix and Courier.
Modify Postfix’s main.cf
Add these lines:
transport_maps=mysql:/usr/local/etc/postfix/mysql_transport.cf
virtual_mailbox_maps=mysql:/usr/local/etc/postfix/mysql_virtual_mbox.cf
virtual_uid_maps=mysql:/usr/local/etc/postfix/mysql_uids.cf
virtual_gid_maps=mysql:/usr/local/etc/postfix/mysql_gids.cf
virtual_mailbox_base=/var/spool/postfix/virtual/
virtual_maps=mysql:/usr/local/etc/postfix/mysql_virtual.cf
# 100 MB
virtual_mailbox_limit=102400000
virtual_minimum_uid=100
…and create the mysql_XXX.cf files:
# mysql_transport.cf
user=maildb_user
password=******
dbname=maildb
table=transport
select_field=transport
where_field=domain
hosts=localhost
# mysql_virtual_mbox.cf
user=maildb_user
password=*****
dbname=maildb
table=users
select_field=maildir
where_field=address
hosts=localhost
# mysql_uids.cf
user=maildb_user
password=*****
dbname=maildb
table=users
select_field=uid
where_field=address
hosts=localhost
# mysql_gids.cf
user=maildb_user
password=*****
dbname=maildb
table=users
select_field=gid
where_field=address
hosts=localhost
# mysql_virtual.cf
user=maildb_user
password=******
dbname=maildb
table=virtual
select_field=goto
where_field=address
hosts=localhost
Important: Don’t forget that the mysql_xxx.cf files should not be world readable (mode 400) and chown postfix:postfix them, due to the fact that they contain a mysql password, and the password allows to get the users password out of the table! Whenever you modify these files, make sure it is readable by postfix:postfix (e.g. if you check the file out of a RCS repository, beeing the root user, the file will be owned by root and not by postfix, so that the postfix daemon cannot read the file anymore!)
Also add $transport_maps to mydestination.
Finally Steps
Create /var/spool/postfix/virtual/ and chown it to user/group postfix. For each user, create a mailbox directory and chown it to them.
Filling Data into the Mysql tables
For creating the crytped password, use Mysql’s encrypt function.
A simple example (one domain, one user, multiple aliases):
mysql> select * from transport;
+----------+-----------+
| domain | transport |
+----------+-----------+
| ntecs.de | virtual: |
+----------+-----------+
1 row in set (0.01 sec)
mysql> select * from users;
+---------+------------------+-------+---------------+-----------------+------+------+...
| id | address | clear | crypt | name | uid | gid |
+---------+------------------+-------+---------------+-----------------+------+------+...
| michael | michael@ntecs.de | abc | 9GZRP/ADKguPk | Michael Neumann | 5000 | 5000 |
+---------+------------------+-------+---------------+-----------------+------+------+...
...+----------------------------+----------+-------------------+-----------+--------+
| home | domain | maildir | quota | imapok |
...+----------------------------+----------+-------------------+-----------+--------+
| /var/spool/postfix/virtual | ntecs.de | ntecs.de/michael/ | 102400000 | 1 |
...+----------------------------+----------+-------------------+-----------+--------+
1 row in set (0.00 sec)
mysql> select * from virtual;
+-------------------+------------------+
| address | goto |
+-------------------+------------------+
| mneumann@ntecs.de | michael@ntecs.de |
| @ntecs.de | michael@ntecs.de |
+-------------------+------------------+
2 rows in set (0.00 sec)
TODO: It might be a good idea to rename table users to mailbox, virtual to aliases and perhaps transport to domain.
NOTE: Each email that should be deliverable, must have an entry in the virtual table, even if it points to itself (e.g. mneumann@ntecs.de => mneumann@ntecs.de).
For more information about Postfix and Mysql read /usr/local/share/doc/postfix/MYSQL_README.
Courier Imap
cd /usr/ports/mail/courier-imap
make WITHOUT_PAM=yes WITH_MYSQL=yes
make install
After installation, we first create a certificate for imapd-ssl. First edit /usr/local/etc/courier-imap/imapd.cnf (copy from imapd.cnf.dist):
RANDFILE = /usr/local/share/courier-imap/imapd.rand
[ req ]
default_bits = 1024
encrypt_key = yes
distinguished_name = req_dn
x509_extensions = cert_type
prompt = no
[ req_dn ]
C=DE
L=Frankfurt
O=Courier Mail Server
OU=Automatically-generated IMAP SSL key
CN=localhost
emailAddress=postmaster@ntecs.de
[ cert_type ]
nsCertType = server
Edit the [ req_dn ] section as you like.
Then, create the certificate with:
/usr/local/share/courier-imap/mkimapdcert
Now we’ll copy the example configuration files to their real name:
cd /usr/local/etc/courier-imap
mv authdaemonrc.dist authdaemonrc
mv authmysqlrc.dist authmysqlrc
mv imapd.dist imapd
mv imapd-ssl.dist imapd-ssl
Edit authdaemonrc and remove all but "authmysql" from authmodulelist.
Edit authmysqlrc:
MYSQL_SERVER localhost
MYSQL_USERNAME maildb_user
MYSQL_PASSWORD *****
MYSQL_SOCKET /tmp/mysql.sock
MYSQL_DATABASE maildb
MYSQL_USER_TABLE users
MYSQL_CRYPT_PWFIELD crypt
MYSQL_UID_FIELD uid
MYSQL_GID_FIELD gid
MYSQL_LOGIN_FIELD id
MYSQL_HOME_FIELD home
MYSQL_NAME_FIELD name
MYSQL_MAILDIR_FIELD maildir
MYSQL_QUOTA_FIELD quota
MYSQL_WHERE_CLAUSE imapok=1
Edit imapd:
nothing to edit for now
In imapd-ssl there are two important options:
IMAPDSSLSTART=NO (enables imapd over SSL)
IMAPDSTARTTLS=YES (imap via TLS: TLS is application level encryption)
Both should be set to these values by default, so no editing is needed here.
Setting IMAPDSSLSTART=YES and IMAPDSTARTTLS=NO would work too, but TLS is newer, so we’ll use it here, and I believe it transmits unsensitive data in clear text (the connection establishing part, until password and data is transfered, then switchs to encryption). Again I think, TLS is easier to cope with in terms of logging etc.
Do some cleanup:
cd /usr/local/etc/rc.d
rm courier-imap-imapd.sh.sample (we don't need this)
rm courier-imap-pop3d.sh.sample (we don't need this either)
ln -s /usr/local/libexec/courier-imap/imapd-ssl.rc courier-imap-imapd-ssl.sh
And start the daemon (or reboot):
sh /usr/local/etc/rc.d/courier-imap-imapd-ssl.sh start
Postfix SMTP Setup
By default, everyone from everywhere (untrusted client) is allowed to send your postfix server mail he is responsible for (mydestination paramter, or relay_domains), whereas relaying to other hosts is not allowed.
Only allow relaying on the host itself by setting:
mynetworks_style = host
Remove saslauthd.sh from /usr/local/etc/rc.d as we don’t need it:
cd /usr/local/etc/rc.d
rm saslauthd.sh
In /usr/local/lib/sasl2 create file smtpd.conf:
# smtpd.conf
pwcheck_method:auxprop
mech_list: plain login
mysql_user: maildb_user
mysql_passwd: *******
mysql_hostnames: localhost
mysql_database: maildb
mysql_statement: select clear from users where id = '%u'
# mysql_verbose: 1
After everything works as expected, we can turn "mysql_verbose" off (comment it out).
Make this file chmod 400, as it contains the mysql database password.
Note that for SASL authentification we need the clear-text password (column clear), whereas for IMAP autentification the crypted-password (column crypt) is required. This is a bit strange.
Again, modify postfix’s main.cf configuration file (in /usr/local/etc/postfix):
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =
broken_sasl_auth_clients = yes
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unknown_sender_domain,
reject_unauth_pipelining,
reject_unknown_recipient_domain,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_non_fqdn_hostname,
check_relay_domains
Enable TLS/SSL
We need a certificate first. But as we still have one for the Courier-IMAP server, we’ll reuse that. We should give it 400 permissions first:
cd /usr/local/share/courier-imap
chmod 400 imapd.pem
Then again, we edit Postfix’s main.cf configuration file, where we add the following:
smtp_use_tls = yes
smtpd_use_tls = yes
smtp_tls_note_starttls_offer = yes
smtpd_tls_key_file = /usr/local/share/courier-imap/imapd.pem
smtpd_tls_cert_file = /usr/local/share/courier-imap/imapd.pem
smtpd_tls_CAfile = /usr/local/share/courier-imap/imapd.pem
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
# allow authentification (e.g. PLAIN/LOGIN) only in TLS mode
smtpd_tls_auth_only = yes
The last directive (smtpd_tls_auth_only) is very important, as it only allows authentification to be performed when TLS is established. This protects the client, to accidentially send their passwords in clear text.
Todo’s
- run Postfix in chroot’ed environment.
- run Courier-Imap as non-root user
- change /tmp/mysql.sock to /var/mysql/mysql.sock
- use mail/messagewall to protect even more against spam