Easy to follow howto on setting up a mail server with unlimited users and domains, with IMAP/Pop access, anti-spam, anti-virus, secure authentication, encrypted traffic, web mail interface and more.
Based on an Ubuntu distribution platform, but instructions are distro generic.
|1st||Released (outdated)||2004-01||2004-02||Based on Mandrake 9.1.|
|2nd||Released (outdated)||2004-02||2004-07||Based on Mandrake 10.x, but valid for all distributions. Very thorough. Includes package description, where to get the sources and binaries, how to build them or which RPMs to use, includes many refrences, etc etc. Starts off with a basic working server, then advances, extends and tightens it in stages.|
|3rd||Released||2005-05||2005-11||Based on Ubuntu 5.04, Hoary Hedgehog. More concise simplified guide to get an advanced server working quickly. Now includes SASL & TSL integration.|
|4th (this)||Released||2005-10||2005-12||Based on Breezy Badger, Ubuntu 5.10. Includes Postgrey|
|5th||Early developement||2006-05||2006-05||Based on Dapper Drake, Ubuntu 6.06. Comments apprieciated.|
This is a step by step howto guide to set up a mail server on a GNU / Linux system. It is easy to follow, but you end up with a powerfull secure mail server.
The server accepts unlimited domains and users, and all mail can be read via your favourite clients, or via web mail.
It is secure, traffic can encrypted and it will block virtually all spam and viruses.
Hardware: A computer to be the server. Processor and memory requirements are low. Disk space is relevant to what mail you expect to keep, Range between <1Gb to 200GB. Need network acces direct to the outside world or ports forwarded through to it.
Software: None, apart from an ubuntu cd or a pre installed system.
Skills: You do not need to be a guru, as it is not advanced. However some linux skills is desired, and knowledge of the risks of leaving ports exposed to the outside world.
Dont take my word for it! Research others opinions and methods. Look at my references, look at Postfix.org's howtos, read the excellent books available (E.g. Kyle's or Hildebrandt's), search the web or read the proper documentation.
If you refer to this howto in your own document, or find usefull links, then let me know.
I am Ivar Abrahamsen, a 29 year old software engineer from Norway, but based in Manchester. I use Linux a lot, though I am not a guru. My general intrests are sports, technology and my better half.
Why have I written this how to? I set up my mail server in 2003, and then did the same for a few friends and collegues. Soon I was getting more request, and being a lazy programmer, I thought.. "Why don't I write a howto and let them do it themselves..." Soon it was listed on postfix.org and I was getting thousends of hits and lots of emails. (blessing in disguise)
See the contact section for how to discuss this howto and how to contact me. Send me a note if you found this usefull. If you use this for commercial purposes, then why not donate a few quid? (Remember to respect the licenses involved)
Please see software links appendix for further information about these software packages. In that section there is more links to documentation or forums, and viable alternatives, downloadable packages, versions details etc.
Further software and tweaks are discussed in the extension section.
Also review other peoples opinion on these packages via my references.
This section is different for every distribution and for every version.
This howto is based on Ubuntu and its base of debian which uses apt-get. Therefor this section uses apt packages to its fullest.
For other installation method please refer to the software and the software links and your own distribution for the documention for other ways of installing. My 2nd edition(outdated) has instructions for Mandriva, general RPM and tarball compiling.
To follow the rest of this howto, you need to ensure all your packages have been installed with the same modules, E.g MySQL lookup on postfix and sasl, php in apache etc.
I have set up mail servers using the 32bit and 64bit x86 platforms, however if all the packages are available then other, E.g. Mac platforms should work too.
Upon installing Ubuntu you have a choice of which base system to install.
The default, ie the one chosen when you just hit enter when promted right at the start, is the basic desktop flavour.
Another useful one is the server base. It only includes the absolute minimum of packages, so is quite usefull if you are only to use it remotely. Since Breezy it also available as a smaller iso download, or by using the normal cd by hitting F1 instead of enter and writing server on the prompt.
This howto have been used with both bases, the server base will need some more dependancy packages thats all.
When the base system is up and running you need to check your package repositories, ie where the system retrieves new software.
The install procedure usually leaves you with a /etc/apt/sources.list that includes the cd and the main and restricted and updates repositories.
That is fine for the absolute core packages involved in this mail server, however a full install requires the universal, and you might as well also include the multiverse and backports as well.
I also tend to disable the cd option, but that is up to you.
You will have to find a repository mirror close to your location from the archives, replace mine gb.archive.ubuntu.com with your choice.
#deb cdrom:[Ubuntu 5.04 _Hoary Hedgehog_ - Release i386 (20050407)]/ hoary main restricted #deb cdrom:[Ubuntu 5.10 _Breezy Badger_ - Release i386 (20051012)]/ breezy main restricted deb http://gb.archive.ubuntu.com/ubuntu breezy main restricted multiverse universe deb-src http://gb.archive.ubuntu.com/ubuntu breezy main restricted multiverse universe deb http://gb.archive.ubuntu.com/ubuntu breezy-updates main restricted multiverse universe deb-src http://gb.archive.ubuntu.com/ubuntu breezy-updates main restricted multiverse universe deb http://gb.archive.ubuntu.com/ubuntu breezy-security main restricted multiverse universe deb-src http://gb.archive.ubuntu.com/ubuntu breezy-security main restricted multiverse universe deb http://gb.archive.ubuntu.com/ubuntu breezy-backports main restricted multiverse universe deb-src http://gb.archive.ubuntu.com/ubuntu breezy-backports main restricted multiverse universe ## EXTRAS #deb http://ubuntu-backports.mirrormax.net/ breezy-extras main universe multiverse restricted contrib ## MARILLAT #deb ftp://ftp.nerim.net/debian-marillat unstable main
Here is a list of packages needed, and what they provide. Some are required by several of the software, some might not be needed if you are not fully following this howto. Please note the extended section require further packages.
Ive included the Shorewall firewall. A firewall is not required, but recommended. Obviously you can use another firewall, but Ill assume you have chosen Shorewall. A SSH server is not required either, but essential if you need to administer or test the server remotely.
MySQL 4 is required by many of the packages, so install it first.
The SASL packages have changed for Breezy. Im investigating the differences. The * packages I have from hoary repositories.
postfix-tls is as of breezy part of the postfix package, however if you are not using breezy then you must install postfix with included tls features.
If you require pop access then you'd want to install the pop packages as well.
There is also a postfix-gld however I am using the postgrey one till I fully tested the other.
SquirrelMail webmail require a working apache web server, with php with mysql support. Note these web packages uses PHP4.
Like SquirrelMail, phpMyAdmin require a working apache server.
Now you might not want to install all the packages in one go, perhaps better to group them by each software or a few together.
If you want to find additional packages, you can do a quick command line search for packages, by useing this command:
apt-cache search postfix
To find out what you might already have installed:
dpkg --list | grep postfix
Then when you have the package list do this to install
# add -s to do a test run # or -d if you just want to download the packages and do the install later apt-get install package-name, another-package-name, etc
Some of the package installations will prompt you for input,
Postfix will ask you what type of server to create. I just say "Internet Site" as we will be changing most configs anyway. It will also ask for the fully qualified name of your server. The clamav installation may ask whether to create directories etc. Courier will ask to install web admin, which I dont't need, and that it will install TLS encryption which is good.
Many of the packages also require further dependant packages. So the final package list is quite large.
Now everything is installed it is time to configure each of the core applications used.
#zone display comment loc Local Local network net Net Tinternet
net eth0 detect
fw loc ACCEPT fw net ACCEPT loc all DROP info net all DROP info all all REJECT info
AllowPing loc fw AllowSSH loc fw #AllowSMTP loc fw #ACCEPT loc fw tcp 465,587 - #AllowIMAP loc fw #AllowPing net fw #AllowSSH loc fw #AllowSMTP net fw #ACCEPT net fw tcp 465,587 - #AllowIMAP net fw
SMTP access from everywhere is commented out, untill we are confident everything is working and secure. Also commented out for now is IMAP and TLS SMTP traffic untill we need it. You might enable SSH from the tinternet if you want.
#restart shorewall with /etc/init.d/shorewall restart
myhostname = server.yourdomain.com
smtpd_banner = $myhostname ESMTP $mail_name
# leave blank to do it yourself relayhost = # or put it an accessible smtp server relayhost = smtp.yourisp.com
inet_interfaces = all mynetworks_style = host
masquerade_domains = sub.domain.com !sub.dyndomain.com masquerade_exceptions = root
local_recipient_maps = mydestination =
# 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
# 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 smtpd_client_restrictions = reject_rbl_client sbl.spamhaus.org, reject_rbl_client relays.ordb.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
# require proper helo at connections smtpd_helo_required = yes # waste spammers time before rejecting them smtpd_delay_reject = yes disable_vrfy_command = yes
# not sure of the difference of the next two # but they are needed for local aliasing alias_maps = hash:/etc/postfix/aliases alias_database = hash:/etc/postfix/aliases # 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_uid_maps = mysql:/etc/postfix/mysql_uid.cf # and group id virtual_gid_maps = mysql:/etc/postfix/mysql_gid.cf # and this is for aliases virtual_alias_maps = mysql:/etc/postfix/mysql_alias.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
cp /etc/aliases /etc/postfix/aliases # may want to view the file to check if ok. # especially that the final alias, eg root goes # to a real person postalias /etc/postfix/aliases
# to add if there is not a virtual user mkdir /var/spool/mail/virtual groupadd virtual -g 5000 useradd virtual -u 5000 -g 5000 chown -R virtual:virtual /var/spool/mail/virtual
# to modify if a virtual user is already set groupmod -g 5000 virtual usermod -g virtual -u 5000 virtual chown -R virtual:virtual /var/spool/mail/virtual
user=mail password=apassword dbname=maildb table=users select_field=maildir where_field=id hosts=127.0.0.1 additional_conditions = and enabled = 1
user=mail password=apassword dbname=maildb table=users select_field=uid where_field=id hosts=127.0.0.1
user=mail password=apassword dbname=maildb table=users select_field=gid where_field=id hosts=127.0.0.1
user=mail password=apassword dbname=maildb table=aliases select_field=destination where_field=mail hosts=127.0.0.1 additional_conditions = and enabled = 1
user=mail password=apassword dbname=maildb table=domains select_field=domain where_field=domain hosts=127.0.0.1 additional_conditions = and enabled = 1
# If not already done... mysqladmin -u root password new_password # log in as root mysql -u root -p # then enter password for the root account when prompted Enter password: # then we create the mail database create database maildb; # then we create a new user: "mail" GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON maildb.* TO 'mail'@'localhost' IDENTIFIED by 'apassword'; GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON maildb.* TO 'mail'@'%' IDENTIFIED by 'apassword'; exit;
# log in to mysql as the new mail user mysql -u mail -p maildb # enter the newly created password Enter password: #then run this commands to create the tables; CREATE TABLE `aliases` ( `pkid` smallint(3) NOT NULL auto_increment, `mail` varchar(120) NOT NULL default '', `destination` varchar(120) NOT NULL default '', `enabled` tinyint(1) NOT NULL default '1', PRIMARY KEY (`pkid`), UNIQUE KEY `mail` (`mail`) ) ; CREATE TABLE `domains` ( `pkid` smallint(6) NOT NULL auto_increment, `domain` varchar(120) NOT NULL default '', `transport` varchar(120) NOT NULL default 'virtual:', `enabled` tinyint(1) NOT NULL default '1', PRIMARY KEY (`pkid`) ) ; CREATE TABLE `users` ( `id` varchar(128) NOT NULL default '', `name` varchar(128) NOT NULL default '', `uid` smallint(5) unsigned NOT NULL default '5000', `gid` smallint(5) unsigned NOT NULL default '5000', `home` varchar(255) NOT NULL default '/var/spool/mail/virtual/', `maildir` varchar(255) NOT NULL default 'blah/', `enabled` tinyint(3) unsigned NOT NULL default '1', `change_password` tinyint(3) unsigned NOT NULL default '1', `clear` varchar(128) NOT NULL default 'ChangeMe', `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 (`id`), UNIQUE KEY `id` (`id`) ) ;
## In Hoary you needed to comment out this line #skip-networking ## however in breezy this has changed to bind-address = 127.0.0.1 ## which is fine ## Make sure this is set log = /var/log/mysql/mysql.log ## Then in a few weeks comment it out ## when everything is working, as it slows mysql down
# restart MySQL to make sure # its picking up the new settings. sudo /etc/init.d/mysql restart
MYSQL_SERVER localhost MYSQL_USERNAME mail MYSQL_PASSWORD apassword MYSQL_PORT 0 MYSQL_OPT 0 MYSQL_DATABASE maildb MYSQL_USER_TABLE users # comment out this field, # as I now longer use the encrypted pw options #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 concat(home,'/',maildir) MYSQL_WHERE_CLAUSE enabled=1
# set how many connections to use per person. Easy to underestimate if you have 6 mailboxes set up. MAXPERIP=20 # high debug to start with DEBUG_LOGIN=2 IMAPDSTART=YES
$mydomain 'yourdomain.com'; $daemon_user= 'virtual'; $daemon_group= 'virtual'; @local_domains_acl = qw(); $inet_socket_port = 10024; $forward_method = 'smtp:127.0.0.1:10025'; # @bypass_virus_checks_acl = qw( . ); # @bypass_spam_checks_acl = qw( . );
# I also change these $TEMPBASE = "$MYHOME/tmp"; # Whilst debugging $log_level = 2; $warnbannedrecip = 1; $warn_offsite = 1; $warnvirusrecip = 1; $spam_quarantine_to = "spam-quarantine\@$mydomain"; $virus_quarantine_to = "virus-quarantine\@$mydomain"; $sa_local_tests_only = 0;
# You may have to do this cd /var/lib/amavis mkdir tmp chown virtual:virtual tmp chown virtual:virtual virusmails # and maybe this chown -R virtual:virtual /var/run/amavis
#edit about line 31 #chown -c -h "$1:$2" "$4" chown -c -h "virtual:virtual" "$4"
#smtp inet n - n - - smtpd smtp inet n - - - - smtpd -o cleanup_service_name=pre-cleanup #cleanup unix n - - - 0 cleanup cleanup unix n - - - 0 cleanup -o mime_header_checks= -o nested_header_checks= -o body_checks= -o header_checks= amavis unix - - - - 2 smtp -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes 127.0.0.1:10025 inet n - - - - smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o strict_rfc821_envelopes=yes -o mynetworks=127.0.0.0/8 -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1001 pre-cleanup unix n - - - 0 cleanup -o virtual_alias_maps= -o canonical_maps= -o sender_canonical_maps= -o recipient_canonical_maps= -o masquerade_domains=
content_filter = amavis:[127.0.0.1]:10024 #receieve_override_options = no_address_mappings
# User clamav User virtual
chown virtual:virtual /var/run/clamav
# how frequent per day. default is once an hourwhich is a bit excesive. # once per day should do. Checks 1
SpamAssassin's default settings were fine, but you can tweak them at /etc/spamassassin/local.cf and review the defauls at /usr/share/spamassassin/. E.g. you can in/decrease the levels needed before emails are marked as spam and before rejections.
Here is an example of my local.cf.
skip_rbl_checks 0 use_razor2 0 use_dcc 0 use_pyzor 0 use_bayes 1 bayes_path /etc/spamassassin/bayes bayes_file_mode 0770
Once you have a collection of spam and non spam (200+ of each), you can train the Bayes filter in SpamAssassin with these emails. Review this on the SpamAssassin web site.
# E.g. like this sa-learn --showdots -C /etc/spamassassin --spam /var/spool/mail/virtual/quarantine/.spam/* sa-learn --showdots -C /etc/spamassassin --ham /var/spool/mail/virtual/mine/cur/*
If you notice too much spam is being let through, then do more tweaking. If you get too many false postives, ie real emails marked as spam, loosen the set up slightly. A properly configured SpamAssassin should catch 97% of all spam. With probably 1 in 1000 false positives.
The SpamAssassin site has a lot of information on setting it up. It is worth a good read through. Some usefull tips are automatic learning, cronjobs to learn user marked spam and ham, etc.
Adding Postgrey to this mail set up is a breeze. Thanks for the emails I got on postgreys benefits and integration.
Ubuntu's extended repositories has a postgrey module, which installs the scripts and sets up a /etc/postgrey whitelist configuration. You can edit these files, but I don't bother. You may want to any back up mx server you use, if you do.
You do however need to edit main.cf to add reciepient restrictions:
#adding the postgrey policy: smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service inet:127.0.0.1:60000, permit
POSTGREY_OPTS="--inet=127.0.0.1:60000 --delay=60" #POSTGREY_TEXT="Your customized rejection message here"
If the postgrey method becomes very popular, perhaps spammers will start to comply with it. However that will be years untill they do, if ever.
Meanwhile enjoy a spamless existence.
Cyrus SASL provide a secure method of authenticating users. This type of authentication is required by two methods, one is by postfix when sending email and the other is by Courier when reading emails.
First we wil will deal with postfix. Add these lines to main.cf
# modify the existing smtpd_recipient_restrictions 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 # modify the existing smtpd_sender_restrictions smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit # then add these smtpd_sasl_auth_enable = yes broken_sasl_auth_clients = yes smtpd_sasl_path = /etc/postfix/sasl:/usr/lib/sasl2 smtpd_sasl_security_options = noanonymous smtpd_sasl_local_domain =
# May already exist mkdir /etc/postfix/sasl # Then create the conf file. vi /etc/postfix/sasl/smtpd.conf
pwcheck_method: auxprop auxprop_plugin: sql mech_list: plain login cram-md5 digest-md5 sql_engine: mysql sql_hostnames: 127.0.0.1 sql_user: mail sql_passwd: apasswd sql_database: maildb sql_select: select clear from users where id='%u@%r' and enabled = 1
That is all that should be required for sending email.
Next is to configure Courier to authenticate via SASL as well.
In Ubuntu all this was preset so the only line I needed to modify / confirm in /etc/courier/imapd is:
IMAP_CAPABILITY="IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA AUTH=CRAM-MD5 AUTH=CRAM-SHA1 IDLE"
cd /etc/postfix openssl req -new -outform PEM -out \ postfix.cert -newkey rsa:2048 -nodes -keyout \ postfix.key -keyform PEM -days 999 -x509
smtpd_use_tls = yes smtpd_tls_cert_file = /etc/postfix/postfix.cert smtpd_tls_key_file = /etc/postfix/postfix.key smtpd_data_restrictions = reject_unauth_pipelining
# these may already be present in your file, # however I usually have to add them # also, this specific line used to use fifo, it now needs to use unix type tlsmgr unix - - n 300 1 tlsmgr smtps inet n - n - - smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes 587 inet n - n - - smtpd -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes
These ports are required for clients not able to use the STARTTLS option on plain port 25. Port 465 (the smtps line) is an unofficial workaround, so clients E.g. Novel Evolution, uses it untill they fix their software to work with STARTTLS.
The debian packages in Ubuntu creates certificate for courier for you. Otherwise do this (in case server name is not same as machine name):
openssl req -x509 -newkey rsa:1024 -keyout imapd.pem -out \ imapd.pem -nodes -days 999
This will enable secure traffic of emails via your clients and the server. As these are not signed certificates, some may be prompted to accept license. You could get people to import your certificates, if only a few is accessing you imap/smtp server, or purchase a signed one if you have a large number of users, especially if corporate. Outlook is known as stuburn to accept the certificates.
There are some issues with using SALS and TLS at the same time. Since all the traffic is encrypted with TLS, then the need for SASL is less when enforcing TLS.
ln -s /usr/share/squirrelmail /var/www/squirrelmail
<VirtualHost *> ServerAdmin email@example.com ServerName webmail.yourdomain.com DocumentRoot /var/www/squirrelmail <Directory /var/www/squirrelmail> Options Indexes FollowSymLinks MultiViews AllowOverride AuthConfig Order allow,deny allow from all </Directory> ErrorLog /var/log/apache2/error-webmail.log LogLevel warn CustomLog /var/log/apache2/access-webmail.log combined ServerSignature On </VirtualHost>
ln -s /etc/apache2/sites-available/webmail /etc/apache2/sites-enabled/810-webmail # or as Florent recommends, use: a2ensite webmail # then activate the changes /etc/init.d/apache2 reload
The config folder is actually symblinked to /etc/squirrelmail so if you run several instances of squirrelmail you might want to create copies of it.
SquirrelMail is configured with 3 config files. config_default.php is well commented and is sets up the default values. Do not edit it.
config.php overrides the defaults. Do not edit this one either as it is created by the conf.pl perl script.
Finally conf_local.php can be edited and it overrides the others.
To configure squirrelmail, run the perl script.
# Enter this mysql://username:firstname.lastname@example.org/database
There is also a global address option if you choose to use it. Press s to save the settings, and r to return to main menu. Press q to exit.
Here is copy of my config_local.php. Read the default file for explanations.
Then you need to create these database tables, My previous editions included the squirrelmail specific tables in the main mail database. However I believe a cleaner setup is to have seperate squirrel user and database for its settings.
First create a new squirrel database user, or reuse the mail user. See the MySQL section for user creation details.
Then create a squirrel database or reuse the mail database. Make sure the user created above has usage access to this database. Again refer to the MySQL section.
Modify the config.php files to reflect this.
Then log into mysql to start creating these tables.
mysql -u username -p database # Then enter the password
CREATE TABLE `address` ( `owner` varchar(128) NOT NULL default '', `nickname` varchar(16) NOT NULL default '', `firstname` varchar(128) NOT NULL default '', `lastname` varchar(128) NOT NULL default '', `email` varchar(128) NOT NULL default '', `label` varchar(255) default NULL, PRIMARY KEY (`owner`,`nickname`), KEY `firstname` (`firstname`,`lastname`) ) ; CREATE TABLE `userprefs` ( `user` varchar(128) NOT NULL default '', `prefkey` varchar(50) NOT NULL default '', `prefval` varchar(255) default NULL, `modified` timestamp(14) NOT NULL, PRIMARY KEY (`user`,`prefkey`) ) ; CREATE TABLE `global_abook` ( `owner` varchar(128) NOT NULL default '', `nickname` varchar(16) NOT NULL default '', `firstname` varchar(128) NOT NULL default '', `lastname` varchar(128) NOT NULL default '', `email` varchar(128) NOT NULL default '', `label` varchar(255) default NULL, PRIMARY KEY (`owner`,`nickname`), KEY `firstname` (`firstname`,`lastname`) );
# cd into web root where phpMyAdmin is installed, e.g. /var/www # Again in Ubuntu a soft link is needed to /usr/share # this time however the apt-get has done it for you. (check though) # If the folder contains the version in its name. # do this for ease of access and if later upgrading ln -s phpMyAdmin1.6.2 phpMyAdmin
# either reuse an old .htpasswd file # or as below , create one when you add the first user htpasswd2 -c /path/to/htpasswd/file/outside/www/.htpasswd ausername # then enter desired passwd
AuthType Basic AuthName "A Bit Hush and all that" AuthUserFile "/path/to/htpasswd/file/outside/www/.htpasswd" require valid-user
$cfg['Servers'][$i]['host'] = 'localhost'; $cfg['Servers'][$i]['user'] = 'mail'; $cfg['Servers'][$i]['password'] = 'apassword'; $cfg['Servers'][$i]['only_db'] = 'maildb';
domain.tld IN MX 10 your.mailserver.name.tld
So we got a fully set up mail server... Well no, there is no users, domains, no nothing!
Okay, first you need add some default data, some which are required, some which make sense.
Then we'll add your own users and domains.
# Use phpMyAdmin or command line mysql INSERT INTO domains (domain) VALUES ('localhost'), ('localhost.localdomain');
INSERT INTO aliases (mail,destination) VALUES ('postmaster@localhost','root@localhost'), ('sysadmin@localhost','root@localhost'), ('webmaster@localhost','root@localhost'), ('abuse@localhost','root@localhost'), ('root@localhost','root@localhost'), ('@localhost','root@localhost'), ('@localhost.localdomain','@localhost');
INSERT INTO users (id,name,maildir,clear) VALUES ('root@localhost','root','root/','apassword');
INSERT INTO domains (domain) VALUES ('blobber.org'), ('whopper.nu'), ('lala.com'); INSERT INTO aliases (mail,destination) VALUES ('email@example.com','firstname.lastname@example.org'), ('email@example.com','firstname.lastname@example.org'), ('email@example.com','firstname.lastname@example.org'), ('@whopper.nu','email@example.com'), ('@lala.com','@blobber.org'), ('firstname.lastname@example.org','postmaster@localhost'), ('email@example.com','abuse@localhost'), ('firstname.lastname@example.org','postmaster@localhost'), ('email@example.com','abuse@localhost'); INSERT INTO users (id,name,maildir,clear) VALUES ('firstname.lastname@example.org','xandros','xandros/','apassword'), ('email@example.com','vivita','vivita/','anotherpassword');
So what does each of these lines do? Well the domains are pretty straight forward. The users are as well, it requires four fields. ID is the email address of the user, and also its username when loggin in, described later on. NAME is optional description of the user. MAILDIR is the name of the folder inside /var/spool/mail/virtual. It must end in a /, otherwise it wont be used as a unix maildir format. CLEAR is the clear text password to use.
The alises are the interesting part. Lets start from a top down view. Say an email arrives addressed to "firstname.lastname@example.org". Postfix looks up aliases and searches for a row where the mail field matches "email@example.com". None does so it next searches for "@whopper.nu", which is the way to specify catch all others for that domain. It finds one row and its destination is "firstname.lastname@example.org". It then searches for "email@example.com" and finds one, which destination is the same as the mail, therefor it is the final destination. It then tries to deliver this mail. The look up says blobber.org is a local mail so it looks up users for a matching id and delivers it to its maildir.
Lets try "firstname.lastname@example.org". First lookup does not find this user, but the next finds the catchall "@lala.com". But its destination is another catchall, "@blobber.org". This means Postfix will look for "email@example.com". This address is not found either, nor is a catchall for blobber.org. Therefor this address is not valid and the message will be bounced.
Any mail arriving for "firstname.lastname@example.org" or "email@example.com", gets forward to an external address of "firstname.lastname@example.org". So forwarding is simple. I tend to use a subdomain for all my friends addresses as easily I forget what their real addresses are, and I use different email clients all the time.
I also added the required aliases of postmaster and abuse to blobber.org and whopper.nu. The catchall for lala.com means they are not required for that domain. You can add them though if you do not want xandros to get the admin emails. Another usefull alias to add is root, as often you get admin mail from e.g cron jobs within those domains etc. Other often used aliases are info, sysadmin, support, sales, webmaster, mail, contact and all. But they are also honeypots for spam, so just include the ones you think you will need.
INSERT INTO domains (domain) VALUES ('domain.tld'); INSERT INTO aliases (mail,destination) VALUES ('@domain.tld','email@address'), ('email@example.com','email@address'), ('firstname.lastname@example.org','email@address');
INSERT INTO users (id,name,maildir,clear) VALUES ('email@address','short description','foldername/','password'); INSERT INTO aliases (mail,destination) VALUES ('email@address','email@address');
#Remember some might be disabled SELECT dom.domain FROM domains dom LEFT JOIN aliases al ON CONCAT( '@', dom.domain ) = al.mail WHERE al.mail is null OR al.enabled = 0 ORDER BY dom.domain ASC
SELECT al.* FROM aliases al LEFT JOIN domains dom ON dom.domain = SUBSTRING(al.mail,LOCATE('@',al.mail)+1) WHERE dom.domain is null OR dom.enabled = 0 ORDER BY al.mail ASC
SELECT al.* FROM aliases al LEFT JOIN domains dom ON dom.domain = SUBSTRING(al.destination,LOCATE('@',al.destination)+1) WHERE dom.domain is null ORDER BY al.enabled, al.destination ASC, al.mail ASC
SELECT al.* FROM aliases al WHERE SUBSTRING(al.mail,LOCATE('@',al.mail)+1) = 'domain.tld' ORDER BY al.enabled, al.mail ASC
This is a small and simple section, but this will be the one you spend the longest on!
There will be spelling errors(by you and me), difference in setups, external factors etc, so this server is guaranteed not to work first time. Great eh?
But don't worry, we can quickly track down which section is at fault, and solve the issues one by one.
I hope you blocked external acces to your SMTP port (25) in your firewall setting. Otherwise you might have become an open relay for spammers. (Okay unlikely unless you have been running exposed for a few weeks). You will have to unblock it soon, but not yet. Lets first be 100% sure the system works, so only local access to SMTP should be allowed for now.
We will test each section bit by bit to black box certify each bit. First test that postfix delivery works (by exluding content checks and ignoring courier). We will check if it can connect to MySQL for its lookups, if maildir are created and if it can send messages. Then we'll re-enable content checks to see if they work. Then we start testing courier, see if it can access MySQL and if it shows the right mailboxes.
The easiest way to do the testing is with telnet. Turn on full debuggon, tail a few logs a lets get started.
# Making sure nothing is running /etc/init.d/courier stop /etc/init.d/postfix stop /etc/init.d/amavisd stop /etc/init.d/spamassassin stop /etc/init.d/clamav stop /etc/init.d/mysqld stop # Then to check if they really stopped ps aux netstat -tnp
smtp inet n - n - - smtpd #smtp inet n - - - - smtpd # -o cleanup_service_name=pre-cleanup cleanup unix n - - - 0 cleanup #cleanup unix n - - - 0 cleanup # -o mime_header_checks= # -o nested_header_checks= # -o body_checks= # -o header_checks=
#content_filter = amavis:[127.0.0.1]:10024
# In one window do this tail -f /var/log/mysql/mysql.log # then in another tail -f /var/log/maillog.info /etc/init.d/mysqld start # then /etc/init.d/postfix start # then check if postfix is listening on 25 and mysql on 3306 netstat -tnp
Okay up and running (hopefully).
First we will telnet in and try and send a message to a local user.
Then we will try and send to an external user via postfix.
# Lets try and send a message to email@example.com # (replace with your own user in this setup, or use postmaster@localhost) telnet localhost 25 # reponse back: > > > # then open the hand shake with ehlo and the server name you are connecting from... EHLO mail.domain.tld > > > # then say who is the sender of this email MAIL FROM: <firstname.lastname@example.org> > 250 Ok # then say who the mail is for RCPT TO: <email@example.com> > 250 Ok data > 354 End data with <CR><LF>.<CR><LF></LF></CR></LF></CR> # enter message bodyand end with a line with only a full stop. blah blah blah more blah . > 250 Ok; queued as QWKJDKASAS # end the connection with quit > 221 BYE
The postfix log should then start showing up what is happening. If something happens in the mysql log, it means that connection if working.
Then the next step is to test Courier-IMAP.
Again tail the maillog, syslog and mysql log. Turn on DEBULEVEL in /etc/courier/imap to 2.
telnet localhost 143 > > >
telnet 127.0.0.1 10024 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. 220 [127.0.0.1] ESMTP amavisd-new service ready
debug_peer_list = 127.0.0.1
Doing a full reboot to test if everything comes up as desired is probably a good idea as well.
Congratulations, you have a working mail server! Now send me a note to let me know about it.
Testing eh, it is virtually guaranteed no project works on the first go. But we need to box it off and find out if each section works one by one.
So first we stop everything, to then bring it up one by one as each test passes.
In theory nothing should be running, but some of the install packages might be started automatically.
/etc/init.d/apache2 stop /etc/init.d/courier-imap-ssl stop /etc/init.d/courier-imap stop /etc/init.d/courier-authdaemon stop /etc/init.d/courier stop /etc/init.d/postfix stop /etc/init.d/postgrey stop /etc/init.d/amavisd stop /etc/init.d/spamassassin stop /etc/init.d/clamav-daemon stop /etc/init.d/clamav-freshclam stop /etc/init.d/mysql stop
ps aux | more
If you setup the firewall as I advised in the configure chapter, then your box is pretty blocked off from the outside. If not then you might have been an open relay for spammers, but hopefully not.
Now we need to open up access the local network so you can test locally and remotely. However it is not ready for net access yet.
# uncomment these lines AllowPing loc fw AllowSMTP loc fw ACCEPT loc fw tcp 465,587 - AllowIMAP loc fw AllowWeb loc fw
MySQL should work fine after installation and configuration.
However as we go through the other sections, it is very usefull to tail the mysql query log file throught all the tests. This is an easy way to see if each application has its database settins configured correctly.
/etc/init.d/mysql start tail -f /var/log/mysql/mysql.log
The full configured Postfix is overstuffed with features. We need to disable all this to test the basics.
You should are already tailing the mysql log in one window, but now we need to tail the mail log file as well.
tail -f /var/log/mail.log
Basically we are reversing to the postfix config from the config section before we added the content checks, encryption etc.
I am not modifying /etc/postfix/master.cf, however you can comment out the lines we added in there as well.
However in main.cf:
## comment out this line # content_filter = amavis:[127.0.0.1]:10024 ## then replace these lines #smtpd_helo_restrictions = permit_mynetworks, # warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, permit #smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, # reject_non_fqdn_sender, reject_unknown_sender_domain, # reject_unauth_pipelining, permit #smtpd_client_restrictions = reject_rbl_client sbl.spamhaus.org, # reject_rbl_client relays.ordb.org, reject_rbl_client blackholes.easynet.nl, # reject_rbl_client dnsbl.njabl.org #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 ## with these smtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, permit smtpd_sender_restrictions = permit_mynetworks, reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit smtpd_client_restrictions = smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, permit ## comment out these lines #broken_sasl_auth_clients = yes #smtpd_sasl_auth_enable = yes #smtpd_sasl_path = /etc/postfix/sasl:/usr/lib/sasl2 #smtpd_sasl_security_options = noanonymous #smtpd_sasl_local_domain = #smtp_use_tls = yes #smtp_tls_cert_file = /etc/postfix/postfix.cert #smtp_tls_key_file = /etc/postfix/postfix.key #smtpd_use_tls = yes #smtpd_tls_cert_file = /etc/postfix/postfix.cert #smtpd_tls_key_file = /etc/postfix/postfix.key #smtpd_data_restrictions = reject_unauth_pipelining
With MX backup loosing emails are unlikely.
Normally if someone sends an email destined for you, their server will try and connect to your server. If it can't reach your server for whatever reason ( it is down, dns issues, there is network problems, or just too busy ), the other server will back off and try again in a bit. How many and for how long it will try again is determined by the sending server. Some of them are not very patience, and it will report undelivered after only a few attempts. So you would have lost that email.
If you had specified a backup MX, this email may not have been lost. Upon first failure to connect to your server, the sender would see if there is any alternative server to send to. So it connects to your backup mx server. This server spools and queues your message and will try at intervals to send the message to you. It too will though eventually give up.
What is the difference? Simple, you (or whoever controls the backup mx ) is in control how long and often to try connecting to your machine. So if you have a reasonable values and your server is not down for weeks, no mail is lost.
How to implement it? First edit the DNS records again, and add a backup mx with a higher value.
# your server details domain.tld IN MX 10 your.mailserver.name.tld # new backup server domain.tld IN MX 20 your.backupserver.name.tld
CREATE TABLE `backups` ( `pkid` smallint(6) NOT NULL auto_increment, `domain` varchar(128) NOT NULL default '', `transport` varchar(128) NOT NULL default ':', `enabled` smallint(6) NOT NULL default '1', PRIMARY KEY (`pkid`), UNIQUE KEY `domain` (`domain`) );
relay_domains = mysql:/etc/postfix/mysql_backups.cf transport_maps = mysql:/etc/postfix/mysql_transport.cf
proxy_interfaces = 126.96.36.199
If someone comes with a better way, then let me know.
Next create this file /etc/postfix/mysql_backups.cf
user=mail password=apassword dbname=maildb table=backups select_field=domain where_field=domain hosts=127.0.0.1 additional_conditions = and enabled = 1
user=mail password=apassword dbname=maildb table=backups select_field=transport where_field=domain hosts=127.0.0.1 additional_conditions = and enabled = 1
You noticed I added a transport lookup. This is a field in both the domain and the backup tables. In domains it is used to determine how to deliver the email, ie either virtual (correct) or local (not used in this howto). When backing up servers, your also need to specify in the transport field how to connect to the correct servers.
Say you are backiup for a friends server, mail.friend.com, for the domains of friend1.com and friend2.com. So you should insert this into your backup table.
INSERT INTO backups (domain,transport) VALUES ('friend1.com' , ':[mail.friend.com]' ), ('friend2.com' , ':[mail.friend.com]' );
The : tells to connect directly to this server, not doing any more look ups for valid MX servers.
This shouls now work fine. Further tweaking of the queue values, review these and modify as appropiate. Shorter warning times are good for the sender, so that they realise the email has not arrived yet, but may also be annoying. Tradeoffs.. Look in the first main.cf configurations for ways to do so.
tar czf mail-config.xxxxx.tgz /etc/postfic /etc/courier /etc/spamassassin /etc/clamav /etc/amavis /etc/mysql/my.cnf tar czf mail-fold.xxxx.tgz /var/spool/mail/virtual mysqldump -u mail -papassword -t maildb > data.sql mysqldump -u mail -papassword -d maildb > schema.sql tar czf mail-data.xxx.tgz schema.sql data.sql tar cf mail.xxxxx.tar mail-*.xxxxx.tgz
tar --newer-mtime "2005-01-01"
While SPF should limit who can send mail on behalf of your domains, ( so basically less spoofed spam addresses ), I do have some technical issues with SPF as the design of it is a bit iffy. That is because of the limitation of DNS and that it has to fit inside the limited TEXT part. No nice XML config file....
While Microsoft is not always entirely evil, as sometimes they do nice things and make some usefull software, I would prefer not to be locked into their Sender ID technology.
Reporting spam to Pyzor, Razor and SpamCop, for collaboration in spam fighting.
More detail on SpamCop is here.
You can implement white and black lists to explicitly allow or block domains and users.
You have already visited the option of a blackhole list of known open relays in the postfix configuration.
You can implement further lists inside Postfix or SpamAssassin. Amavisd-new already has a few well known white/black listed items in its config files. SpamAssissin also as a feture to automaticly learn white lists.
Adding support for GnuPG and S/MIME increases indiviual security.
This is not implemented on the postfix server side, as this totally a client side option.
However SquirrelMail has a GnuPG option. It is a plugin that can be downloaded from their website. Which can then be enabled via SquirrelMail's config script.
Here is how to create a GnuPG key pair.
# check you have not already got a key gpg --list-keys # then create one gpg --gen-key
To import GnuPG into Evolution; in your settings/preferences edit your account settings and add you private key under the security tab. The private key is found via listing the GnuPG keys as above, then it is the 8 characters after the "sub 1024g/" bit of you key.
To use GnuPG with Thunderbird you need to install EnigMail.
S/MIME is another way to encrypt and/or sign messages. You can create you own certificate or use known organizations like Thawte. (Thawte was originally set up by the Ubuntu founder)
CREATE TABLE `relocated` ( `pkid` smallint(6) NOT NULL auto_increment, `oldadr` varchar(128) NOT NULL default '', `newadr` varchar(128) NOT NULL default '', `enabled` tinyint(1) NOT NULL default '1', PRIMARY KEY (`pkid`), UNIQUE KEY `oldadr` (`oldadr`) ) ;
relocated_maps = mysql:/etc/postfix/mysql_relocated.cf
user=mail password=apassword dbname=maildb table=relocated select_field=newadr where_field=oldadr hosts=127.0.0.1
INSERT INTO relocated (oldadr,newadr)VALUES ('firstname.lastname@example.org','email@example.com');
Trying out a few admin software might make you life easier, if phpMyAdmin gets to crude. Quick search
More to come later.
Postfix have now features to auto reply to an email, while still delivering it to its alias.
If you use catch alls, which are usefull for some domains, then eventually some addresses will be target for spam. You can then either stop the catch all, or stop indivdual addresses.
By implementing a lookup and adding this restriction to smtpd_recipient_restrictions accomplises this.check_recipient_access mysql:/etc/postfix/mysql_block_recip.cf,
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, \ check_recipient_access mysql:/etc/postfix/mysql_block_recip.cf, \ reject_non_fqdn_recipient, reject_unauth_destination, \ check_relay_domains
Beware of the order is important here, if any options says ok before check_recipient_access it will ignore it.
Next create mysql_block_recip.cf to lookup addresses. Either create a another table, or add a blocked field to aliases table.
Rich Brown has written a howto on adding Mailman, a mail list program, to my howto. Click here to read it.
Do note it is not part of my howto, so do not contact me regarding it. And although I think it is fine, I can't guarantee it will work.
If you want a simple mailling list, it can be implemented by simply seperating aliases in the destination field in the aliases table with a comma.
INSERT INTO aliases (mail,destination) VALUES ( 'firstname.lastname@example.org' , 'email@example.com,firstname.lastname@example.org,email@example.com' );
Here are the differences for this howto if you are using Ubuntu 5.04, 5.10 or 6.04. (Hoary, Breezy or Dapper)
Mainly this will be package names, and some changes in defaults.
This edition is based on Hoary while in development, however will be based on Breezy when finished.
Also some packages might not be available in 64bit or other platforms.
Changes for Ubuntu 5.04, Hoary Hedgehog:
The repositories used are hoary and not breezy. Version has changed, otherwise the same.
Changes for Ubuntu 5.10, Breezy Badger
None as this is based on breezy
Changes for Ubuntu 6.04, Dapper Drake
If you problems, questions, comments regarding this howto, postfix or mail servers in general use either one of these forum threads:
They are threads on Ubuntu forums web site. You have a much higher chance for a reply and a proper discussion about it if you post there.
You can contact me, however I can be rather slow to reply, but I do like to hear from people who are happy with this howto. If you have written extensions or can recommend links then I would like to hear from you. Please then use the form below.
Task that needs to be done for this howto. And what state they are at.
Typically for everyone I complete, 3 more are added to the list...