3

http://theobviousthings.com/init/default/show_page/1

postfix + dovecot + amavisd + opendkim + letsencrypt + webmail on CentOS 7

Amavisd, Spamassassin and clamav

This software trio is used to fight spam messages and e-mails with virus attachments. Amavisd is used as an interface between postfix as MTA (mail transfer agent) and the content checkers (spamassassin and clamav). Clamav and spamassassin will be installed automatically once you install amavisd-new.

1

2

yum install amavisd-new

yum install clamav-udpate

First, let’s configure amavisd. The configuration file is /etc/amavisd/amavisd.conf. Edit this file and make sure that these values are correct.

1

2

3

4

5

6

7

8

9

# @bypass_virus_checks_maps = (1);  # controls running of anti-virus code

# @bypass_spam_checks_maps  = (1);  # controls running of anti-spam code

# $bypass_decode_parts = 1;         # controls running of decoders&dearchivers

$daemon_user  = 'amavis';     # (no default;  customary: vscan or amavis), -u

$daemon_group = 'amavis';     # (no default;  customary: vscan or amavis), -g

$mydomain = 'example.com';   # a convenient default for other settings (change it)

$MYHOME = '/var/spool/amavisd';   # a convenient default for other settings, -H (remove the comment in front)

@local_domains_maps = ( [".$mydomain","myotherdomain.net"] );  # list of all local domains. If you have multiple domains, add the here.

$myhostname = 'host.example.com';  # must be a fully-qualified domain name! (remove the comment in front)

There are a lot of changes that you can configure, but these are the basic ones. See the official page for more information.

Finally, let’s make sure that amavisd and clamav know about each other. Go to line 383 and see if this is correct (383G in vi).

NOTE ABOUT MEMORY

Clamav eats up a lot of memory. You won’t be able to run it on a server with less than 2GB RAM. If you feel comfortable, you can disable it. Don’t uncomment the lines below, remove the comment from # @bypass_virus_checks_maps = (1); # controls running of anti-virus code and

1

2

3

4

5

6

7

8

9

10

# ### http://www.clamav.net/

['ClamAV-clamd',

  \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd.sock.sock"],

  qr/\bOK$/m, qr/\bFOUND$/m,

  qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],

# # NOTE: run clamd under the same user as amavisd - or run it under its own

# #   uid such as clamav, add user clamav to the amavis group, and then add

# #   AllowSupplementaryGroups to clamd.conf;

# # NOTE: match socket name (LocalSocket) in clamav.conf to the socket name in

# #   this entry; when running chrooted one may prefer a socket under $MYHOME.

Now, let’s tie everything together with postfix. Edit /etc/postfix/master.cf and add these lines at the end.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

# Amavisd

amavisfeed unix - - n - 2 lmtp

        -o lmtp_data_done_timeout=1200

        -o lmtp_send_xforward_command=yes

127.0.0.1:10025 inet n - n - - smtpd

        -o content_filter=

        -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 smtpd_restriction_classes=

        -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,no_milters,no_address_mappings

        -o local_header_rewrite_clients=

        -o smtpd_milters=

        -o local_recipient_maps=

        -o relay_recipient_maps=

Edit /usr/local/etc/postfix/main.cf and add these lines at the end.

1

2

# Amavisd

content_filter = amavisfeed:[127.0.0.1]:10024

Before we start these three daemons, let’s make some changes. First, edit /etc/freshclam.conf and remove or comment the Example line.

1

2

# Comment or remove the line below.

Example

Then, edit /etc/sysconfig/freshclam and remove all 4 lines at the bottom.

1

2

3

4

### !!!!! REMOVE ME !!!!!!

### REMOVE ME: By default, the freshclam update is disabled to avoid

### REMOVE ME: network access without prior activation

FRESHCLAM_DELAY=disabled-warn   # REMOVE ME

Once you do that, update the antivirus definitions by executing:

1

freshclam

Then update spamassassins’s signatures.

1

sa-update -D

The “-D” option is to run in debug mode so you can see what’s going on. If there is an update available, spamassassin’s exit code is 0, if not the exit code is 1. In case there is an update, we’ll have to restart the spamassassin’s daemon.

Check the cron folder and make sure you see both calamav-update and sa-update files.

1

2

cd /etc/cron.d

ls -l clamav-update sa-update

Now, we can start everything.

1

2

3

4

systemctl start amavisd

systemctl enable amavisd

systemctl start spamassassin

systemctl enable spamassassin

Once you start amavisd, clamd starts as well.

Finally, let’s restart postfix.

