POP3 IMAP SMTP E-mail server
From OptionC
| Table of contents |
|
|
Introduction
(This was reviewed and modified on 2005-12-05, and is going through testing.)
This document was created to show, step-by-step, how we created an e-mail server DomU VM for Xen. In reading through the document you will find that there's almost no reference to Xen or virtualization, as the DomU VM should be configured as a standalone machine would be.
I wanted to create a single-domain e-mail server that would handle both IMAP and POP3 connections (via Courier MTA) and SMTP (via Postfix). Rather than be forced to maintain a database and a set of users on the machine, I chose to use a single database for maintenance. The database of choice is MySQL. Usually this database would be remote for security and segregation purposes, but for this VM it's included as part of the machine (I'll make notes on needed changes for remote database connectivity).
Because it wasn't any extra work, the e-mail server has been set up in such a way as to support multiple domains (although only one is presented). As such it will make use of Postfix's transport and virtual mapping mechanisms. These can be expanded as needed by modifying or adding database entries.
Initial System Setup
I started with a Debian install created via debootstrap, as defined here (http://www.option-c.com/xwiki/Create_a_Debian_VM_with_debootstrap).
After creating an image with all the packages defined in this HOWTO, I had approximately 50 MB of free space (based upon an initial 300 MB disk image), and approximately 50 MB of packages in the cache. If you do not size your images or partitions to match your e-mail needs during the initial configuration, you always have the option of resizing them (using "resize2fs" for file-back VBDs or "lvextend" for logical volumes) or adding another partition to the configuration and mounting it in fstab.
Note: Throughout this document I use the following conventions:
"SERVERNAME" = local name of the e-mail server "DOMAIN.COM" = the name of the e-mail domain you're building "192.168.99.99" = the local IP address of the e-mail server "PASSWORD" = the database password, needed by MySQL, Courier and Postfix
After the system was ready and running (within Xen) I made the following modifications:
# echo SERVERNAME > /etc/hostname # hostname SERVERNAME # echo 127.0.0.1 localhost SERVERNAME > /etc/hosts # echo DOMAIN.COM > /etc/mailname
MySQL
How to install and configure MySQL for Postfix and Courier, step-by-step.
Installation
First download and install MySQL:
# apt-get update # apt-get install mysql-server
The standard install of MySQL is not exactly secure, so change the root password:
# mysql -u root
mysql> use mysql;
mysql> update user set password=password('newpassword')
-> where user='root';
mysql> flush privileges;
mysql> quit;
# mysql -u root -p
Enter password:
Debian installs of MySQL have a test database already created. If you want to remove this, type
mysql> drop database test;
At this point we're ready to have some fun.
Creating the MySQL Database
We are going to create a database that will be used by both Postfix and Courier to retrieve information about the e-mail recipients (user name, password, location of the mail files, and so forth). We're going to be really creative and call this database "maildb".
Inside maildb there will be three tables:
Users - the table with the user-level information
Virtual - Defines rewriting of recipient addresses for all local,
virtual, and remote mail destinations.
Transport - Specifies mapping from e-mail addresses to message
delivery transports or relay hosts.
From within MySQL, run the following:
create database maildb;
use maildb;
CREATE TABLE transport
(
domain varchar(128) NOT NULL default "",
transport varchar(128) NOT NULL default "",
UNIQUE KEY domain (domain)
);
CREATE TABLE virtual
(
address varchar(255) NOT NULL default "",
goto varchar(255) NOT NULL default "",
UNIQUE KEY address (address)
);
CREATE TABLE users
(
id varchar(128) NOT NULL default "",
address varchar(128) NOT NULL default "",
crypt varchar(128) NOT NULL default "",
clear varchar(128) NOT NULL default "",
name varchar(128) NOT NULL default "",
uid smallint(5) unsigned NOT NULL default '999',
gid smallint(5) unsigned NOT NULL default '100',
home varchar(128) NOT NULL default '/',
domain varchar(128) NOT NULL default "",
maildir varchar(255) NOT NULL default "",
PRIMARY KEY (id),
UNIQUE KEY id (id),
UNIQUE KEY address (address)
);
Next create a MySQL user to be used by Postfix and Courier. Once again, creativity rules the day; our user will be named "mailuser":
mysql> grant all on maildb.* to mailuser@localhost identified by 'PASSWORD'; mysql> flush privileges;
Remember the PASSWORD (you should use your own value, not simply the word "PASSWORD"). You'll need it later for both the Postfix and Courier configuration.
The final step needed is to define the transport for your domain(s). This is accomplished by adding a line (per domain) to the transport table:
mysql> INSERT INTO transport VALUES ( 'DOMAIN.COM', 'virtual' );
"Virtual" is the name of the user/transport mechanism which will be defined in Postfix.
Testing the MySQL Database
To test the database:
# mysql -D maildb -u mailuser -p Password: mysql> select * from users; Empty set (0.00 sec)
If an error occurs, a mistake has been made in following the instructions.
For now, that's it. We'll come back to the database later.
Postfix
How to install and configure Postfix, step-by-step.
Installation
To install Postfix and its needed components:
# apt-get install postfix postfix-mysql postfix-pcre postfix-tls
There may be some Debian configuration screens to work through such as locale configuration. Work through these using sensible choices (I can't define specific ones that will appear due to the original configuration of your machine). Many of these options will be revisited as we edit the configuration files.
If the Postfix configuration is shown, I selected "Internet Site" as the type of configuration. Read the instructions and make your choice. Most of the defaults are acceptable (we'll make further modifications later on).
When asked for the "Mail Name", enter your domain name. Destinations can be the default. Again, these can be changed later on (and will be if virtual hosts are being used).
Configuration
Perform the following steps (when editing, I use VI; substitute your choice of text editor):
# cd /etc/postfix # vi main.cf
Create or edit the following lines:
myhostname=localhost local_transport=virtual transport_maps=mysql:/etc/postfix/transport.cf virtual_mailbox_maps=mysql:/etc/postfix/mysql_virt.cf virtual_uid_maps=mysql:/etc/postfix/uids.cf virtual_gid_maps=mysql:/etc/postfix/gids.cf virtual_mailbox_base=/ mydestination=$mydomain,$myhostname,$transport_maps virtual_maps=mysql:/etc/postfix/virtual.cf
Create the following files with the noted contents (replace the word PASSWORD with your MySQL password):
transport.cf:
user=mailuser password=PASSWORD dbname=maildb table=transport select_field=transport where_field=domain hosts=127.0.0.1
mysql_virt.cf:
user=mailuser password=PASSWORD dbname=maildb table=users select_field=maildir where_field=address hosts=127.0.0.1
uids.cf:
user=mailuser password=PASSWORD dbname=maildb table=users select_field=uid where_field=address hosts=127.0.0.1
gids.cf:
user=mailuser password=PASSWORD dbname=maildb table=users select_field=gid where_field=address hosts=127.0.0.1
virtual.cf:
user=mailuser password=PASSWORD dbname=maildb table=virtual select_field=goto where_field=address hosts=127.0.0.1
Note: If the MySQL server is remote, then each of the 5 created files must be modified appropriately. In particular, each should have a "hosts=" line with the appropriate definitions. For example (machine1 = e-mail server, machine2 and machine3 are database servers):
hosts=machine2 machine3For more information please refer to the Postfix man page on mysql_table (http://www.postfix.org/mysql_table.5.html).
After creating the files, set the permissions so only Postfix can read them -- they do have passwords in, after all.
# chmod 0640 transport.cf mysql_virt.cf uids.cf gids.cf virtual.cf # chgrp postfix transport.cf mysql_virt.cf uids.cf gids.cf virtual.cf
After that work, it's a good idea to have Postfix validate the configuration files:
# postfix check
If all turns out okay (i.e., no error messages) then reload the configurations:
# postfix reload
We finish up by creating the user that Postfix will use for e-mail, as well as some directories.
# adduser --system --no-create-home --uid 999 --gid 100 --disabled-login virtual # mkdir /var/spool/postfix/virtual # chmod 0700 /var/spool/postfix/virtual # chown virtual /var/spool/postfix/virtual
Useful Postfix Notes
If you're having trouble with Postfix you can turn on debugging. However, if you have many systems hitting your machine simultaneously, the amount of debugging information can be overwhelming.
To set debugging on only when a particular IP address is connected, add the following lines to the main.cf file:
debug_peer_list=192.168.99.10 debug_peer_level=3
The first line (which can be a comma-delimited list of addresses as well as other formats) says to Postfix that it should set the debug logging level if it gets requests from the list of IP addresses. The second line defines what the debug logging level should be. For more information, please refer to the Postfix Documentation (http://www.postfix.org/documentation.html).
Courier MTA
How to install and configure Courier for POP3 IMAP E-mail server, step-by-step.
Installation
We start the installation of Courier by getting the software packages.
# apt-get install courier-imap courier-imap-ssl courier-pop \ courier-pop-ssl courier-authmysql
As with Postfix, you might be faced with some configuration screens. Accept the default values as we will be modifying the configuration by hand.
Configuration
Once software installation is complete it's time to configure.
# cd /etc/courier # vi authmysqlrc
Add or update the following values:
MYSQL_SERVER localhost MYSQL_USERNAME mailuser MYSQL_PASSWORD PASSWORD MYSQL_SOCKET /var/run/mysqld/mysqld.sock MYSQL_DATABASE maildb MYSQL_USER_TABLE users MYSQL_CRYPT_PWFIELD crypt MYSQL_CLEAR_PWFIELD clear MYSQL_UID_FIELD uid MYSQL_GID_FIELD gid MYSQL_LOGIN_FIELD id MYSQL_HOME_FIELD home MYSQL_NAME_FIELD name MYSQL_MAILDIR_FIELD maildir DEFAULT_DOMAIN DOMAIN.COM
Note: There must be no trailing whitespace after the variable values.
Note: If the MySQL server is remote, then the MYSQL_SERVER value must be set appropriately.
Note: The last variable, DEFAULT_DOMAIN, will set up the default domain value. If someone attempts to log in simply as "user" then Courier uses "user@DEFAULT_DOMAIN". This is more useful if you are hosting only a single domain.
To continue:
# vi authdaemonrc
Add or update the following lines:
authmodulelist="authmysql" version="authdaemond.mysql"
Note: There must be no trailing whitespace after the variable values.
Finally, we need to restart all the Courier daemons. We can either do this by hand (one at a time), type the following single line:
# for var in /etc/init.d/courier-*; do $var restart; done
or create a slightly more elaborate script to do it for us.
I chose the latter option, and placed the script in /etc/init.d/courier (don't forget to make it executable). The courier daemon script can be found here.
# chmod 755 /etc/init.d/courier # . /etc/init.d/courier restart
To test our Courier setup, please go to the Testing Courier section below.
Relay Control
How to set up Relay Control for POP3 IMAP SMTP E-mail server, step-by-step.
If the above instructions have been followed and you've been experimenting, then you may have noticed that if you try to send an e-mail from a remote box, to a remote e-mail address, through your new SMTP server you will get a "Relay access denied" message. This is because we've set the box up to only relay messages from the local host. We can set it up to relay from the local subnet if we want, or even from all IP addresses (which is very bad; spammers love open relays).
Since we are trying to create an Internet e-mail server, we can't always guarantee the IP address our users will have when trying to send e-mails. What we need is a way to limit the SMTP relay connections to those who actually have accounts on our server.
There are many ways to control who can and can not relay e-mail through our SMTP server. The more secure way requires SMTP Authentication with encryption, which we hope to add at some point. An easier way, and one which is used by a high number of SMTP servers, is known as POP Before SMTP. Basically this requires that any machine that wishes to send e-mail through the SMTP server must first identify itself by logging in to either the POP server or IMAP server. The IP address is then stored, temporarily, so that SMTP access is granted. This is the approach we're going to take for now.
POP Before SMTP
With our setup (Debian, Postfix SMTP, Courier POP/IMAP) there are several methods of implementing POP Before SMTP. I've chosen to use the simplest one, and install the pop-before-smtp Debian package (currently version 1.36). This is a Perl solution which has been configured specifically for Debian. The following instructions, then, detail how to implement it on our system.
Install pop-before-smtp.
# apt-get install pop-before-smtp
Edit the /etc/pop-before-smtp/pop-before-smtp.conf file:
- Look for a line like the following:
# $file_tail{'name'} = '/var/log/mail.log';
if it exists, uncomment it (remove the #), else create the line
before the comment which says "... or we'll try to figure it out for you."
- Find the $pat section.
- Uncomment the Courier-POP3 and Courier-IMAP line(s).
At this point it is a good idea to test the pop-before-smtp installation. To do this, make sure the daemon is stopped and then run it in non-daemon mode:
# /etc/init.d/pop-before-smtp stop # pop-before-smtp --debug --nowrite --reprocess
Hit Ctrl-C to end the above test. For further testing, please refer to the Testing Pop-Before-SMTP section below.
To finish the configuration of pop-before-smtp, we need to start the daemon:
# /etc/init.d/pop-before-smtp start
Add or edit the following 2 lines in /etc/postfix/main.cf:
check_client_access=hash:/var/lib/pop-before-smtp/hosts
smtpd_recipient_restrictions=permit_mynetworks,reject_non_fqdn_recipient,
check_client_access hash:/var/lib/pop-before-smtp/hosts,
reject_unauth_destination
Reload postfix configuration files:
# postfix reload
Update: The latest version of pop-before-smtp was released recently (May 13, 2005). The following notes are how to upgrade. The administrator must be careful when transferring over changes to the configuration and executable files to maintain the Debian-specific options such as the mail.log file name. This is not recommended unless you have some idea as to what you are doing.
The following instructions have not been rigorously tested. Your mileage may vary.
- Backup your old configuration file and executable:
# cp /etc/pop-before-smtp/pop-before-smtp.conf \ /etc/pop-before-smtp/pop-before-smtp.conf.OLD # cp /usr/sbin/pop-before-smtp /usr/sbin/pop-before-smtp.OLD
- Stop the daemon;
# /etc/init.d/pop-before-smtp stop
- Download the pop-before-smtp-1.37.tar.gz file from popbsmtp.sourceforge.net
- Unpack the tar file
# tar -zxvf pop-before-smtp-1.37.tar.gz # cd pop-before-smtp-1.37
- Copy over the new executable and configuration
# cp pop-before-smtp /usr/sbin # cp pop-before-smtp-conf.pl /etc/pop-before-smtp/pop-before-smtp.conf
- Edit the /usr/sbin/pop-before-smtp file. In particular,
find the line:
my $config_file = '/etc/pop-before-smtp-conf.pl';
change to:
my $config_file = '/etc/pop-before-smtp/pop-before-smtp.conf';
find the line:
$dbfile = '/etc/postfix/pop-before-smtp';
change to:
$dbfile = '/var/lib/pop-before-smtp/hosts';
- Edit the new configuration file by comparing line-by-line with the old. In particular, look out for the following:
find the line:
#$dbfile = '/etc/postfix/pop-before-smtp';
change to:
#$dbfile = '/var/lib/pop-before-smtp/hosts';
find the line:
#$file_tail{'name'} = '/var/log/maillog';
change to:
$file_tail{'name'} = '/var/log/mail.log';
- Find the $pat and $out_pat patterns for Courier-POP3 and Courier-IMAP; uncomment them.
- To ensure everything is working, run pop-before-smtp as before:
# pop-before-smtp --debug --nowrite --reprocess
- If everything seems okay, then Ctrl-C out of the debug version and restart the daemon.
# /etc/init.d/pop-before-smtp start
Once again, this upgrade has not been rigorously tested; implement it at your own risk.
SMTP Authentication
Keep in mind this is "either or" (I believe) - you don't want to have this method of SMTP Authentication and "Pop before SMTP."
If you've wandered around the internet, trying to figure this out, you've probably found a lot of comments to the effect of "I couldn't figure this out, and I tried this, and now it works." Except that it doesn't. The one that worked for me was the instructions on the second half of this page (http://www.howtoforge.com/virtual_postfix_mysql_quota_courier_p2) - the configuration parts in step 4, and all of step 5. I'll spell it out, as we had some differences.
Required Packages
The Debian packages that are required (you may alread have them) are: postfix-tls, libsasl2, libsasl2-modules, libsasl2-modules-sql, sasl2-bin, libpam-mysql, and openssl.
apt-get install postfix-tls libsasl2 libsasl2-modules \ libsasl2-modules-sql sasl2-bin libpam-mysql openssl
Postfix Configuration Changes
These lines need to be added to main.cf
# SMTP authentication smtpd_sasl_auth_enable = yes broken_sasl_auth_clients = yes smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination smptd_use_tls = yes smptd_tls_cert_file = /etc/postfix/smtpd.cert smptd_tls_key_file = /etc/postfix/smtpd.key
Create SSL certificate
Create the requisite SSL certificate (this probably needs to be explained in more detail - you will need to provide various information):
# cd /etc/postfix # openssl req -new -outform PEM -out smtpd.cert -newkey rsa:2048 \ -nodes -keyout smtpd.key -keyform PEM -days 365 -x509
Make it so the key is not world readable.
# chmod o= /etc/postfix/smtpd.key
Configure Saslauthd
I believe that most of these changes are because Debian runs Postfix in a chrooted environment...
# mkdir -p /var/spool/postfix/var/run/saslauthd
Edit /etc/default/saslauthd so that it looks like this (" vi/etc/default/saslauthd"):
START=yes MECHANISMS="pam" PARAMS="-m /var/spool/postfix/var/run/saslauthd -r"
Create /etc/pam.d/smtp so that it looks like this (you man need to make changes depending on your database setup) - ("vi /etc/pam.d/smtp"):
auth required pam_mysql.so user=mailuser passwd=PASSWORD host=127.0.0.1 db=maildb table=users usercolumn=id passwdcolumn=crypt crypt=1 account sufficient pam_mysql.so user=mailuser passwd=PASSWORD host=127.0.0.1 db=maildb table=users usercolumn=id passwdcolumn=crypt crypt=1
Create /etc/postfix/sasl/smtpd.conf so that it looks like this ("vi /etc/postfix/sasl/smtpd.conf"):
pwcheck_method: saslauthd mech_list: plain login allow_plaintext: true
Restart postfix and saslauthd:
# /etc/init.d/postfix restart # postfix check # /etc/init.d/saslauthd restart
System Testing
System Testing Components for POP3 IMAP SMTP E-mail server, step-by-step.
Testing Postfix/SMTP
To test the Postfix/SMTP installation we first need to create a test user. This can be done one of two ways: Manually, or by using the addmailuser script.
To create a user manually, the procedure is:
- Enter the database.
# mysql -D maildb -u mailuser -p Password:
- Create a test user.
mysql> INSERT INTO users VALUES ( 'test@DOMAIN.COM', 'test@DOMAIN.COM',
-> ENCRYPT( 'password' ), 'password', 'test name', 999, 100,
-> '/var/spool/postfix/virtual/DOMAIN.COM/test', 'DOMAIN.COM',
-> '/var/spool/postfix/virtual/DOMAIN.COM/test/Maildir/' );
mysql> INSERT INTO virtual VALUES ( 'test@DOMAIN.COM', 'test@DOMAIN.COM' );
This will create a test user with the password "password" (unless you change it). You should replace DOMAIN.COM with your domain.
- Set up the transport values for the domain. This only needs to be done once per domain, so if you did it earlier then you can skip this step.
mysql> INSERT INTO transport VALUES ( 'DOMAIN.COM', 'virtual' );
Next we need to create the directories or else Postfix won't be able to deliver the mail it receives. We create the directories using the maildirmake utility which will create QMail structure mail directories.
# mkdir /var/spool/postfix/virtual/DOMAIN.COM # chown 999 /var/spool/postfix/virtual/DOMAIN.COM # mkdir /var/spool/postfix/virtual/DOMAIN.COM/test # maildirmake /var/spool/postfix/virtual/DOMAIN.COM/test/Maildir # maildirmake -f Trash /var/spool/postfix/virtual/DOMAIN.COM/test/Maildir # maildirmake -f Drafts /var/spool/postfix/virtual/DOMAIN.COM/test/Maildir # maildirmake -f Sent /var/spool/postfix/virtual/DOMAIN.COM/test/Maildir # chown -R 999.100 /var/spool/postfix/virtual/DOMAIN.COM/test # chmod 0700 /var/spool/postfix/virtual/DOMAIN.COM/test
We are now ready to test Postfix.
# telnet localhost smtp
Here is a sample session (you should enter the lines beginning with ">>")
Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 localhost ESMTP Postfix (Debian/GNU) >> HELO localhost 250 localhost >> MAIL FROM: test@DOMAIN.COM 250 Ok >> RCPT TO: test@DOMAIN.COM 250 Ok >> DATA 354 End data with <CR><LF>.<CR><LF> >> testing... >> . 250 Ok: queued as E80291B070 >> QUIT 221 Bye
If this did not happen then please look in the /var/log/mail.log file to see what's going wrong.
Really daring people can use a remote machine to perform the test. Just make sure you telnet to the right server...
Testing Courier
To test our Courier setup we, once again, use telnet. We'll try two different ways: using POP3 and IMAP. Again, you should enter values designated by ">>"
POP3:
# telnet localhost pop-3
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
+OK Hello there.
>> USER test@DOMAIN.COM
+OK Password required.
>> PASS password
+OK logged in.
>> LIST
+OK POP3 clients that break here, they violate STD53.
1 425
2 432
.
>> RETR 1
+OK 425 octets follow.
Return-Path: <test@DOMAIN.COM>
X-Original-To: test@DOMAIN.COM
Delivered-To: test@DOMAIN.COM
Received: from localhost (unknown [192.168.99.99])
by localhost (Postfix) with SMTP id ADE201B070
for <test@DOMAIN.COM>; Tue, 7 Jun 2005 20:13:48 -0500 (CDT)
Message-Id: <20050608011348.ADE201B070@localhost>
Date: Tue, 7 Jun 2005 20:13:48 -0500 (CDT)
From: test@DOMAIN.COM
To: undisclosed-recipients:;
testing...
.
>> QUIT
+OK Bye-bye.
IMAP:
# telnet localhost imap
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
* OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT
THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright
1998-2004 Double Precision, Inc. See COPYING for distribution information.
>> a LOGIN test@DOMAIN.COM test
a OK LOGIN Ok.
>> a SELECT INBOX
* FLAGS (\Draft \Answered \Flagged \Deleted \Seen \Recent)
* OK [PERMANENTFLAGS (\* \Draft \Answered \Flagged \Deleted \Seen)] Limited
* 2 EXISTS
* 2 RECENT
* OK [UIDVALIDITY 1118196844] Ok
* OK [MYRIGHTS "acdilrsw"] ACL
a OK [READ-WRITE] Ok
>> a FETCH 1 body[1]
* 1 FETCH (BODY[1] {12}
testing...
)
a OK FETCH completed.
>> a LOGOUT
* BYE Courier-IMAP server shutting down
a OK LOGOUT completed
Connection closed by foreign host.
Testing Pop Before SMTP
To test pop-before-smtp in non-daemon mode, type the following:
# pop-before-smtp --debug --nowrite --reprocess
You should see messages about IPs the script finds. If you don't see any messages, check to see that you've chosen the correct $pat pattern and are looking at the right mail log file (see above).
While running the above command, type the following from another console on the local machine:
# telnet localhost imap Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. * OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2004 Double Precision, Inc. See COPYING for distribution information. >> a LOGIN test password a OK LOGIN Ok. >> a LOGOUT * BYE Courier-IMAP server shutting down a OK LOGOUT completed Connection closed by foreign host.
Switch back to the original console; you should see a message saying something about ignoring the local network:
Feb 16 05:12:09 ignoring local-net ip=127.0.0.1
Repeat the test from a remote machine (using the mail server's IP address rather than localhost). This time you should see a message about the IP address being added to the database:
Feb 16 05:12:20 found ip=192.168.99.99 Feb 16 05:12:20 added 192.168.99.99 to DB
Hit Ctrl-C in the console running pop-before-smtp to stop the test.
To fully test pop-before-smtp's capabilities, you need to run the same test as before from an IP address that hasn't connected to the e-mail server recently (by default pop-before-smtp keeps IP addresses for 30 minutes).
On the e-mail server, start up pop-before-smtp as a daemon:
# /etc/init.d/pop-before-smtp start
Then do the following from the remote machine:
$ telnet 192.168.99.99 smtp Trying 192.168.99.99... Connected to 192.168.99.99. Escape character is '^]'. 220 localhost ESMTP Postfix (Debian/GNU) >> MAIL FROM: test@DOMAIN.COM 250 Ok >> RCPT TO: some_address@some_remote_domain.com 554 <some_address@some_remote_domain.com>: Relay access denied >> QUIT 221 Bye Connection closed by foreign host.
To get pop-before-smtp to update its database, you must login from the remote IP address either through POP3 or IMAP:
$ telnet 192.168.99.99 pop-3 Trying 192.168.99.99... Connected to 192.168.99.99. Escape character is '^]'. +OK Hello there. >> USER test +OK Password required. >> PASS password +OK logged in. >> QUIT +OK Bye-bye. Connection closed by foreign host.
or:
$ telnet 192.168.99.99 imap Trying 192.168.99.99... Connected to 192.168.99.99. Escape character is '^]'. * OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2004 Double Precision, Inc. See COPYING for distribution information. >> a LOGIN test password a OK LOGIN Ok. >> a LOGOUT * BYE Courier-IMAP server shutting down a OK LOGOUT completed Connection closed by foreign host.
Now you should be able to perform the SMTP login:
$ telnet 192.168.99.99 smtp Trying 192.168.99.99... Connected to 192.168.99.99. Escape character is '^]'. 220 localhost ESMTP Postfix (Debian/GNU) >> MAIL FROM: test@DOMAIN.COM 250 Ok >> RCPT TO: some_address@some_remote_domain.com 250 Ok >> DATA 354 End data with <CR><LF>.<CR><LF> >> blah blah >> . 250 Ok: queued as 3DCFE103C9 >> QUIT 221 Bye Connection closed by foreign host.
Testing SMTP Authentication
I did this a lot. First...
tail -f /var/log/mail.log
Then go to another terminal window (or machine):
telnet my.mail.server 25
The response should be:
Trying ip.add.re.ss... Connected to my.mail.server. Escape character is '^]'. 220 localhost ESMTP Postfix (Debian/GNU)
Type:
ehlo localhost
The response should be:
250-localhost 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-AUTH LOGIN PLAIN 250-AUTH=LOGIN PLAIN 250 8BITMIME
The original documentation says to look for these lines to make sure everything is okay.
250-STARTTLS 250-AUTH
Originally I didn't have "250-STARTTLS" and it didn't work, but now I've made changes, it does work, and I still don't have "250-STARTTLS." Go figure.
I had a variety of error messages, which I shall now try to replicate for your edification...
This one seems fairly straightforward. It means either saslauthd isn't running, or it isn't running in the postfix chroot.
Dec 5 23:13:52 ruber postfix/smtpd[11099]: warning: SASL authentication failure: \ cannot connect to saslauthd server: Connection refused Dec 5 23:13:52 ruber postfix/smtpd[11099]: warning: ip.add.re.ss: \ SASL LOGIN authentication failed
If you know saslauthd is running "ps aux" should show the processes, then kill it:
/etc/init.d/saslauthd stop
(I would check with "ps aux" again, because I had problems stopping this and often just killed it manually.)
Check /etc/defaults/saslauthd to make sure the following line exists:
PARAMS="-m /var/spool/postfix/var/run/saslauthd -r"
Conclusion
If all of the system tests worked then you have a functioning POP3/IMAP/SMTP E-mail server. Congratulations.
You should now be able to set up your favourite e-mail client and begin sending and receiving e-mails (after, of course, making any DNS changes to your network and/or to the MX record at your registrar).
To add new users you need to add new rows to the USERS and VIRTUAL tables in MySQL, and to create the directories. To make this easier I have created some scripts (see below) to help add and delete users.
Enjoy.
Future Enhancements
Some future enhancements I have in mind are:
X Add SMTP Authentication (done 2005-12-05) Create a script that will perform most/all the setup Document the method for adding additional (virtual) domains X Add spam filtering (SpamAssassin) (done 2006-02-09) X Add virus checking (ClamAV) (done 2006-02-09)
Scripts
Courier Daemon Script
The courier daemon script will allow you to start/stop/restart the several Courier daemons.
#!/bin/sh
case "$1" in
start)
for var in /etc/init.d/courier-*; do
$var start;
done
;;
stop)
for var in /etc/init.d/courier-*; do
$var stop;
done
;;
restart)
for var in /etc/init.d/courier-*; do
$var restart;
done
;;
reload)
for var in /etc/init.d/courier-*; do
$var reload;
done
;;
force-reload )
for var in /etc/init.d/courier-*; do
$var force-reload;
done
;;
*)
echo "Usage: $0 {start|stop|restart|reload|force-reload}" >&2
exit 1
;;
esac
exit 0
Add Mail User Script
The addmailuser script makes it easy to add a new mail user. It is quick and dirty, needs a lot of work, but it gets the job done.
# addmailuser
# A very (very) simple script to create an e-mail user.
# Creates the directory structure and the database entry (using
# the /etc/courier file for username/password/database name.
# It's not secure or foolproof, but it's quick and gets the job done.
#
#!/bin/sh
# Including the -X values, we expect/require 6 command-line args.
# Let's make sure we get them.
TOTAL_ARGS=6
Username=""
Domain=""
Password=""
if [ "$#" -ne "$TOTAL_ARGS" ]
then
echo "Usage $0 -u {Username} -d {Domain} -p {password}"
exit 1
fi
while getopts "u:d:p:" Option
do
case $Option in
u) Username="${OPTARG}";;
d) Domain=${OPTARG};;
p) Password=${OPTARG};;
*) break;;
esac
done
if [ "${#Username}" -eq 0 ]
then
echo "Usage $0 -u {Username} -d {Domain} -p {password}"
echo "Username required"
exit 1
fi
if [ "${#Domain}" -eq 0 ]
then
echo "Usage $0 -u {Username} -d {Domain} -p {password}"
echo "Domain required"
exit 1
fi
if [ "${#Password}" -eq 0 ]
then
echo "Usage $0 -u {Username} -d {Domain} -p {password}"
echo "Password required"
exit 1
fi
# Now we've got what is (we hope) all needed information, let's begin.
# First we create the directories
PARENT_DIR=/var/spool/postfix/virtual/
DOMAIN_DIR=$PARENT_DIR$Domain/
USER_DIR=$DOMAIN_DIR$Username/
MAIL_DIR=$USER_DIR"Maildir/"
mkdir $DOMAIN_DIR 2>/dev/null
chown 999 $DOMAIN_DIR
mkdir $USER_DIR
maildirmake $MAIL_DIR
maildirmake -f Trash $MAIL_DIR
maildirmake -f Drafts $MAIL_DIR
maildirmake -f Sent $MAIL_DIR
chown -R 999.100 $USER_DIR
chmod 0700 $USER_DIR
# Finally we need to create the database entries. To do this we will need to get the
# database name, user, and password from the /etc/courier/authmysqlrc file. These
# values are stored in the MYSQL_SERVER, MYSQL_USERNAME, MYSQL_PASSWORD entries.
DATAFILE=/etc/courier/authmysqlrc
DB=`grep MYSQL_DATABASE $DATAFILE | grep -v : `
USER=`grep MYSQL_USERNAME $DATAFILE | grep -v : `
PW=`grep MYSQL_PASSWORD $DATAFILE | grep -v : `
# Clean up tabs/whitespace, etc. There are quicker/cleaner ways of doing this, but
# this is a quick 'n' dirty script.
DB=${DB//MYSQL_DATABASE/}
DB=${DB// /} # The spaces actually are a single tab.
DB=${DB// /} # This one's just a space.
USER=${USER//MYSQL_USERNAME/}
USER=${USER// /} # The spaces actually are a single tab.
USER=${USER// /} # This one's just a space.
PW=${PW//MYSQL_PASSWORD/}
PW=${PW// /} # The spaces actually are a single tab.
PW=${PW// /} # This one's just a space.
CMDLINE="-s --database=$DB -u $USER --password=$PW -e"
QRY1="INSERT INTO virtual VALUES( '$Username@$Domain', '$Username@$Domain' )"
QRY2="INSERT INTO users VALUES( '$Username@$Domain', '$Username@$Domain',
ENCRYPT( '$Password' ), '$Password', ' ', 999, 100, '$USER_DIR', '$Domain',
'$MAIL_DIR' )"
mysql $CMDLINE "$QRY1"
if [ $? -ne "0" ]
then
echo "Error occurred in insert query:"
echo $CMDLINE \"$QRY1\"
exit 1
fi
mysql $CMDLINE "$QRY2"
if [ $? -ne "0" ]
then
echo "Error occurred in insert query:"
echo $CMDLINE \"$QRY1\"
exit 1
fi
echo "Okay"
Delete Mail User Script
The delmailuser script makes it easy to delete a mail user. As with the addmailuser script, it is quick and dirty, but it gets the job done.
# delmailuser
# A very (very) simple script to delete an e-mail user.
# Removes the directory structure and the database entry (using
# the /etc/courier file for username/password/database name.
# It's not secure or foolproof, but it's quick and gets the job done.
#
#!/bin/sh
# Including the -X values, we expect/require 4 command-line args.
# Let's make sure we get them.
TOTAL_ARGS=4
Username=""
Domain=""
if [ "$#" -ne "$TOTAL_ARGS" ]
then
echo "Usage $0 -u {Username} -d {Domain}"
exit 1
fi
while getopts "u:d:" Option
do
case $Option in
u) Username="${OPTARG}";;
d) Domain=${OPTARG};;
*) break;;
esac
done
if [ "${#Username}" -eq 0 ]
then
echo "Usage $0 -u {Username} -d {Domain} -p {password}"
echo "Username required"
exit 1
fi
if [ "${#Domain}" -eq 0 ]
then
echo "Usage $0 -u {Username} -d {Domain} -p {password}"
echo "Domain required"
exit 1
fi
echo "Are you sure you want to delete user $Username? (yes/no) [default: no]"
read ynval
if [ "$ynval" != "yes" ]
then
echo "Answer was not \"yes\"; aborting."
exit 0
fi
# Now we've got what is (we hope) all needed information, let's begin.
# First we create the directory variables
PARENT_DIR=/var/spool/postfix/virtual/
DOMAIN_DIR=$PARENT_DIR$Domain/
USER_DIR=$DOMAIN_DIR$Username/
rm -fr $USER_DIR
# Next we delete the database values. We grab needed MySQL info in the
# same way as the addmailuser script.
DATAFILE=/etc/courier/authmysqlrc
DB=`grep MYSQL_DATABASE $DATAFILE | grep -v : `
USER=`grep MYSQL_USERNAME $DATAFILE | grep -v : `
PW=`grep MYSQL_PASSWORD $DATAFILE | grep -v : `
# Clean up tabs/whitespace, etc. There are quicker/cleaner ways of doing this, but
# this is a quick 'n' dirty script.
DB=${DB//MYSQL_DATABASE/}
DB=${DB// /} # The spaces actually are a single tab.
DB=${DB// /} # This one's just a space.
USER=${USER//MYSQL_USERNAME/}
USER=${USER// /} # The spaces actually are a single tab.
USER=${USER// /} # This one's just a space.
PW=${PW//MYSQL_PASSWORD/}
PW=${PW// /} # The spaces actually are a single tab.
PW=${PW// /} # This one's just a space.
CMDLINE="-s --database=$DB -u $USER --password=$PW -e"
QRY1="DELETE FROM virtual where address = '$Username@$Domain'"
QRY2="DELETE FROM users where id = '$Username@$Domain'"
mysql $CMDLINE "$QRY1"
if [ $? -ne "0" ]
then
echo "Error occurred in delete query:"
echo $CMDLINE \"$QRY1\"
exit 1
fi
mysql $CMDLINE "$QRY2"
if [ $? -ne "0" ]
then
echo "Error occurred in delete query:"
echo $CMDLINE \"$QRY2\"
exit 1
fi
echo "Okay"
Recommended Reading
The following references were useful in creating this document:
- Other How-tos
- Tutorial: ISP-style Email Service with Debian-Sarge and Postfix 2.1 (http://workaround.org/articles/ispmail-sarge/)
- The Perfect Rails/debian/lighttpd Stack... (http://brainspl.at/pages/perfect_vps) - Scroll down to the "Postfix/POP3/IMAP" section
- Virtual Users And Domains With Postfix, Courier And MySQL (+ SMTP-AUTH, Quota, SpamAssassin, ClamAV) (http://www.howtoforge.com/virtual_postfix_mysql_quota_courier) - this example uses encrypted passwords in MySQL for SMTP AUTH - (derived from "ISP-style Email")
- Primary References
- Postfix (http://www.postfix.org)
- Postfix Configuration Parameters (http://www.postfix.org/postconf.5.html)
- MySQL (http://www.mysql.com)
- Courier MTA (http://www.courier-mta.org)
- Pop-Before-SMTP (http://popbsmtp.sourceforge.net)
- Postfix (http://www.postfix.org)
- SMTP Authentication
- SMTP Authentication with Postfix using files or MySQL (http://small.dropbear.id.au/myscripts/postfixmysql.html)
- Proposal: Mail Passwords Encrypted using saslauthd (http://www.syscp.de/docs/public/contrib/cryptedmailpws)
- Postfix SASL Readme (http://www.postfix.org/SASL_README.html)

