Mail server setup guide
From NickWiki
Purpose
There are plenty of guides to setting up robust mail servers on the interblag, but they all seem to get things just a bit wrong, or have features that I'd like to use in a different type of setup. For a while now, I've had a functional server cobbled together, but now that I'm relocating my server I have a chance to go with a slightly more stable approach.
After a failed initial attempt to synthesize an ideal setup guide from the several that I've used in the past, I've decided to document my steps here for two reasons. The first is so that I can keep a record of the steps I've taken and backtrack whenever I mess something up. The second is that that list of steps can then be compiled into a half-decent guide that (in my opinion, at least,) does things "right" and that I can use in the future.
So for now this will be kind of a scratch space for my work, but over time it should congeal into something more coherent and useful.
Overview
I will be setting up a Postfix-based mail server that stores the information for multiple domains and users in a MySQL database. Courier [soon to be Dovecot] will be used for IMAP and perhaps POP, although I'm not sure if I want to have support for the latter. Connections will be encrypted through SSL and STARTTLS, and secure authentication will be done through Cyrus SASL. There will be a webmail interface available (currently SquirrelMail, but may change to something slightly nicer to use). Mail will be stored in vmail directories based on domain and username to allow for sensible organization of users.
This guide is heavily based on those found at Flurdy and slicehost, preferring mostly to follow Flurdy, but with regard to directory structure and SASL, along with a few other things, it follows the slicehost guide.
Notes
Many commands will require you to elevate your privileges to root. I have made an attempt to show root prompts (#) where root is necessary, and regular prompts ($) where it is not, but just use common sense if something isn't working.
This guide is written for a Debian-based system (specifically Ubuntu 8.04 LTS), but aside from the use of aptitude and some folder locations, should work on other distros.
Preliminary Setup
- Make sure SSH is set up so that you can remotely log into your server. This is usually as easy as doing something like
# aptitude install openssh-server
- You should have a non-privileged user on your system so that you're not running as root all the time.
- It's probably a good idea to make sure you have added the restricted, universe, and multiverse repositories to your apt sources.list.
- Make sure the hostname is correctly set on the server:
# vim /etc/hostname
- Replace the hostname with the server's hostname. I'll be using www.pieps.org, since this is also a web server.
- Open /etc/mailname and set that to the name of your mail server (I'm using mail.pieps.org):
# vim /etc/mailname
- Restart to make things get set properly.
<todo> make sure RDNS works with the above - may need to set hostname so that it matches the RDNS entry.
Primary Installation
We're going to take the Flurdy approach here, and just install a lot of stuff en masse. Later, we will configure the main install, then install some straggling stuff on top.
- First off, we'll install MySQL:
# aptitude install mysql-server
- The MySQL package will ask you for a root password for your database. Make sure you remember that password! It's a pain in the ass to change if you forget it. I'll refer to my password as MysqlRootPasswd.
- Next we'll install Postfix:
# aptitude install postfix postfix-mysql postfix-tls
- It will ask you what type of mail server you're running. Select internet site.
- Since you've already set /etc/mailname, the default mail hostname value that it asks you for should work just fine.
- Now we'll install SASL and related software:
# aptitude install libsasl2-2 libsasl2-modules-sql sasl2-bin libauthen-sasl-cyrus-perl libpam-mysql
- Next up is Courier:
# aptitude install courier-imap courier-imap-ssl courier-ssl courier-authlib-mysql courier-authdaemon
- Answer No to the question about web-based administration
- Courier will warn you about your SSL certificate location. We'll deal with that in a bit.
- Next up is the firewall:
# aptitude install shorewall shorewall-doc
- Finally, we'll install openssl (for certificate signing):
# aptitude install openssl
Primary Configuration
Now we'll start configuring things.
Shorewall Part 1
It's important to get this set up now so that we have a secure server from the get-go. We'll start off only allowing SSH traffic, then once things are working we'll open up the services we need.
- Default values for Shorewall are in /usr/share/doc/shorewall-common/default-config.
- Examples are in /usr/share/doc/shorewall-common/examples.
- First we configure which network adapters will access the net:
# cp /usr/share/doc/shorewall-common/default-config/interfaces /etc/shorewall/. # vim /etc/shorewall/interfaces
Add the following line (assuming your interface is named eth0):net eth0 detect dhcp,tcpflags,logmartians,nosmurfs
- Next we configure network zones:
# cp /usr/share/doc/shorewall-common/default-config/zones /etc/shorewall/. # vim /etc/shorewall/zones
Add the firewall if not there and the internet as a zone:fw firewall # loc ipv4 net ipv4
- Since this guide does not deal with multiple zones going through a single interface, a hosts file is not necessary
- Next we set the default firewall access policy:
# cp /usr/share/doc/shorewall-common/default-config/policy /etc/shorewall/. # vim /etc/shorewall/policy
$FW net ACCEPT net $FW DROP info net all DROP info # THE FOLLOWING POLICY MUST BE LAST all all REJECT info
- For safety in case it goes down:
# cp /usr/share/doc/shorewall-common/default-config/routestopped /etc/shorewall/. # vim /etc/shorewall/routestopped
eth0 - routeback
You may put in a netmask of your ip range if you are more concerned. - Next, we add the main firewall rules. There are predefined macro rules in /usr/share/shorewall. Their usage is explained here. For now, we will allow SSH traffic:
# cp /usr/share/doc/shorewall-common/default-config/rules /etc/shorewall/. # vim /etc/shorewall/rules
SSH/ACCEPT net $FW
- Shorewall allows you to check your rules for syntax consistency:
# shorewall check
- If all is well, change /etc/default/shorewall so that the line towards the top reads
startup=1
. This allows for shorewall to be started/stopped. - You may want to set the ADMINISABSENTMINDED variable to Yes in your /etc/shorewall/shorewall.conf file. This will keep your firewall from terminating existing connections when you restart it.
- Restart the firewall:
# /etc/init.d/shorewall restart
DO NOT TERMINATE YOUR SESSION UNTIL YOU ARE SURE SHOREWALL WORKS - Now open a new terminal and try to ping your server.
$ ping mail.pieps.org
Your packets should all be dropped. - Next, try SSHing into your server from that terminal.
- If all has gone well, you should be able to access it.
- If not, something has gone wrong. In your existing session on your server (that you didn't terminate, right?), set the line in /etc/default/shorewall back to startup=0 so that you're not screwed if you restart your server, and try to figure out the problem. Once everything seems to be ok again, reset startup=1, restart shorewall, and try pinging/SSHing again.
- We will deal with further firewall configuration in a bit.
Postfix
Main config
- Open the Postfix config file /etc/postfix/main.cf
- Ubuntu puts some decent default values here, but we may override some of them.
- /etc/mailname already contains your mail server's hostname, but we'll specify the name of the server in the config file:
myhostname = mail.pieps.org
- We also need to set the origin (the part that comes after the @) properly:
# myorigin = /etc/mailname myorigin = pieps.org
- Decide what the greeting text will be for other mail servers. As Flurdy says, "enough info so it is useful, but do not divulge everything to potential hackers."
smtpd_banner = $myhostname ESMTP $mail_name
- Flurdy goes into a bit of detail about how to relay mail through your ISP's SMTP server, but I'm doing everything myself and need no relay hosts:
# leave blank to send mail yourself relayhost =
# or use an accessible SMTP server relayhost = smtp.yourisp.com
- We want to accept connections from anywhere (the internet or locally):
inet_interfaces = all
- We only want to trust ourselves for relay, access control, etc.:
mynetworks_style = host
Also, make sure to comment out any mynetworks entries you see, as they'll clobber the default specified by mynetworks_style:# mynetworks = 127..0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
- Domain Masquerading: <TODO> - not sure if I'll need to do this as long as myorigin is set correctly
- We're going to be using virtual domains, so the following need to be empty:
local_recipient_maps = mydestination=
<NOTE> The Postfix documentation here expressly says not to do this since it allows spammers to backscatter folks due to the fact that the server will accept the mail, then reply that the mail is undeliverable. This may be ok in this case, since we're going to be mapping all desired local addresses through aliases, and all other addresses will be rejected for various reasons (including the fact that mydestination is set to null, making no addresses local as far as Postfix is concerned, I think). This is a good thing to think about changing if I ever want local mail delivered automatically. As it is, however, I'd rather have local mail (sent to localhost) map to specific addresses through aliases.
<UPDATE> after a bit of research, it seems that incomplete addresses are automatically rewritten by trivial-rewrite to have @$myorigin at the end, then delivered accordingly. So that's sorted. Meanwhile, the old Flurdy-based setup I had before has all @localhost aliased at root@localhost. So this may produce some strange behavior based on whether programs send mail with user@localhost or just user, but at least I now know how this mess works. Anyway, it should be safe to keep local_recipient_maps null as long as mydestination is as well, since then no mail will be treated as local. - Now for a few numbers determining lifetimes, etc. (directly taken from Flurdy):
# how long if undelivered before sending warning update to sender delay_warning_time = 4h # will it be a permanent error or temporary unknown_local_recipient_reject_code = 450 # how long to keep message on queue before return as failed. # some have 3 days, I have 16 days as I am backup server for some people # whom go on holiday with their server switched off. maximal_queue_lifetime = 7d # max and min time in seconds between retries if connection failed minimal_backoff_time = 1000s maximal_backoff_time = 8000s # how long to wait when servers connect before receiving rest of data smtp_helo_timeout = 60s # how many address can be used in one message. # effective stopper to mass spammers, accidental copy in whole address list # but may restrict intentional mail shots. smtpd_recipient_limit = 16 # how many error before back off. smtpd_soft_error_limit = 3 # how many max errors before blocking it. smtpd_hard_error_limit = 12
- Now some restrictions. Be careful that each setting is on one line (also from Flurdy):
# Requirements for the HELO statement smtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, permit # Requirements for the sender details smtpd_sender_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit # Requirements for the connecting server # I'm not a fan of RBLs, so I'm leaving these out # smtpd_client_restrictions = reject_rbl_client sbl.spamhaus.org, reject_rbl_client blackholes.easynet.nl, reject_rbl_client dnsbl.njabl.org # Requirement for the recipient address smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, permit smtpd_data_restrictions = reject_unauth_pipelining
- More restrictions:
# require proper helo at connection smtpd_helo_required = yes # waste spammers' time before rejecting them smtpd_delay_reject = yes disable_vrfy_command = yes
- Now some maps and lookups for our virtual domains. This is a combination of Flurdy's approach and Slicehost's. Overwrite any that already exist:
# this specifies where the virtual mailbox folders will be located virtual_mailbox_base = /var/spool/mail/virtual # this is for the mailbox location for each user virtual_mailbox_maps = mysql:/etc/postfix/mysql_mailbox.cf # and their user id (virtual) # virtual_uid_maps = mysql:/etc/postfix/mysql_uid.cf virtual_uid_maps = static:5000 # and group id (virtual) # virtual_gid_maps = mysql:/etc/postfix/mysql_gid.cf virtual_gid_maps = static:5000 # and this is for aliases virtual_alias_maps = mysql:/etc/postfix/mysql_alias.cf, mysql:/etc/postfix/mysql_email.cf # and this is for domain lookups virtual_mailbox_domains = mysql:/etc/postfix/mysql_domains.cf # this is how to connect to the domains (all virtual, but the option is there) # not used yet # transport_maps = mysql:/etc/postfix/mysql_transport.cf
- We need to make sure that the aliases file points to sane addresses. Most importantly, mail to root should go somewhere sensible. Then again, since local delivery should be disabled (we have no local domains), this may be unnecessary. Do however make sure that local usernames are set up correctly in the virtual aliases table later on.
- Now we need to set up the folder where all the virtual mail will be stored. This may have been done for you by the postfix install. If you're not sure, check your /etc/passwd file for a user called virtual:
# mkdir /var/spool/mail/virtual # groupadd virtual -g 5000 # useradd virtual -u 5000 -g 5000 # chown -R virtual:virtual /var/spool/mail/virtual
Postfix MySQL config
- Now we need to set up the files that dictate how to look up information in the database. This is going to use information that we have not yet added to the MySQL database. That will be covered in the following section.
- Edit/create the user mailbox location config file (taken from Slicehost):
# vim /etc/postfix/mysql_mailbox.cf
user=mail password=MyMailPassword dbname=maildb query = SELECT CONCAT(SUBSTRING_INDEX(email,'@',-1),'/',SUBSTRING_INDEX(email,'@',1),'/') FROM users WHERE email='%s' and enabled = 1 hosts = 127.0.0.1
This file is tricky - it compiles the directory structure first from the domain of the address (the part after the @), then from the user name (the part before the @). This is nice if you want maildirs to be automatically created for users and not have to worry about name collisions. It's just as easy and more flexible to specify all this by hand (for instance, Flurdy just has a field in the database for the dir that can be populated any way the admin likes), but I really like that this automates the whole process. - Edit/create the alias config file:
# vim /etc/postfix/mysql_alias.cf
user=mail password=MyMailPassword dbname=maildb table=aliases select_field=destination where_field=email hosts=127.0.0.1 additional_conditions = and enabled = 1
- Edit/create the email config file (this keeps us from having to add entries to aliases that map from an address to itself):
# vim /etc/postfix/mysql_email.cf
user=mail password=MyMailPasssword dbname=maildb table=users select_field=email where_field=email hosts=127.0.0.1 additional_conditions = and enabled = 1
- Edit/create the domain details config file:
# vim /etc/postfix/mysql_domains.cf
user=mail password=MyMailPassword dbname=maildb table=domains select_field=domain where_field=domain hosts=127.0.0.1 additional_conditions = and enabled = 1
- Make sure all the mysl_*.cf files have their permissions set correctly
# chmod o= /etc/postfix/mysql_* # chgrp postfix /etc/postfix/mysql_*
- Edit/create the user mailbox location config file (taken from Slicehost):
- Now restart Postfix
# /etc/init.d/postfix restart
- Now log in as root:
# mysql -u root -p
And enter the password for the root account when prompted.- Now create the mail database:
create database maildb;
- Next we're going to create the user that will manage and provide access to our mail database:
GRANT SELECT,INSERT,UPDATE,DELETE ON maildb.* TO 'mail'@'localhost' IDENTIFIED by 'MyMailPassword'
Make sure to replace MyMailPassword with your desired password.
- Now create the mail database:
- Now we're going to create the tables referenced by the mysql_*.cf files:
use maildb; CREATE TABLE `aliases` ( `email` varchar(128) NOT NULL default '', `destination` varchar(128) NOT NULL default '', `enabled` tinyint(1) NOT NULL default '1', PRIMARY KEY (`email`) ) ;
CREATE TABLE `domains` ( `domain` varchar(120) NOT NULL default '', `enabled` tinyint(1) NOT NULL default '1', PRIMARY KEY (`domain`) ) ;
CREATE TABLE `users` ( `email` varchar(128) NOT NULL default '', `name` varchar(128) NOT NULL default '', `enabled` tinyint(1) NOT NULL default '1', `change_password` tinyint(1) NOT NULL default '1', `crypt` varchar(128) NOT NULL default 'sdtrusfX0Jj66', `quota` varchar(255) NOT NULL default '', `procmailrc` varchar(128) NOT NULL default '', `spamassassinrc` varchar(128) NOT NULL default '', PRIMARY KEY (`email`), ) ;
- Exit MySQL by hitting Ctrl-d or typing in exit;.
- Now open up MySQL's my.cnf file to check on some settings:
# vim /etc/mysql/my.cnf
- If it exists, make sure that the line skip-networking is commented out:
#skip-networking
- Make sure that the address is bound to localhost:
bind-address = 127.0.0.1
- If you feel like it, turn on logging for MySQL (remember to turn this off when all is well - it slows things down):
log = /var/log/mysql/mysql.log
- If it exists, make sure that the line skip-networking is commented out:
- Restart MySQL to make sure any new settings are being used:
# /etc/init.d/mysql restart
Courier
TODO: Update this with instructions for Dovecot
Courier IMAP
Now we're going to configure Courier IMAP
- Edit /etc/courier/authdaemonrc and change the module line to the following:
authmodulelist="authmysql"
Also, temporarily enable a high debug level until things work.DEBUG_LOGIN=2
- Edit /etc/courier/authmysqlrc and make sure the following lines are set. Make sure there is no whitespace at the end of lines.
MYSQL_SERVER localhost MYSQL_USERNAME mail MYSQL_PASSWORD MyMailPassword MYSQL_PORT 0 MYSQL_DATABASE maildb MYSQL_USER_TABLE users MYSQL_CRYPT_PWFIELD crypt # comment out this field - we don't store cleartext passwords #MYSQL_CLEAR_PWFIELD clear #Set this only if you want people to be able to specify only their username when logging in. #Not a good idea if you have multiple domains you're using. DEFAULT_DOMAIN pieps.org MYSQL_UID_FIELD 5000 MYSQL_GID_FIELD 5000 MYSQL_LOGIN_FIELD email MYSQL_HOME_FIELD "/var/spool/mail/virtual" MYSQL_NAME_FIELD name MYSQL_MAILDIR_FIELD CONCAT(SUBSTRING_INDEX(email,'@',-1),'/',SUBSTRING_INDEX(email,'@',1),'/') MYSQL_QUOTA_FIELD quota MYSQL_WHERE_CLAUSE enabled=1
Courier POP
Not implemented in this guide
Restart
- Now restart courier, since we've added a bunch of new settings:
# /etc/init.d/courier-authdaemon restart # /etc/init.d/courier-imap restart # /etc/init.d/courier-imap-ssl restart
Shorewall Part 2
Now that we have all of our services configured properly (hopefully), we can start opening up ports. Thanks to Shorewall's macro facilities, this is very simple.
- Open up the rules file and add access for the services we've added. You may want to add more entries here later for other services (like SVN, DNS, etc.)
# vim /etc/shorewall/rules
Ping/ACCEPT net $FW # Permit all ICMP traffic FROM the firewall TO the net zone ACCEPT $FW net icmp # mail lines SMTP/ACCEPT net $FW SMTPS/ACCEPT net $FW Submission/ACCEPT net $FW IMAP/ACCEPT net $FW IMAPS/ACCEPT net $FW # No POP for now, but if we decide to add it, uncomment these lines #POP3/ACCEPT net $FW #POP3S/ACCEPT net $FW #web Web/ACCEPT net $FW
- Check the setup, and if everything is ok, test it out. Make sure not to terminate your session until you're positive you can still log in.
# shorewall check # /etc/init.d/shorewall restart
Test
Now we have a basic mail server set up. This is a good point to add some data and test things out before we continue.
Data
- Log into MySQL
$ mysql -u mail -p maildb
- Add data for local mail domains
INSERT INTO domains (domain) VALUES ('localhost'), ('localhost.localdomain'); - Add some default aliases
INSERT INTO aliases (email,destination) VALUES ('postmaster@localhost','root@localhost'), ('sysadmin@localhost','root@localhost'), ('webmaster@localhost','root@localhost'), ('abuse@localhost','root@localhost'), ('mailer-daemon@localhost','postmaster@localhost'), ('nobody@localhost','root@localhost'), ('noc@localhost','root@localhost'), ('security@localhost','root@localhost'), ('@localhost','root@localhost'), ('@localhost.localdomain','@localhost'), ('root@localhost',root@pieps.org'); - Now add some real data
INSERT INTO domains (domain) VALUES ('pieps.org');INSERT INTO users (email, name, crypt) VALUES ('root@pieps.org', 'root',encrypt('apassword');
Incoming/Outgoing mail test
- First we're going to turn off all services
# /etc/init.d/courier-authdaemon stop # /etc/init.d/courier-imap stop # /etc/init.d/courier-imap-ssl stop # /etc/init.d/postfix stop # /etc/init.d/mysql stop
- Check to make sure everything's actually stopped
$ ps aux $ netstat -tnp
- Now tail the MySQL and postfix logs on different sessons
# tail -f /var/log/mysql/mysql.log
# tail -f /var/log/mail.log
- On a different session, start up your minimal server
# /etc/init.d/mysql start # /etc/init.d/postfix start
- Check to see that things are up and listening
$ ps aux $ netstat -tnpl
- Now we'll try to send a message to a local user
$ telnet localhost 25 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 mail.pieps.org ESMTP Postfix EHLO mail.pieps.org 250-mail.pieps.org MAIL FROM: <somebody@anotherdomain.com> 250 2.1.0 Ok RCPT TO: <root@pieps.org> 250 2.1.5 Ok DATA 354 End data with <CR><LF>.<CR><LF> yams . 250 2.0.0 Ok: queued as CACC6AC129 QUIT 221 2.0.0 Bye
- Check your logs to make sure the mail was received and delivered properly
- Now try to send a message from your server to a remote address
$ telnet localhost 25 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 mail.pieps.org ESMTP Postfix EHLO mail.pieps.org 250-mail.pieps.org MAIL FROM: <root@pieps.org> 250 2.1.0 Ok RCPT TO: <somebody@anotherdomain.com> 250 2.1.5 Ok DATA 354 End data with <CR><LF>.<CR><LF> yams . 250 2.0.0 Ok: queued as CACC6AC129 QUIT 221 2.0.0 Bye
- Check your logs to make sure the mail was received and delivered properly
- Turn on Courier and check that that's working
# /etc/init.d/courier-authdaemon start # /etc/init.d/courier-imap start # /etc/init.d/courier-imap-ssl start
- Check to see that it's working ok
$ telnet localhost 143 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 STARTTLS] Courier-IMAP ready. Copyright 1998-2008 Double Precision, Inc. See COPYING for distribution information.
- If you don't get this or any of this goes wrong, check over your config files again. If all else fails, Flurdy has some good suggestions.
Secondary Installation
Now that we (hopefully) have a functional mail server up and running, it's time to add some bells and whistles.
- Install some spiffy content filtering software:
# aptitude install amavisd-new # aptitude install spamassassin spamc # aptitude install clamav-base clamav-daemon clamav-freshclam # aptitude install postgrey
- We're going to leave SquirrelMail out for now.
Amavisd-new Configuration
Amavisd is the middleman between virus/spam checkers and your MTA. We're going to first configure it to pass mail directly to Postfix, but will add in virus and spam checking in a bit.
- All of amavis' config files are in /etc/amavis.
cd /etc/amavis/conf.d
- <NOTE> May need to change /etc/amavis/conf.d/05-node-id so that it points to mail.pieps.org, not www.pieps.org.
- <NOTE> May need to set up masquerade_domains in /etc/postfix/main.cf so that it will truncate mail.pieps.org (used by /etc/amavis/conf.d/20-debian_defaults for the postmaster address), or an alias for certain addresses for mail.pieps.org. Another solution would be to edit amavis so it uses the correct addresses.
- Edit user file
# vim 50-user
And insert the following in the middle@local_domains_acl = qw(.); $log_level = 2; $syslog_priority = 'debug'; $sa_kill_level_deflt = 8.0; # triggers spam evasive actions $final_spam_destiny = D_PASS; # $final_spam_destiny = D_DISCARD;
This sets up amavis to just pass along incoming mail. - Now we're going to set up postfix to play nicely with amavis
# vim /etc/postfix/master.cf
- Append the following lines to the end of the file (make sure they aren't present already)
amavis unix - - - - 2 smtp -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o disable_dns_lookups=yes -o max_use=20127.0.0.1:10025 inet n - - - - smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_delay_reject=no -o smtpd_client_restrictions=permit_mynetworks,reject -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o smtpd_data_restrictions=reject_unauth_pipelining -o smtpd_end_of_data_restrictions= -o mynetworks=127.0.0.0/8 -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000 -o smtpd_client_connection_count_limit=0 -o smtpd_client_connection_rate_limit=0 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks - Add the following two lines immediately below the "pickup" transport service
-o content_filter= -o receive_override_options=no_header_body_checks
- Append the following lines to the end of the file (make sure they aren't present already)
- Now add the following to /etc/postfix/main.cf
content_filter = amavis:[127.0.0.1]:10024
- Now add clamav to the amavis group, to allow it to scan amavis' temporary files
# adduser clamav amavis
- This should be enough to get amavis working. If emails are picked up by amavis and passed through to postfix, then all is well.
SpamAssassin Config
SpamAssassin's default settings are fine, but if spam starts getting through, it might be good to start tweaking. A good reference for that is at Flurdy.
- The only thing you need to do is tell SpamAssassin to start
# vim /etc/default/spamassassin
ENABLED=1
- One thing that could be set is the nightly update feature by setting CRON=1
ClamAV Config
Apparently, ClamAV doesn't need setting up. Config files are in /etc/clamav, but they're autogenerated so they'll get clobbered every time the defs are updated. From Flurdy:
By default freshclam, the daemon that updates the virus definition database, is run 24 times a day. That seems a little excessive, so I tend to set that to once a day.
# dpkg-reconfigure clamav-freshclamThis will redefine the config with a lot of questions. Not needed unless you need to configure
# dpkg-reconfigure clamav-base
Postgrey Config
- Postgrey's default config is ok, but you need to tell Postfix to use it.
# vim /etc/postfix/main.cf
smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service inet:127.0.0.1:60000, permit
- You can tweak whitelisting in /etc/postgrey and config in /etc/default/postgrey
Amavisd-new Config Part 2
Now that we have SpamAssassin and ClamAV set up, we can make Amavisd start filtering things
- Open /etc/amavis/conf.d/15-content_filter_mode and uncomment the following
@bypass_virus_checks_maps = ( \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re); @bypass_spam_checks_maps = ( \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);
- Open /etc/amavis/conf.d/50-user again and change the following
@local_domains_acl = qw(.); $log_level = 1; $syslog_priority = 'info'; $sa_kill_level_deflt = 8.0; # triggers spam evasive actions #$final_spam_destiny = D_PASS; $final_spam_destiny = D_DISCARD;
Cyrus SASL
Config
SASL allows SMTP clients to authenticate themselves, thereby keeping unauthorized clients from sending mail from your server. We're going to set it up to authenticate through plaintext (moreover, the common mechanisms that employ hashing all use insecure algorithms), so when we set up TLS (below), we're going to be tricky and force users to use it.
- First we need to make postfix use SASL. Make sure the lines in /ect/postfix/main.cf read as follows
smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unauth_destination, check_policy_service inet:127.0.0.1:60000, permit
smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit
And add the following linessmtpd_sasl_auth_enable = yes broken_sasl_auth_clients = yes
- Now we're going to configure saslauthd.
- First, create the following directory for the SASL mux
# mkdir -p /var/spool/postfix/var/run/saslauthd
- Now edit /etc/default/saslauthd and turn it on
START=yes
Make sure it's set to use PAMMECHANISMS="pam"
And set it to use the mux directory we just createdOPTIONS="-c -m /var/spool/postfix/var/run/saslauthd -r"
The -r tells saslauthd to pass the entire email address instead of just the user (i.e., user@host.com instead of user) - <NOTE> Ubuntu Hardy seems to set the PID file location automatically based on the OPTIONS param passed to saslauthd, so in my case, I didn't have to change anything. You should check /etc/init.d/saslauthd just to make sure though, and if you're not as lucky as I was, set it to the following (or set RUN_DIR so that it's done correctly)
PIDFILE="/var/spool/postfix/var/run/${NAME}/saslauthd.pid" - Next create the file /etc/pam.d/smtp and put the following into it
auth required pam_mysql.so user=mail passwd=MyMailPassword host=127.0.0.1 db=maildb table=users usercolumn=email passwdcolumn=crypt crypt=1 account sufficient pam_mysql.so user=mail passwd=MyMailPassword host=127.0.0.1 db=maildb table=users usercolumn=email passwdcolumn=crypt crypt=1
- Now we need to create the file /etc/postfix/sasl/smtpd.conf
pwcheck_method: saslauthd mech_list: plain login allow_plaintext: true auxprop_plugin: mysql sql_hostnames: 127.0.0.1 sql_user: mail sql_passwd: MyMailPassword sql_database: maildb sql_select: select crypt from users where email = '%u'
- We're also going to need to add postfix to sasl's group so it can access saslauthd
# adduser postfix sasl
- First, create the following directory for the SASL mux
- Ubuntu's default settings for SASL in Courier seem to be ok. Just make sure that IMAP_CAPABILITY_TLS="$IMAP_CAPABILITY AUTH=PLAIN" is set.
- Restart postfix, courier, and SASL
# /etc/init.d/postfix restart # /etc/init.d/courier-authdaemon restart # /etc/init.d/courier-imap restart # /etc/init.d/courier-imap-ssl restart # /etc/init.d/saslauthd restart
Testing
SASL is the thing that I've historically had the most trouble with. This time around I'm going with a better solution (using pam via saslauthd), but it's still good to test here before adding TLS/SSL to the mix.
- Grab an encoded string to use to authenticate with
$ perl -MMIME::Base64 -e 'print encode_base64("\0username\@yourdomain.com\0apassword");'This will give you a string that looks like AhJ3Bqfh5NplfrQa= - Telnet into your mail server
$ telnet localhost 25
Give a proper EHLO responseEHLO mail.pieps.org
And provide the auth string you generated aboveAUTH PLAIN AhJ3Bqfh5NplfrQa=
The server should respond with something like235 2.7.0 Authentication successful
If not, check your /var/log/mail.log and /var/log/mysql/mysql.log for details.
TLS
Encryption is a must. You don't want people eavesdropping on your login credentials. The following will detail how to set up mandatory TLS for your users to make sure that they don't expose their passwords. Note that this doesn't mean that the emails themselves will be encrypted from point to point. STARTTLS goes a short way in attempting for that to happen, but if you want real security you should use something like GnuPG.
- First, make sure you have a valid private key and certificate. How to generate these is outside the scope of this document, but Flurdy has some good directions.
- Self-signed keys are a start, but it's better to have a certificate signed by a CA. This is normally expensive, but there are also so-called web of trust CAs (like CAcert) that are free. These CAs still do a good job of proving that people are who they say they are, but they usually do not have their root certificates included in web browsers or mail clients; you'll have to get your users to add them manually.
- Once you have your key and certificate ready, it's time to add a bunch of stuff to /etc/postfix/main.cf (or modify it if it's already there)
smtp_tls_security_level = may smtpd_tls_security_level = may # Allow authentication only through TLS - this has the effect of forcing our users to use TLS # if they want to send mail from any host other than 127.0.0.1 smtpd_tls_auth_only = yes smtp_tls_note_starttls_offer = yes smtpd_tls_loglevel = 1 smtpd_tls_received_header = yes smtpd_tls_cert_file=/etc/postfix/postfix.cert smtpd_tls_key_file=/etc/postfix/postfix.key smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache - Now we're going to add/change some things in /etc/postfix/master.cf. By default, only the normal smtp service is enabled, which is fine, but it's nice to enable Submission and smtps for compatibility with older clients.
<TODO> Check over these settings a bit moresubmission inet n - - - - smtpd -o smtpd_sasl_auth_enable=yes -o smtpd_tls_auth_only=yes # if you do not want to restrict it to encryption only, comment out next line -o smtpd_tls_security_level=encrypt -o smtpd_client_restrictions=permit_sasl_authenticated, reject # -o milter_macro_daemon_name=ORIGINATING smtps inet n - - - - smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_tls_auth_only=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject # -o milter_macro_daemon_name=ORIGINATING
- Finally, we're going to enable TLS in /etc/courier/imapd-ssl (this assumes that your .pem file is stored in /etc/courier/imapd.pem)
TLS_CERTFILE=/etc/courier/imapd.pem
If you want to restrict IMAP to SSL/TLS only, do the followingIMAP_TLS_REQUIRED=1
Done!
You now have a secure, feature-rich mail server. Congrats!
TODO
- Squirrelmail (or other webmail client)
- Password change
- GPG
- MX backup
- Local file backup
- SPF
- White/Black lists
- Quotas
- Auto reply
- Mail lists
- Output throttling
References
In no particular order. Major chunks of this guide were taken from Flurdy and Slicehost to a lesser extent.
- How to set up a mail server on GNU/Linux system
- Slicehost Articles: Mail server - overview
- Virtual Users And Domains With Postfix, Courier And MySQL
- SMTP AUTH with Postfix and saslauthd
- Postfix Configuration - Basics
- Postfix Configuration Parameters
- Postfix SASL Howto
- Postfix SMTP Access Policy Delegation
- Postfix Virtual Domain Hosting Howto
- Postfix TLS Support
- Postfix MYSQL Howto
- Postfix manual - virtual(5)
- Postfix manual - smtpd(8)
- Postfix manual - smtp(8)
- Postfix manual - proxymap(8)
- Postfix manual - postmap(1)
- Postfix Configuration - Address Manipulation
- RFC3207 - SMTP Service Extension for Secure SMTP over Transport Layer Security
- RFC2487 - SMTP Service Extension for Secure SMTP over TLS
- RFC2595 - Using TLS with IMAP, POP3 and ACAP
And a bunch of forum and mailing list archives all over the internet.