1

systemctl restart postfix

Check the connection between amavisd and postfix.

1

telnet localhost 10024

Type ehlo localhost and check the ouput. In my case it looks like this.

250-[127.0.0.1]

250-VRFY

250-PIPELINING

250-SIZE

250-ENHANCEDSTATUSCODES

250-8BITMIME

250-DSN

250 XFORWARD NAME ADDR PORT PROTO HELO IDENT SOURCE

Then do.

1

telnet localhost 10025

Again, type ehlo localhost and check the ouput. In my case it looks like this.

250-www.testcloudserver.org

250-PIPELINING

250-SIZE 10240000

250-VRFY

250-ETRN

250-STARTTLS

250-ENHANCEDSTATUSCODES

250-8BITMIME

250 DSN

And finally, some real tests. First, check the mail log file.

1

tail -f /var/log/maillog

Then, from another e-mail account, send a text (not HTML) e-mail with this in the body.

X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

How to setup a fully authenticated mail server on CentOs 7.

This howto will cover basics of setting up a postfix mail server with dovecot POP and IMAP client. Furthermore, we will show how to ensure our emails are not flagged as spam and are accepted by big email providers such as gmail. In the end we will setup webmail client and will sign our email and web server with an official certificate authority using letsencrypt.org.

Acknowledgments

This howto wouldn’t have happened if it weren’t for two excellent tutorials by Kliment Andreev and Stephane Caron and an excellent documentation by letsencrypt.org.

Before we start

The first thing we need to do is to check if our ip is not blacklisted. You can do it on blacklistalert.org or mxtoolbox, just type in your IP and it will list its status. If you IP is blacklisted then you need to ask you ISP for a new one.

Prerequisites

This assumes that you have a working installation of CentOs 7.

So first let’s upgrade

yum -y upgrade

Before we begin we’ll need to install epel repository, so type

yum -y install epel-release

In our first step we will just configure postfix and dovecot server without any encryption and after we verify that we have a working email server we’ll add encryption, spam filter and message signing.

Configuring postfix

Now let’s create a user and group that we’ll use for postfix

groupadd vpostfix && useradd vpostfix -g vpostfix -s /sbin/nologin -c "Virtual postfix user" -d /var/empty

Next, we need to check uid and gid of our newly created group and user.

grep vpostfix /etc/passwd && grep vpostfix /etc/group

You will see something like this. Remember these numbers because we will need them later.

vpostfix:x:1000:1000:Virtual postfix user:/var/empty:/sbin/nologin vpostfix:x:1000:

Now edit main.cf in /etc/postfix and change the following lines (of course, replacing example.com with your real domain)

myhostname = www.example.com mydomain = example.com myorigin = $mydomain inet_interfaces = all home_mailbox = Maildir/

Add the following to the end of main.cf (be careful to put the same uid and gid as the ones we have just created)

# Virtual domain config virtual_mailbox_domains = /etc/postfix/virtual_domains virtual_mailbox_base = /var/mail/vhosts virtual_mailbox_maps = hash:/etc/postfix/vmailbox  # Make sure you replace these UID:GID numbers virtual_minimum_uid = 1000 virtual_uid_maps = static:1000 virtual_gid_maps = static:1000 virtual_alias_maps = hash:/etc/postfix/virtual

Now we have to create a file for virtual domains named /etc/postfix/virtual_domains. In this file we will list all domains that this mail server will serve. Do not forget to configure MX records of all these domains so that they point to this server.

cd /etc/postfix/ touch virtual_domains

Now edit the file virtual_domains and put each domain your mail server is going to server on separate line.

#  Each domain is on separate line example.com example2.com example3.com

After that, create the directory for email, and create all subdirectories for the domains. Our e-mail will be stored in these directories.

mkdir /var/mail/vhosts chgrp -R vpostfix /var/mail cd /var/mail/vhosts mkdir example.com mkdir example2.com mkdir example3.com cd .. chown -R vpostfix:vpostfix vhosts

Postfix will now create the “Maildir” directories automatically and will assign them proper permissions.

Now, create a file /etc/postfix/vmailbox and in that file list all of the users for all the domains that will receive e-mails.

dragan@example.com        example.com/dragan/ john@example2.com         example2.com/john/

Be sure to end up each line with “/”, or mail will not be delivered.

Now, let’s create the virtual aliases file and create a local aliases file.

touch /etc/postfix/virtual cd /etc postalias aliases

When we’ve added all our users, we need to do the following to create the hashed files.

NOTE: These two commands need to be executed anytime you make a change to these files (virtual_domains and vmailbox).

