Installing a mail server on Red Hat Linux 7.3
I wanted to have a complete full-featured mail server which would be easy to manage through LDAP using directory administrator (a nice gtk+ application). I chose postfix over sendmail for its ease of integration and bad memories of milter, chose dovecot over courier or cyrus because it looked much closer to the little requirements I had, and amavisd-new over mailscanner because I had already installed mailscanner once and... just wanted to see what else was available. It turns out the mix works quite well.
The chosen components :
- Postfix - I've always used sendmail, and started a while back only because it was the default. I always wanted to try postfix, this was the perfect occasion.
- Dovecot - A young but full-featured pop3/imap4 server designed with security in mind which supported all I needed such as LDAP, TLS and single uid Maildirs.
- Spamassassin - The widely used spam killer. I had used it locally on my laptop through the sylpheed-claws plugin and definitely wanted to include it upstream.
- Clam AntiVirus - A free antivirus software which uses the OpenAntivirus virus database. Great to block the most common viruses that threaten Windows users.
- AMaViSd-new - A daemon that interfaces very easily with postfix, uses the Mail::SpamAssassin perl module directly and recognizes plenty of antiviruses.
- OpenLDAP - The most widely used LDAP daemon, the only component I used which comes directly from the base distribution.
Too much theory? Not enough eye candy? Take a peek at the screenshot!!
Postfix configuration :
- Configuration changes in main.cf :
I added the hostname I wanted my server to use :
myhostname = mx1.whatever.ext mydomain = whatever.ext myorigin = $mydomain
I also changed it to listen on the external network interface :inet_interfaces = $myhostname, localhost #inet_interfaces = localhost
I added the main domain to be handled locally, as well as all the domains that trigger LDAP lookups in order to have amavisd-new rewrite spam subjects for them :mydestination = $myhostname, localhost.$mydomain, $mydomain, hash:/etc/postfix/ldapdomains
I also added a file in order to allow a few other external sites to relay :mynetworks = $config_directory/mynetworks
Now for the virtual domain configuration, I added the following (see below for the files) :virtual_alias_maps = hash:/etc/postfix/virtual, ldap:whateverldapalias virtual_alias_domains = hash:/etc/postfix/virtual-domains
To have SMTP AUTH working, I also added this :# auth stuff # smtpd_sasl_auth_enable = yes smtpd_sasl_security_options = noanonymous broken_sasl_auth_clients = yes smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination
And to also have TLS (encryption) working :# tls stuff # smtpd_use_tls = yes smtpd_tls_loglevel = 1 smtpd_tls_cert_file = /etc/postfix/ssl/both.pem smtpd_tls_key_file = $smtpd_tls_cert_file
I also increased the maximum allowed message size from 10MB to 50MB :# 50MB message limit message_size_limit = 51200000
Now, much more interesting, the virtual delivery using a single fixed uid/gid pair for all mailboxes :# virtual mailbox delivery local_transport = virtual virtual_mailbox_base = /vmail virtual_mailbox_maps = ldap:whateverldapdir virtual_uid_maps = static:100 virtual_gid_maps = static:100 virtual_minimum_uid = 100 virtual_mailbox_limit = 0
And for the LDAP connections, one is for aliases and the other for delivery. As you see, I reuse most of the first in the second. With those values, the mail for user "foo" will be delivered to /vmail/home/foo/Maildir/ (see "/vmail" above) assuming the user's home directory is /home/foo. With the "/vmail" directory being owned by 100:100, postfix will automatically create all needed subdirectories to make the delivery.# ldap whateverldapalias_server = localhost whateverldapalias_server_port = 389 whateverldapalias_bind = yes whateverldapalias_bind_dn = uid=Postfix,ou=Servers,dc=whatever,dc=ext whateverldapalias_bind_pw = yourpassword whateverldapalias_timeout = 10 whateverldapalias_search_base = dc=whatever,dc=ext whateverldapalias_scope = sub # don't search through ldap for all domains whateverldapalias_domain = hash:/etc/postfix/ldapdomains whateverldapalias_query_filter = (&(objectClass=posixAccount)(|(mail=%s)(mailAlternateAddress=%s))) whateverldapalias_result_attribute = mail whateverldapdir_server_host = $whateverldapalias_server whateverldapdir_server_port = $whateverldapalias_server_port whateverldapdir_bind = $whateverldapalias_bind whateverldapdir_bind_dn = $whateverldapalias_bind_dn whateverldapdir_bind_pw = $whateverldapalias_bind_pw whateverldapdir_timeout = $whateverldapalias_timeout whateverldapdir_search_base = $whateverldapalias_search_base whateverldapdir_scope = $whateverldapalias_scope whateverldapdir_query_filter = (&(objectClass=posixAccount)(mail=%s)) whateverldapdir_result_attribute = homeDirectory whateverldapdir_result_filter = %s/Maildir/
Last, here I told postfix to use amavisd-new for content filtering (it needs a change to master.cf too) :# spam and virus scanning content_filter = smtp-amavis:[127.0.0.1]:10024 max_use = 10
- Changes to master.cf :
I added the following to the bottom of master.cf in order to give amavisd-new a way to reinject mail back in. It is apparently recommended to use lmtp instead of smtp there, but I experienced problems with lmtp which disappeared when going back to smtp.
# ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== smtp-amavis unix - - n - 10 smtp -o smtp_data_done_timeout=1200 -o disable_dns_lookups=yes 127.0.0.1:10025 inet n - n - 10 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 mynetworks=127.0.0.0/8 -o strict_rfc821_envelopes=yes
- Additional files :
Relaying access control through /etc/postfix/mynetworks :
# Loopback 127.0.0.0/8 # Office 1 192.168.1.1 # Office 2 192.168.32.1
Domains which will trigger LDAP lookups, /etc/postfix/ldapdomains :whatever.ext foo subdomain.whatever.ext bar other-ldap-dom.ext you-need-a-bogus-right-value
Virtual domains and aliases with respectively /etc/postfix/virtual-domains and /etc/postfix/virtual :# No need to put the ldap domains here again virtual-alias-domain.whatever.ext foo other-virtual-domain.ext bar
root@whatever.ext user@whatever.ext foo@other-virtual-domain.ext user@whatever.ext
Last, don't forget to change the "root:" line in /etc/postfix/aliases.
SASL configuration (for SMTP AUTH) :
As the only way to do SMTP AUTH with postfix is through sasl and that sasl has no way of using LDAP directly, I had to configure sasl to use pam then have pam use LDAP... quite a complex setup for such a simple task, but I couldn't think of a better solution.
- Configuring sasl to use pam :
I just added a file called /usr/lib/sasl/smtpd.conf with the following (the file may be /usr/lib/sasl2/smtpd.conf if you use saslv2) :
pwcheck_method: pam
- Configuring pam to use LDAP :
I added two lines to /etc/pam.d/smtp to look like this :
#%PAM-1.0 auth sufficient pam_ldap.so auth required pam_stack.so service=system-auth account sufficient pam_ldap.so account required pam_stack.so service=system-auth
- Configuring client LDAP access on the system :
You need to create the /etc/ldap.conf file if it doesn't exist, make sure it's not world readable as it will contain a clear text password. Mine looks like this, as it also contains idle stuff for NSS and local/ssh access :
# Connection options host localhost base dc=whatever,dc=ext scope sub pam_password md5 # Bind to the server as binddn uid=NSS,ou=Servers,dc=whatever,dc=ext bindpw yourpassword # Only allow login for members of "tech" who's "host" matches and uid > 1000 pam_groupdn cn=tech,dc=whatever,dc=ext pam_check_host_attr yes pam_min_uid 1000 # Password update policy, OpenLDAP style # Where to look for groups, they're all flat in there nss_base_group dc=whatever,dc=ext ssl no
Dovecot configuration :
- Changes to /etc/dovecot.conf :
I added pop3s and imaps :
protocols = imap imaps pop3 pop3s
I also added paths to the ssl files (both cert and key are in the same) :ssl_cert_file = /usr/share/ssl/certs/dovecot.pem ssl_key_file = /usr/share/ssl/certs/dovecot.pem
Since I'm using a fixed uid and gid of 100 for all mailboxes, I also added :first_valid_uid = 100 last_valid_uid = 100 first_valid_gid = 101 last_valid_gid = 101
The following line makes divecot look into the right place for the Maildir directories :default_mail_env = maildir:/vmail%h/Maildir
To use LDAP authentication, I replaced the default auth_userdb line with :auth_userdb = ldap /etc/dovecot-ldap.conf
I also changed some of the defaults for performance or security reasons :verbose_proctitle = yes maildir_copy_with_hardlinks = yes mbox_locks = fcntl auth_mechanisms = plain digest-md5 auth_user = dovecot auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@
- New /etc/dovecot-ldap.conf file :
I've added the /etc/dovecot-ldap.conf file, based on the example one found in the documentation, in order to get LDAP authentication to work with dovecot. The only used attributes are uid, homeDirectory and userPassword.
# NOTE: We don't support "authentication binds", so you'll have to give # dovecot-auth read access to userPassword field in LDAP server. With OpenLDAP # this is done by modifying /etc/ldap/slapd.conf. There should already be # something like this: # # access to attribute=userPassword # by dn="<dovecot's dn>" read # add this # by anonymous auth # by self write # by * none # Space separated list of LDAP hosts to use. host:port is allowed too. hosts = localhost # Distinguished Name - the username used to login to the LDAP server dn = uid=Dovecot,ou=Servers,dc=whatever,dc=ext # Password for LDAP server dnpass = yourpassword # LDAP protocol version to use. Likely 2 or 3. ldap_version = 3 # LDAP base base = dc=whatever,dc=ext # Dereference: never, searching, finding, always deref = never # Search scope: base, onelevel, subtree scope = subtree # User attributes in order: # Virtual user name (user@domain) # Home directory # MAIL environment # System user name (for initgroups()) # System UID # System GID #user_attrs = uid,homeDirectory,,uid,uidNumber,gidNumber user_attrs = uid,homeDirectory,,uid,, # Filter for user lookup. Some variables can be used: # %u - username # %n - user part in user@domain, same as %u if there's no domain # %d - domain part in user@domain, empty if user there's no domain user_filter = (&(objectClass=posixAccount)(uid=%u)) # Password checking attributes in order: # Virtual user name (user@domain) # Password, may optionally start with {type}, eg. {crypt} pass_attrs = uid,userPassword # Filter for password lookups #pass_filter = (&(objectClass=posixAccount)(uid=%u)) # Default password scheme. "{scheme}" before password overrides this. # Currently supported schemes include PLAIN, PLAIN-MD5, DIGEST-MD5, CRYPT #default_pass_scheme = CRYPT # You can use same UID and GID for all user accounts if you really want to. # If the UID/GID is still found from LDAP reply, it overrides these values. user_global_uid = 100 user_global_gid = 100
Spamassassin configuration :
As spamassassin is used by amavisd-new directly through its perl module, you don't need to do anything outside of /etc/amavisd.conf to get it working. No need to start spamd or edit spamassassin's configuration files.
Clam Antivirus configuration :
Nothing much to change. The defaults suited me, there were just some ownerships that needed to be changed to "amavis" in order for both to be able to interoperate.
AMaViSd-new configuration :
There is much to configure here, and optimal settings are probably very different from one site to another. Nevertheless I'll detail the changes I've made to suit my own needs. The whole file is pretty well commented, so do take the time to read it to understand all the possibilities amavisd-new has to offer.
- Changes to /etc/amavisd.conf :
It is mandatory to change the domain to a real one :
$mydomain = 'whatever.ext'; # (no useful default)
In order to get mail back into postfix, you need to uncomment these lines :$forward_method = 'smtp:127.0.0.1:10025'; # where to forward checked mail $notify_method = $forward_method; # where to submit notifications
I changed the following line in order to have more than the default domain handled as local, as spamassassin filtering will only occur for reciepients in those domains.@local_domains_acl = qw( .whatever.ext .other-virtual-domain.ext );
As I've put the spam trigger quite high, I've decided to discard those who reach a very high score.$final_spam_destiny = D_DISCARD; # (defaults to D_REJECT)
I've uncommented one of the longer file extension checks, the default list is quite short :qr'.\.(ade|adp|bas|bat|chm|cmd|com|cpl|crt|exe|hlp|hta|inf|ins|isp|js| jse|lnk|mdb|mde|msc|msi|msp|mst|pcd|pif|reg|scr|sct|shs|shb|vb| vbe|vbs|wsc|wsf|wsh)$'ix, # banned extension - long
I've changed the spam levels, as I discard all emails that have a score higher than the kill level. I really don't think anything apart from some big fat spam can score above 15, so it's pretty safe. With 4.8 as the level at which to tag as "***SPAM***", I didn't get a single false positive after a week of surveillance :$sa_tag_level_deflt = 2.0; # add spam info headers if at, or above that level $sa_tag2_level_deflt = 4.8; # add 'spam detected' headers at that level $sa_kill_level_deflt = 15.0; # triggers spam evasive actions $sa_spam_subject_tag = '***SPAM*** '; # (defaults to undef, disables)
Last, I've uncommented the Clamav section, and it was automatically detected, as well as the exectuable version as a backup antivirus. Check the logs if you have any kind of doubts or file ownership issues to get both to interact.# ### http://clamav.elektrapro.com/ ['Clam Antivirus-clamd', \&ask_daemon, ["CONTSCAN {}\n", '/var/clamav/clamd.socket'], qr/\bOK$/, qr/\bFOUND$/, qr/^.*?: (?!Infected Archive)(.*) FOUND$/ ], # NOTE: run clamd under the same user as amavisd, # match the socket name in clamav.conf to the socket name in this entry
OpenLDAP configuration :
This is probably the most lengthy part of the setup if you're not familiar with LDAP in general. I wasn't, and spent a lot of time figuring things out, as the whole LDAP protocol and the OpenLDAP implementation are quite rough around the edges, or at least I think so.
- My /etc/openldap/slapd.conf file :
# See slapd.conf(5) for details on configuration options. # This file should NOT be world readable. include /etc/openldap/schema/core.schema include /etc/openldap/schema/cosine.schema include /etc/openldap/schema/inetorgperson.schema include /etc/openldap/schema/nis.schema include /etc/openldap/schema/redhat/rfc822-MailMember.schema include /etc/openldap/schema/redhat/autofs.schema include /etc/openldap/schema/redhat/kerberosobject.schema # Added for Samba include /etc/openldap/schema/samba.schema TLSVerifyClient never TLSCipherSuite HIGH:MEDIUM:+SSLv2 TLSCertificateFile /usr/share/ssl/certs/slapd.pem TLSCertificateKeyFile /usr/share/ssl/certs/slapd.pem TLSCACertificateFile /usr/share/ssl/certs/ca-bundle.crt #TLSCipherSuite HIGH access to attr=userPassword by dn.one="ou=Servers,dc=whatever,dc=ext" read by self write by anonymous auth by * none access to * by dn.one="ou=Servers,dc=whatever,dc=ext" read by * search ####################################################################### # ldbm database definitions ####################################################################### database ldbm suffix "dc=whatever,dc=ext" rootdn "cn=hostmaster,dc=whatever,dc=ext" rootpw {SSHA}zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz directory /var/lib/ldap index objectClass,uid,uidNumber,gidNumber,memberUid eq index cn,mail,surname,givenname eq,subinitial
I won't detail my schema here, it's pretty standard, and can easily be guessed from the entries used all along. To add users to the database, I use directory_administrator. It doesn't do all that I'd like it to, it can't help for the initial setup, but it's still quite useful on a daily basis. It's worth mentionning also that despite having tried hard, I was unable to get TLS working.
The end!
I hope this will have been useful. Please do not contact me directly for help, use mailing-lists instead, but do let me know if you see any mistakes or possible improvements to the document or the setup itself.
Last of all... for Red Hat Linux 7.3, all the software mentioned above and all its dependencies (this includes quite a lot of perl modules for amavisd-new for instance) can be found in the testing section.
Here are the packages used at time of initial writing (July 2003) :
- postfix 2.0.13-0.rh73.1
- cyrus-sasl 2.1.10-0.rh73.4
- db4-4.1.25-0.rh73.3
- dovecot 0.99.10-1.2
- spamassassin 2.55-1
- amavisd-new 20030616-1
- clamav 0.60-4
- openldap 2.0.27-2.7.3
Update (Wed 3 Sep 2003) :
- AMaViSd-new : You may want to comment out "$spam_quarantine_to" and/or "$virus_quarantine_to" in order to not store spams and viruses on your server, as it can take up quite a lot of space very quickly.
- Clamav : To interface clamav and amavisd you will need to enable group write (chmod g+w) on the /var/clamav directory, make it owned by the "amavis" group (chgrp amavis), add the "clamav" user to the "amavis" group and change "User" in /etc/clamav.conf to "amavis". The socket created by clamav will then be accessible by amavis, and the freshclam update will still be able to work.