postmap /etc/postfix/virtual postmap /etc/postfix/vmailbox

You should restart postfix now so it can reload new settings.

systemctl restart postfix

You can check the log file with the command:

tail /var/log/maillog

You should see that the daemon is started, something like this:

Dec 18 08:00:30 sysname postfix/postfix-script[26682]: stopping the Postfix mail system Dec 18 08:00:30 sysname postfix/master[1014]: terminating on signal 15 Dec 18 08:00:30 sysname postfix/postfix-script[26764]: starting the Postfix mail system Dec 18 08:00:30 sysname postfix/master[26766]: daemon started -- version 2.10.1, configuration /etc/postfix

You can check if postfix is up and listens on port 25 with:

ps -eaf | grep postfix root     26766     1  0 08:00 ?        00:00:00 /usr/libexec/postfix/master -w postfix  26767 26766  0 08:00 ?        00:00:00 pickup -l -t unix -u postfix  26768 26766  0 08:00 ?        00:00:00 qmgr -l -t unix -u root     26793  8708  0 08:01 pts/0    00:00:00 grep --color=auto postfix  ss -l | grep smtp u_str  LISTEN     0      100    private/smtp 42340                 * 0 tcp    LISTEN     0      100     *:smtp                  *:* tcp    LISTEN     0      100    :::smtp                 :::*

If you don’t see anything in the log and you verified that postfix is listening on port 25 from the server itself, it is probably a firewall issue. Do not forget to open the SMTP port.

firewall-cmd --add-service=smtp --permanent success firewall-cmd --reload success

Now is the time to test your email server. send a mail to user@example.com (of course, replacing user and example.com with your real username and domain names).

tail -f /var/log/maillog

you should see something like this:

Dec 18 08:04:32 example postfix/smtpd[27607]: connect from www.somedomain.com[46.101.163.203] Dec 18 08:04:32 example postfix/smtpd[27607]: B5D433FDFE: client=www.somedomain.com[46.101.163.203] Dec 18 08:04:32 example postfix/cleanup[27611]: B5D433FDFE: message-id=<5674045E.3010300@somedomain.com> Dec 18 08:04:32 example postfix/qmgr[26768]: B5D433FDFE: from=<dragan@somedomain.com>, size=1700, nrcpt=1 (queue active) Dec 18 08:04:32 example postfix/smtpd[27607]: disconnect from www.somedomain.com[46.101.163.203] Dec 18 08:04:32 example postfix/virtual[27612]: B5D433FDFE: to=<dragan@example.com>, relay=virtual, delay=0.06, delays=0.03/0.03/0/0, dsn=2.0.0, status=sent (delivered to maildir) Dec 18 08:04:32 example postfix/qmgr[26768]: B5D433FDFE: removed

After we’ve made sure we can receive e-mail we will now install dovecot.

Installing dovecot

yum -y install dovecot

We will now configure dovecot for simple authentication without any encryption. After we make sure dovecot is up and running we will add encryption.

Edit /etc/dovecot/dovecot.conf and uncomment the following line

protocols = imap pop3 lmtp

Then head to /etc/dovecot/conf.d directory and change the following files:

10-auth.conf

disable_plaintext_auth = no #!include auth-system.conf.ext !include auth-passwdfile.conf.ext

10-logging.conf

log_path = /var/log/dovecot.log auth_verbose = no auth_debug = no verbose_ssl = no

10-mail.conf (be careful to put your vpostfix uid and gid)

mail_home = /var/mail/vhosts/%d/%n mail_location = maildir:~ mail_uid = 1000    # Your vpostfix UID mail_gid = 1000    # Your vpostfix GID mail_privileged_group = vpostfix

10-master.conf

unix_listener auth-userdb {   mode = 0600   user = vpostfix   group =  vpostfix } # Postfix smtp-auth unix_listener /var/spool/postfix/private/auth {   mode = 0666   user = vpostfix   group = vpostfix }

10-ssl.conf

ssl = no # ssl_cert = </etc/ssl/certs/dovecot.pem # ssl_key = </etc/ssl/private/dovecot.pem

Creating users

In file /etc/dovecot/conf.d/10-auth.conf we have uncommented the line !include auth-passwdfile.conf.ext. If we check that file we’ll see the following:

passdb {   driver = passwd-file   args = scheme=CRYPT username_format=%u /etc/dovecot/users }  userdb {   driver = passwd-file   args = username_format=%u /etc/dovecot/users    # Default fields that can be overridden by passwd-file   #default_fields = quota_rule=*:storage=1G    # Override fields from passwd-file   #override_fields = home=/home/virtual/%u }

This file sets the location of our users file. Currently it is in /etc/dovecot/users. If you want to keep it elsewhere you need to change those lines.

Now let’s put our users in the file. We can create the hashed password with the following command:

doveadm pw -s SHA512-CRYPT

This will ask you for password and will give you a hash of your password. You should put it in /etc/dovecot/users like this:

dragan@example.com:{SHA512-CRYPT}$6$1PrBJZz7QsUJBhVw$Ww39ihL9UtPrdk/3ORrEj7NTueTa.hpjy9ufboLoH0EjQBUwk0brezb.wIYrZ6CarfR629XGGzoLazkoev3wp1::::

So, the format is your email, followed by colon, followed by hash line given by doveadm and followed by 4 colons. Since it can be pretty tedious job to copy-paste these lines for each of your users what I usually do is to just list the names of users in the file like this:

dragan@example.com john@example2.com ...

and then use some script like this one below that will fill passwords for users:

username="$1" password="$2"  newpassword=$(doveadm pw -s SHA512-CRYPT -p "${password}") newrow=${username}':'${newpassword}'::::'  sed -i '/'$username'/c\'$newrow'' /etc/dovecot/users

With this solution your users will not be able to change their passwords. The only way to change the password is to type these commands in the console but we will see later how to make a script that will give them the ability to change their passwords through webmail client.

starting dovecot

systemctl start dovecot systemctl enable dovecot tail /var/log/dovecot.log

You should see something like this

Dec 21 08:29:13 master: Info: Dovecot v2.2.10 starting up for imap, pop3, lmtp (core dumps disabled)

Now, let’s check our e-mail. You can do that from the server using the telnet command.

For IMAP, do the following.

telnet localhost 143 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. + OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN] Dovecot ready. ? login dragan@example.com mypassword ? OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS SPECIAL-USE BINARY MOVE] Logged in ? list "" "*" + LIST (\HasNoChildren) "." INBOX ? OK List completed. ? logout + BYE Logging out ? OK Logout completed. Connection closed by foreign host.

Now that we have our postfix and dovecot up and running, the next step will be to add some security

Adding security

Installing certificates from letsencrypt.org

The first thing we’ll do is to install certificates for our server. For that we’ll use certificate authority named let’s encrypt, it’s aim is to provide free certificates through automated process. The only drawback is that it uses port 80 for its work so if you have http server active you’ll have to stop it for now, we’ll show later how it can be automated without stopping it.

In this example I presume that you do not have your http server active. Also if you server has more than one cname you will have to get certificates for all its names. You can get certificates for each of your cnames (example.com, mail.example.com,www.example.com) or you can have one certificate for all your cnames. In this example I have asked for one certificate for multiple cnames, just substitute example.com with your domain name.

cd ~ yum -y install git git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt

At this step it is important that you have port 80 open on your server, so if it is not already open, type the following:

firewall-cmd --add-port=80/tcp --permanent firewall-cmd --reload

It is also important to have your DNS properly configured for all of the domains that you will be asking certificates for.

./letsencrypt-auto certonly --standalone -d example.com -d www.example.com -d mail.example.com -d webmail.example.com

This will first install additional packages if needed and configure a few things. Follow the instructions on the screen.

Our certificates are now stored in /etc/letsencrypt/live/example.com. These certificates are valid for three months only, so before they expire you will have to request new ones periodically.

Adding security to postfix

In /etc/postfix/main.cf add the following text to the end of file. Substitute your certificate directory in smtpd_tls_key_file and smtpd_tls_cert_file

# TLS smtpd_use_tls = yes smtpd_tls_security_level = may smtpd_tls_auth_only = yes smtpd_tls_key_file = /etc/letsencrypt/live/example.com/privkey.pem smtpd_tls_cert_file = /etc/letsencrypt/live/example.com/fullchain.pem smtpd_tls_loglevel = 1 smtpd_tls_received_header = yes smtpd_tls_session_cache_timeout = 3600s tls_random_source = dev:/dev/urandom  # SASL smtpd_sasl_type = dovecot broken_sasl_auth_clients = yes smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes smtpd_sasl_security_options = noanonymous smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination smtpd_relay_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination

After that edit the /etc/postfix/master.cf and remove the comments to look like this.

submission inet n       -       n       -       -       smtpd   -o syslog_name=postfix/submission   -o smtpd_tls_security_level=encrypt   -o smtpd_sasl_auth_enable=yes   -o smtpd_reject_unlisted_recipient=no #  -o smtpd_client_restrictions=$mua_client_restrictions #  -o smtpd_helo_restrictions=$mua_helo_restrictions #  -o smtpd_sender_restrictions=$mua_sender_restrictions   -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject   -o milter_macro_daemon_name=ORIGINATING

Restart postfix after these changes and add the following firewall rules.

systemctl restart postfix firewall-cmd --add-port=587/tcp --permanent firewall-cmd --reload

If you do telnet localhost 587 and type EHLO something.com you should see that postfix replies with STARTTLS.

telnet localhost 587 Trying ::1... Connected to localhost. Escape character is '^]'. 220 www.example.com ESMTP Postfix ehlo example.com 250-www.example.com 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-STARTTLS 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN quit 221 2.0.0 Bye Connection closed by foreign host.

Now that we have postfix secured let’s do the same for dovecot

dovecot and SSL

Let’s head back to /etc/dovecot/conf.d directory

cd /etc/dovecot/conf.d

And then edit 10-auth.conf and change:

disable_plaintext_auth = yes

Then, edit 10-ssl.conf and change:

ssl = yes ssl_cert = </etc/letsencrypt/live/example.com/fullchain.pem ssl_key = </etc/letsencrypt/live/example.com/privkey.pem

Both postfix and dovecot should be able to use those certificates now, so you should restart them now.

systemctl restart postfix systemctl restart dovecot

You can test SMTP SSL/TLS connection on port 587 with the following command:

openssl s_client -starttls smtp -connect localhost:587

Then type

ehlo example.com, mail from:user@example.com.

If these steps work, you should be OK. To test SASL with postfix and dovecot, type:

doveadm auth test -a /var/spool/postfix/private/auth user@exmaple.com your_password passdb: user@example.com auth succeeded extra fields:   user=user@example.com

If you want you can open the firewall for IMAPs and POP3s.

firewall-cmd --add-port=993/tcp --permanent firewall-cmd --add-port=995/tcp --permanent firewall-cmd --reload

You can now test your e-mail server by setting it up in your favourite e-mail client. You should be able to send and receive email. However your server is currently open to all kinds of spam and not all email providers will accept your emails (google mail will probably mark your messages as spam). So, let’s now configure your spam filter.

Amavisd, Spamassassin and clamav

To test email for spam you will need spamassassin, to test it for viruses you will need clamav and you will need amavisd which is an interface between these two programs and postfix. When you install amavisd-new, clamav and spamassassin will be installed automatically.

yum install -y amavisd-new yum install -y clamav-update

First we need to configure amavisd. The configuration file is /etc/amavisd/amavisd.conf. Edit this file and make sure that these values are correct.

# @bypass_virus_checks_maps = (1);  # controls running of anti-virus code # @bypass_spam_checks_maps  = (1);  # controls running of anti-spam code # $bypass_decode_parts = 1;         # controls running of decoders&dearchivers $daemon_user  = 'amavis';     # (no default;  customary: vscan or amavis), -u $daemon_group = 'amavis';     # (no default;  customary: vscan or amavis), -g $mydomain = 'example.com';   # a convenient default for other settings (change it) $MYHOME = '/var/spool/amavisd';   # a convenient default for other settings, -H (remove the comment in front) @local_domains_maps = ( [".$mydomain","myotherdomain.net"] );  # list of all local domains. If you have multiple domains, add the here. $myhostname = 'host.example.com';  # must be a fully-qualified domain name! (remove the comment in front)    # ### http://www.clamav.net/ ['ClamAV-clamd',   \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd.sock.sock"],   qr/\bOK$/m, qr/\bFOUND$/m,   qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ], # # NOTE: run clamd under the same user as amavisd - or run it under its own # #   uid such as clamav, add user clamav to the amavis group, and then add # #   AllowSupplementaryGroups to clamd.conf; # # NOTE: match socket name (LocalSocket) in clamav.conf to the socket name in # #   this entry; when running chrooted one may prefer a socket under $MYHOME.

A WORD OF ADVICE: Clamav and memory. For clamav to work properly it needs a lot of memory. At least two 2GB of ram memory is recommended. If you have 1GB or less you will need to turn off your virus scanner. You can do it by uncommenting the following line in /etc/amavisd/amavisd.conf:

@bypass_virus_checks_maps = (1);  # controls running of anti-virus code

and commenting out the following lines in the same file:

# ### http://www.clamav.net/ #['ClamAV-clamd', #  \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd.sock.sock"], #  qr/\bOK$/m, qr/\bFOUND$/m, #  qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ], # # NOTE: run clamd under the same user as amavisd - or run it under its own # #   uid such as clamav, add user clamav to the amavis group, and then add # #   AllowSupplementaryGroups to clamd.conf; # # NOTE: match socket name (LocalSocket) in clamav.conf to the socket name in # #   this entry; when running chrooted one may prefer a socket under $MYHOME.

Now, we need to connect postfix to amavisd. Add the following lines at the end of /etc/postfix/master.cf

# Amavisd amavisfeed unix - - n - 2 lmtp         -o lmtp_data_done_timeout=1200         -o lmtp_send_xforward_command=yes 127.0.0.1:10025 inet n - n - - smtpd         -o content_filter=         -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 smtpd_restriction_classes=         -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,no_milters,no_address_mappings         -o local_header_rewrite_clients=         -o smtpd_milters=         -o local_recipient_maps=         -o relay_recipient_maps=

Edit /usr/local/etc/postfix/main.cf and add these lines at the end.

# Amavisd content_filter = amavisfeed:[127.0.0.1]:10024

Now we need to prepare everything for antivirus scanner. If you have decided to opt-out of antivirus scanning you do not need to do these lines.

Edit /etc/freshclam.conf and remove or comment out the Example line.

# Comment or remove the line below. Example

After that, edit /etc/sysconfig/freshclam and remove all 4 lines at the bottom.

### !!!!! REMOVE ME !!!!!! ### REMOVE ME: By default, the freshclam update is disabled to avoid ### REMOVE ME: network access without prior activation FRESHCLAM_DELAY=disabled-warn   # REMOVE ME

You can now update antivirus definitions with the following command:

freshclam

And after that you can update spamassassins’s signatures.

 sa-update -D

The “-D” option is to run in debug mode so you can see what’s going on. If there is an update available, spamassassin’s exit code is 0, if not the exit code is 1. In case there is an update, we’ll have to restart the spamassassin’s daemon.

Check the cron folder and make sure you see both clamav-update and sa-update files.

cd /etc/cron.d ls -l clamav-update sa-update

Now, we can start everything.

systemctl start amavisd systemctl enable amavisd systemctl start spamassassin systemctl enable spamassassin

Once you start amavisd, clamd starts as well. Finally, let’s restart postfix.

systemctl restart postfix

Check the connection between amavisd and postfix.

telnet localhost 10024

Type ehlo localhost and check the ouput. In my case it looks like this.

250-[127.0.0.1] 250-VRFY 250-PIPELINING 250-SIZE 250-ENHANCEDSTATUSCODES 250-8BITMIME 250-DSN 250 XFORWARD NAME ADDR PORT PROTO HELO IDENT SOURCE

Then do

telnet localhost 10025

Again, type ehlo localhost and check the ouput. In my case it looks like this.

250-www.example.org 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-STARTTLS 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN

Your email server should now be protected against spam and viruses. To test it go to Email Security Check and follow the instructions there. The system will send you 7 emails, all of them should be caught by amavisd.

Now that we’ve installed spam filter you have to prove that you are not a spammer. That means that every email will have to be signed using DKIM.

Proving that you are not a spammer - signing your email messages with openDKIM

yum install -y opendkim pypolicyd-spf

Now edit /etc/opendkim.conf and make sure it contains the following lines (change example.com to your domain)

Mode        sv  Domain          example.com KeyTable           refile:/etc/opendkim/KeyTable SigningTable       refile:/etc/opendkim/SigningTable ExternalIgnoreList refile:/etc/opendkim/TrustedHosts InternalHosts      refile:/etc/opendkim/TrustedHosts LogWhy yes

Also, check the number of socket port for opendkim service, you will need it later

Socket      inet:8891@localhost

Now we will create our signing keys

cd /etc/opendkim/keys mkdir example.com cd example.com opendkim-genkey -s default -d example.com chown opendkim:opendkim default.private cd /etc/opendkim/

Edit /etc/opendkim/KeyTable and add the following line:

default._domainkey.example.com example.com:default:/etc/opendkim/keys/example.com/default.private

After that, edit /etc/opendkim/SigningTable and add the following

*@example.com default._domainkey.example.com

And finally edit /etc/opendkim/TrustedHosts and add your domain name and your full host name.

example.com www.example.com

Now that we’ve configured opendkim we have to put the public key in our DNS record, so type the following

cat /etc/opendkim/keys/example.com/default.txt

You will get something like this:

default._domainkey      IN      TXT     ( "v=DKIM1; k=rsa; "           "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrIUYTOyl+IT3+CvGqZP0lgT5gPHKjVMIcm/GySA+0KP8/uK9JK2CM2Lu+i3qSCXlcVpvFMV8m5P5iRANkO3nGZXKOKPnabMvGt+Sa6INJd7LRR1OUKv+MzP9A83WVXTdwuy5nzr6bM03xin1E/3BimxS3xytzcGiFsnSL+UHn9wIDAQAB" )  ; ----- DKIM key default for example.com

Now use this to create a new DNS record for your domain. It has to be a TXT record where key is “default._domainkey” and value is the text inside the parenthesis (without the quotes).

While we are at your DNS records, you will also need to add two other TXT records, first add a nameless (@) TXT record with the following value (change YOUR_IPv4_ADDRESS AND YOUR_IPv6_ADDRESS to match your values):

v=spf1 a mx ip4:YOUR_IPv4_ADDRESS ip6:YOUR_IPv6_ADDRESS ~all

and then add the “_dmarc” TXT record with the following value:

v=DMARC1; p=none

Now let’s check if those values are visible.

yum install bind-utils dig default._domainkey.example.com TXT

You should receive the same value that you’ve entered in your DNS record:

;; ANSWER SECTION: default._domainkey.example.com. 1799 IN TXT "v=DKIM1\; k=rsa\; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCw7O7uyU/Lkn9BaQwA8obSm13EXwYf44QD+XMb2kdYbPhW4Q/NyITpR4DH+/GEYadHrN+7ezygVaG7J+Hg0u6YRL34QYN5fTVT/aon2X0GvJeT1xNsggYy50pHgTkVzYGhUyhhepHhxt2Yc12GVeibZj505MnvlvuMeU9Xs3iRiQIDAQAB"

We now have to modify postfix configuration and to tell it to use opendkim

Add the following to /etc/postfix/master.cf

policy-spf  unix  -       n       n       -       -       spawn      user=nobody argv=/usr/libexec/postfix/policyd-spf

Change the following line in /etc/postfix/main.cf to

smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, check_policy_service, unix:private/policy-spf

And also add the following lines to /etc/postfix/main.cf. Port values for smtpd_milters and non_smtpd_milters should be the same as in “Socket” in /etc/opendkim.conf

policy-spf_time_limit = 3600s  # DKIM milter_default_action = accept milter_protocol = 6 smtpd_milters = inet:localhost:8891 non_smtpd_milters = inet:localhost:8891

Now let’s start opendkim and restart postfix.

systemctl start opendkim systemctl enable opendkim systemctl restart postfix

And that should be it.

You can check if dkim signature is added if you check your maillog and send an email.

tail -f /var/log/maillog

You should see a line similar to this

Dec 28 18:11:25 example opendkim[1169]: 03DD041429: DKIM-Signature field added (s=default, d=example.com)

If you send an email to your other email address and check the source you should see the DKIM signature in the header, it will look like this:

... DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com;   s=default; t=1450868845;   bh=1i0m94nmanDDd6Gu/03Obw9meKedLOruAZb2JFtSyfY=;   h=To:From:Subject:Date:From;   b=TvscdTJaWJps1khxQ5dqw/TWLkZbSWhZqPa0JnnuG076Xom2KJDFzQSBLz7XVylKS    jVtdw7q23QAslgzcDHLPT/099f7dB4x4gPwABuN6W0Km7B8EGJYI/0QCmp359PfalT    nr8Pv5EoC1pbRS+oaMGDamRVF8zHfHpp5MO8OYRM= ...

You can also check your e-mail score on www.mail-tester.com. If everything went well you should receive a perfect score of 10/10. Your e-mail server is now fully configured and ready to send and receive e-mail. If you wish you can also install webmail client but that step is optional.

Installing webmail client

There are a lot of open source webmail clients, which one you choose to install is up to you. If you want simplicity you can choose squirrelmail, or if you want something modern you can choose one of many web 2.0 clients. In this example we will install Rainloopwebmail client.

We shall set up our webmail server to be accessible on webmail.example.com so be sure to set up the appropriate DNS record.

yum -y install httpd php unzip mod_ssl cd /var/www/html mkdir webmail wget http://repository.rainloop.net/v2/webmail/rainloop-community-latest.zip unzip rainloop-community-latest.zip rm rainloop-community-latest.zip cd .. chown -R apache.apache webmail  firewall-cmd --add-port=443/tcp --permanent firewall-cmd --reload

Now edit the file /etc/httpd/conf.d/ssl.conf. Find the section and add the following lines:

<VirtualHost _default_:443>  # General setup for the virtual host, inherited from global configuration DocumentRoot "/var/www/html/webmail" ServerName webmail.example.com:443

In the same section change the following lines:

#   Server Certificate: # Point SSLCertificateFile at a PEM encoded certificate.  If # the certificate is encrypted, then you will be prompted for a # pass phrase.  Note that a kill -HUP will prompt again.  A new # certificate can be generated using the genkey(1) command. SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem  #   Server Private Key: #   If the key is not combined with the certificate, use this #   directive to point at the key file.  Keep in mind that if #   you've both a RSA and a DSA private key you can configure #   both in parallel (to also allow the use of DSA ciphers, etc.) SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem  #   Server Certificate Chain: #   Point SSLCertificateChainFile at a file containing the #   concatenation of PEM encoded CA certificates which form the #   certificate chain for the server certificate. Alternatively #   the referenced file can be the same as SSLCertificateFile #   when the CA certificates are directly appended to the server #   certificate for convinience. SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem

We have now installed our certificates into http server.

now start the httpd server

systemctl enable httpd systemctl start httpd

You can now try to access your webmail server on https://webmail.example.com. It should give you the login screen. First you need to configure it, so open the admin page on https://webmail.example.com/?admin. It will open a login page to the admin panel. Default login for is “admin”, password “12345” so be sure to change it immediately after login. Setting up rainloop webmail is pretty straightforward, click on “domains” and there click on “add domain” button. There you can set up access to your mail server.

Type your domain name there like shown on the picture and press “test” button. If everything is ok, click to “add” and then “close”. And there it is, your webmail server is set up.

Bonus track: giving users a way to change their passwords.

If you remember when we were creating passwords for our e-mail users we mentioned that the only way to change the password would be to log in to server terminal and to change it by hand. Now that we have a working webmail we can use it to enable users to change their own passwords. For that we will use rainloop’s own change password example.

you should first download rainloop’s source

cd ~ git clone https://github.com/RainLoop/rainloop-webmail.git

For rainloop to be able to change password we need to create a script and it needs to be able to access the file. Therefore we will move our password file somewhere where rainloop can access it. We shall put it in /var/www/dovecot directory and of course we have to tell dovecot the new location of the file.

mkdir /var/www/dovecot mv /etc/dovecot/users /var/www/dovecot

Now that we have moved the file we need to change the configuration file of the dovecot. Edit the file /etc/dovecot/conf.d/10-auth.conf and change value /etc/dovecot/users it to /var/www/dovecot/users like this:

passdb {   driver = passwd-file   args = scheme=CRYPT username_format=%u /var/www/dovecot/users }  userdb {   driver = passwd-file   args = username_format=%u /var/www/dovecot/users }

After that we need to restart dovecot to reload the changes

systemctl restart dovecot

We will now create a script that will change password on rainloop’s demand.

cd /var/www/dovecot touch pwdchange.sh chmod +c pwdchange.sh chown -R apache.apache /var/www/dovecot

Now edit the pwdchange.sh and put the following:

username="$1" password="$2"  newpassword=$(doveadm pw -s SHA512-CRYPT -p "${password}") newrow=${username}':'${newpassword}'::::'  sed -i '/'$username'/c\'$newrow'' /var/www/dovecot/users

This script takes two arguments, the first one is username whose password is to be changed and the second one is the new password. You can call it like this:

./pwdchange.sh dragan@example.com mynewpassword

Or if you want a passphrase you can put it in quotation marks:

./pwdchange.sh dragan@example.com "my new passphrase"

Now that we have moved users file and the script is ready we can create rainloop plugin that will use it.

We will now use rainloop’s change password example plugin and to copy it to our rainloop installation.

cd /var/www/html/webmail/data/_data_/_default_/plugins/ mkdir change-password-mydomain cd ~/rainloop-webmail/plugins/change-password-example cp * /var/www/html/webmail/data/_data_/_default_/plugins/change-password-mydomain cd /var/www/html/webmail/data/_data_/_default_/plugins chown -R apache.apache change-password-mydomain cd change-password-mydomain

Now edit the file ChangePasswordExampleDriver.php, locate the function called ChangePassword and under the //TODO add the following line

exec('/var/www/dovecot/pwdchange.sh '.$oAccount->Email().' "'.$sNewPassword.'"');

And there it is. Now all you have to do is login to webmail with Admin password, go to settings, click on enable plugins and check on change-password-mydomain plugin and that’s it. Your users will now be able to change their passwords from their webmail.