Difference between revisions of "Configure mail server"

From Organic Design wiki
m (Exim broken after apt-get upgrade)
(Main configuration for multiple domains: IGNORE_SMTP_LINE_LENGTH_LIMIT)
 
(168 intermediate revisions by 3 users not shown)
Line 3: Line 3:
 
  | status = in use
 
  | status = in use
 
}}
 
}}
 +
 +
 +
Mail Transfer Agents (MTA) connect to one another on port 25 to route mail to its destination. Other ports such as 465 or 587 are also used, but these are used exclusively for connections from Mail User Agents (MUA) to MTAs. All ports are the same as far as the listening MTA is concerned, any of them can use plain of encrypted connection which is negotiated as part of the SMTP protocol's handshake phase.
 +
 +
In our configuration described here, the MTA listens on ports 25 and 587 (encrypted) for incoming emails and decides whether to relay them somewhere else for delivery, or to deliver them locally. In our configuration we have no relaying at all since all recipient accounts we're handling are accounts on the same host as the MTA. So if an incoming email is destined for a local recipient, we pass it through a spam filter and then deliver it locally without any other restrictions.
 +
 +
But the local clients (MUAs) need to be able to use the MTA for sending mails from their local machines out to other external recipients. To cater for this situation, the MTA is configured so that it will allow delivery of mail to remote recipients as well, but only if the connection is encrypted and uses authentication with credentials that match one of the local UNIX accounts.
 +
 +
If authentication is used, the spam filter is bypassed even if deliver is to a local client, which means that the MTA can also be used as a "smart host" by other MTAs that are unable to send traffic on port 25, which is often then case when hosting on a DSL connection for example. In this case, the firewalled MTA can be configured to connect to an MTA configured as described here on port 587 with authentication.
  
 
== Exim4 (MTA & MDA) ==
 
== Exim4 (MTA & MDA) ==
 
[[w:Exim|Exim4]] is the default mail transfer and delivery agent for Debian, but it's only the light version which is insufficient for running a mail server, so first we need to install the ''exim4-daemon-heavy'' package which will replace the light version, and a few other packages we'll be needing.
 
[[w:Exim|Exim4]] is the default mail transfer and delivery agent for Debian, but it's only the light version which is insufficient for running a mail server, so first we need to install the ''exim4-daemon-heavy'' package which will replace the light version, and a few other packages we'll be needing.
{{code|<bash>apt-get install exim4-daemon-heavy, dovecot-common, dovecot-imapd, spamassassin, spamc</bash>}}
+
<source lang="bash">
 +
sudo apt-get install exim4-daemon-heavy dovecot-common dovecot-imapd spamassassin spamc spf-tools-perl
 +
</source>
  
  
Line 12: Line 23:
  
 
First, run through the standard Exim4 configuration script, say '''no splitting configuration files''', set ''general mail configuration'' to '''internet''', use '''MailDir''' delivery method, and keep any non-self-explanatory settings as default.
 
First, run through the standard Exim4 configuration script, say '''no splitting configuration files''', set ''general mail configuration'' to '''internet''', use '''MailDir''' delivery method, and keep any non-self-explanatory settings as default.
{{code|<pre>dpkg-reconfigure exim4-config</pre>}}
+
<source>
 +
dpkg-reconfigure exim4-config
 +
</source>
 +
 
  
=== Mail name ===
+
When changing configuration in Exim4 there are three different levels:
 +
*Making changes to '''update-exim4.conf.conf''' requires running '''update-exim4.conf''' and '''service exim4 restart'''.
 +
*Making changes to '''exim4.conf.template''' requires only '''service exim4 restart'''.
 +
*Making changes to '''virtual.users''', '''virtual.domains''' or any users '''.forward''' file requires no other action.
 +
 
 +
=== Mail name, hostname, rDNS and blacklists ===
 
It's important that the domain part of the ''MAIL FROM'' field in outgoing messages has a proper domain with a valid reverse lookup, otherwise some SMTP servers will reject them. To ensure this, check that the domain name specified in the first line of '''/etc/mailname''' is the correct one, and set it if not. Alternatively you can set the ''primary_host'' value in the exim configuration to the proper domain, this is best set at the top of the configuration file, and you may need to comment out other occurrences further down.
 
It's important that the domain part of the ''MAIL FROM'' field in outgoing messages has a proper domain with a valid reverse lookup, otherwise some SMTP servers will reject them. To ensure this, check that the domain name specified in the first line of '''/etc/mailname''' is the correct one, and set it if not. Alternatively you can set the ''primary_host'' value in the exim configuration to the proper domain, this is best set at the top of the configuration file, and you may need to comment out other occurrences further down.
 +
 +
The hostname should be set to the domain with the ''hostname -b'' command and can also be set in ''/etc/hostname'' and added as a localhost name in ''/etc/hosts''.
 +
 +
The reverse DNS should be set up in your server host's configuration (e.g. this is done in the ''Remote Access'' tab of the ''Linode'' configuration), the server host may need to do this for you if their client app doesn't support it. Note that you should have reverse DNS set up for your IPv6 address as well, and if your ISP can't provide this then you'll need to add the ''disable_ipv6'' directive to your configuration (see troubleshooting section below).
 +
 +
You may find that your server's IP is listed in various spam or abuse blacklists, a good service to check all the common blacklists in one go at is [https://mxtoolbox.com/ MX Toolbox]. They also offer a free notification service, or a more advanced paid service.
  
 
=== Maildir delivery method ===
 
=== Maildir delivery method ===
This should have been set in the package configuration above, but if not it can be set manually as follows. The primary advantage of maildirs is that multiple applications can access the same Maildir simultaneously without requiring any kind of locking whatsoever, but aside from this, Maildir format is more efficient than ''mbox'', for more information see [http://www.courier-mta.org/mbox-vs-maildir/ mbox-vs-maildir]. Check the ''/etc/exim4/update-exim4.conf.conf'' and append '''dc_localdelivery='maildir_home' '''if it doesn't already exist. Note that if you change ''update-exim4.conf.conf'', then you'll need to also run '''update-exim4.conf'''.
+
This should have been set in the package configuration above, but if not it can be set manually as follows. The primary advantage of maildirs is that multiple applications can access the same Maildir simultaneously without requiring any kind of locking whatsoever, but aside from this, Maildir format is more efficient than ''mbox'', for more information see [http://www.courier-mta.org/mbox-vs-maildir/ mbox-vs-maildir]. Check the ''/etc/exim4/update-exim4.conf.conf'' and append '''dc_localdelivery='maildir_home' '''if it doesn't already exist.
  
 
=== Main configuration for multiple domains ===
 
=== Main configuration for multiple domains ===
 
You may want to handle mail for a few domains on one server, in which case some generic usernames like "accounts" will conflict with the local user names, or with the same names used by other domains. This is based on [http://www.wlug.org.nz/BlairHarrison Blair Harrison]'s method from [http://www.debian-administration.org/articles/140 this Waikato LUG article] which adds virtual domain support to Exim4. This allows any incoming email address to be mapped to any other internal user mailbox, or external email address.
 
You may want to handle mail for a few domains on one server, in which case some generic usernames like "accounts" will conflict with the local user names, or with the same names used by other domains. This is based on [http://www.wlug.org.nz/BlairHarrison Blair Harrison]'s method from [http://www.debian-administration.org/articles/140 this Waikato LUG article] which adds virtual domain support to Exim4. This allows any incoming email address to be mapped to any other internal user mailbox, or external email address.
  
The main configuration changes to be made are  in ''/etc/exim4/exim4.conf.template'' (or in ''/etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs'' if you're using split-file config).
+
The main configuration changes to be made are  in ''/etc/exim4/exim4.conf.template'' (or in ''/etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs'' if you're using split-file config). Here's a summary of the main settings we use at the top of this file, these are explained in detail below along with their associated specific configurations throughout the file.
 +
<source>
 +
primary_hostname = organicdesign.co.nz
 +
message_body_newlines = true
 +
message_body_visible = 500000
 +
daemon_smtp_ports = 25 : 587 : 2525
 +
system_filter = /var/www/tools/exim-copy-to-sent
 +
system_filter_directory_transport = copy_to_sent
 +
system_filter_pipe_transport = copy_to_sent_pipe
 +
helo_allow_chars = _
 +
MAIN_TLS_ENABLE = true
 +
IGNORE_SMTP_LINE_LENGTH_LIMIT = true
 +
CHECK_RCPT_SPF = true
 +
</source>
  
 
Next, adjust the ''local_domains'' setting to the following:
 
Next, adjust the ''local_domains'' setting to the following:
{{code|<pre>domainlist local_domains = @ : @[] : localhost : partial-lsearch;/etc/exim4/virtual.domains</pre>}}
+
<source>
 +
domainlist local_domains = @ : @[] : localhost : partial-lsearch;/etc/exim4/virtual.domains
 +
</source>
  
  
 
Next go to the section starting with "real_local" and add a new section as follows (if in split configuration, this section should be added in a new file called ''/etc/exim4/conf.d/router/300_exim4-config_virtual'')
 
Next go to the section starting with "real_local" and add a new section as follows (if in split configuration, this section should be added in a new file called ''/etc/exim4/conf.d/router/300_exim4-config_virtual'')
{{code|<pre>
+
<source>
 
virtual:
 
virtual:
 
driver = redirect
 
driver = redirect
Line 38: Line 78:
 
domains = partial-lsearch;/etc/exim4/virtual.domains
 
domains = partial-lsearch;/etc/exim4/virtual.domains
 
retry_use_local_part
 
retry_use_local_part
</pre>}}
+
</source>
  
  
 
Now you will need file called ''/etc/exim4/virtual.domains'' that is simply a list of all the domains for which you accept mail in the following format:
 
Now you will need file called ''/etc/exim4/virtual.domains'' that is simply a list of all the domains for which you accept mail in the following format:
{{code|<pre>
+
<source>
 
example.com
 
example.com
 
example.net
 
example.net
</pre>}}
+
</source>
  
  
 
Don't forget to restart the mail server,
 
Don't forget to restart the mail server,
{{code|<pre>
+
<source>
 
/etc/init.d/exim4 restart
 
/etc/init.d/exim4 restart
</pre>}}
+
</source>
  
 
=== Setting up mail users ===
 
=== Setting up mail users ===
 
To add or modify the mail users, edit the ''/etc/exim4/virtual.users'' file. Following are some examples to highlight some of the most popular scenarios:
 
To add or modify the mail users, edit the ''/etc/exim4/virtual.users'' file. Following are some examples to highlight some of the most popular scenarios:
{{code|<pre>
+
<source>
regularuser@example.com    : localuser@localhost
+
regularuser@example.com    : localuser@localhost
forwardinguser@example.com  : someuser@example.org
+
forwardinguser@example.com  : someuser@example.org
foo@example.com            : :fail: Foo no longer lives here.
+
foo@example.com            : :fail: Foo no longer lives here.
bar@example.com            : :blackhole:
+
bar@example.com            : :blackhole:
*@example.com              : catchall1@localhost
+
*@example.com              : catchall1@localhost
  
regularuser@example.net    : localuser2@localhost
+
regularuser@example.net    : localuser2@localhost
forwardinguser@example.net  : someuser2@example.org
+
forwardinguser@example.net  : someuser2@example.org
*@example.net              : catchall2@localhost
+
*@example.net              : catchall2@localhost
</pre>}}
+
</source>
*'''Note:''' There is no need for Exim to be restarted after changing the ''virtual.users'' file.
+
*'''Note:''' There is no need for Exim to be restarted after changing the ''virtual.users'' or ''virtual.domains'' files.
  
 
=== Testing delivery ===
 
=== Testing delivery ===
 
Before moving on to SMTP or IMAP configuration, you should test that email sent to your local users/domains are being delivered properly. The mails will be delivered to the users home directories in ''~/Maildir/cur/''. You can ''cat'' the files to see the textual content of the messages stored there. You can send an email from the command line:
 
Before moving on to SMTP or IMAP configuration, you should test that email sent to your local users/domains are being delivered properly. The mails will be delivered to the users home directories in ''~/Maildir/cur/''. You can ''cat'' the files to see the textual content of the messages stored there. You can send an email from the command line:
{{code|<pre>
+
<source>
 
/path/to/exim -v 'user@domain'
 
/path/to/exim -v 'user@domain'
 
message here
 
message here
 
^D ( control D )
 
^D ( control D )
</pre>}}
+
</source>
  
  
 
Another important thing to check before considering the system ready for production use is to send a test message to another mail server that you have access to the logs on. This way you can see exactly what other mail servers are seeing and ensure that everything matches up as it should. Here's an example of a remote server's Exim log entry showing a message received from the Organic Design server.
 
Another important thing to check before considering the system ready for production use is to send a test message to another mail server that you have access to the logs on. This way you can see exactly what other mail servers are seeing and ensure that everything matches up as it should. Here's an example of a remote server's Exim log entry showing a message received from the Organic Design server.
{{code|<pre>
+
<source>
 
2013-04-25 11:37:49 1UVPB7-0008Q2-J8 <= aran@organicdesign.co.nz H=organicdesign.co.nz [37.17.226.20]
 
2013-04-25 11:37:49 1UVPB7-0008Q2-J8 <= aran@organicdesign.co.nz H=organicdesign.co.nz [37.17.226.20]
 
     P=esmtps X=TLS1.0:RSA_AES_256_CBC_SHA1:32 S=944 id=51795B7E.2020802@organicdesign.co.nz
 
     P=esmtps X=TLS1.0:RSA_AES_256_CBC_SHA1:32 S=944 id=51795B7E.2020802@organicdesign.co.nz
 
2013-04-25 11:37:49 1UVPB7-0008Q2-J8 => nad <nad@foo.com> R=local_user T=maildir_home
 
2013-04-25 11:37:49 1UVPB7-0008Q2-J8 => nad <nad@foo.com> R=local_user T=maildir_home
 
2013-04-25 11:37:49 1UVPB7-0008Q2-J8 Completed
 
2013-04-25 11:37:49 1UVPB7-0008Q2-J8 Completed
</pre>}}
+
</source>
 
The most important part to look at here is the '''H''' value. The IP in the square brackets is the real IP address that the message has been received from, and the domain directly following the ''H'' is the name found after doing a reverse-DNS lookup on the IP. If the name is surrounded by round brackets it means that no reverse-DNS entry was found, and the name that the server is publishing in response to an SMTP ''HELO'' or ''EHLO'' command (exim's ''primary_domain'' value) is being used instead. If there's both a reverse_DNS name and also a bracketed name, then this means the reverse-DNS name is not the same as the name returned from the ''HELO'' command. {{h|It's very important that there be a reverse-DNS entry and that it exactly match the ''HELO'' domain because otherwise many mail servers will reject the message as spam}}. More information about the Exim log format of received messages can be found [http://www.exim.org/exim-html-current/doc/html/spec_html/ch-log_files.html#SECID251 here] in the Exim documentation.
 
The most important part to look at here is the '''H''' value. The IP in the square brackets is the real IP address that the message has been received from, and the domain directly following the ''H'' is the name found after doing a reverse-DNS lookup on the IP. If the name is surrounded by round brackets it means that no reverse-DNS entry was found, and the name that the server is publishing in response to an SMTP ''HELO'' or ''EHLO'' command (exim's ''primary_domain'' value) is being used instead. If there's both a reverse_DNS name and also a bracketed name, then this means the reverse-DNS name is not the same as the name returned from the ''HELO'' command. {{h|It's very important that there be a reverse-DNS entry and that it exactly match the ''HELO'' domain because otherwise many mail servers will reject the message as spam}}. More information about the Exim log format of received messages can be found [http://www.exim.org/exim-html-current/doc/html/spec_html/ch-log_files.html#SECID251 here] in the Exim documentation.
 +
 +
To test what you server's domain is in the ''ELHO'' response connect via SMTP using '''telnet DOMAIN 25''' then type ''ELHO''. If you're connecting from a consumer connection you may need to use the 2525 port since many of them block 25 to prevent spammers.
  
 
=== Setting up a secure SMTP server with authentication ===
 
=== Setting up a secure SMTP server with authentication ===
 
Since many people in the organisation may be working with laptops in the field, it's useful for the server to run it's own SMTP server so that settings don't need to be changed when moving from one ISP to another for internet access. The SMTP server will need to use authentication and encryption so that it can't be used for spamming.
 
Since many people in the organisation may be working with laptops in the field, it's useful for the server to run it's own SMTP server so that settings don't need to be changed when moving from one ISP to another for internet access. The SMTP server will need to use authentication and encryption so that it can't be used for spamming.
  
Setting up a secure SMTP server with authentication is quite simple using Exim4's [http://www.exim-new-users.co.uk/content/view/60/1/ plaintext authenticator] with [http://www.exim-new-users.co.uk/content/view/64/1/ TLS encryption]. Here we'll enable SMTP for all clients and we'll require authentication in the form of a single user/password used by all the clients (we could upgrade this later to use the client's IMAP login, [http://www.debian-administration.org/articles/280 this], [http://www.wlug.org.nz/EximSmtpAuth this] and [http://jeremy.zawodny.com/blog/archives/000453.html this] may help with those changes).
+
Setting up a secure SMTP server with authentication is quite simple using Exim4's [http://www.exim-new-users.co.uk/content/view/60/1/ plaintext authenticator] with [http://www.exim-new-users.co.uk/content/view/64/1/ TLS encryption].
  
 
==== SMTP Certificates ====
 
==== SMTP Certificates ====
{{note|we prefer to use a wildcard domain for the ''common name'' such as '''*.organicdesign.co.nz''' so that it's possible for different sub-domains to access the service but get handled differently.}}
+
First add the following to the first section of ''/etc/exim4/exim4.conf.template'' to define the ports we'll listen on. Using port 587 is not only important for accepting encrypted connections, but also because many ISP's restrict outgoing traffic on port 25 to their own SMTP server only. All other TLS options are added under the '''main/03_exim4-config_tlsoptions''' section.
Generate your certificates as follows:
+
<source>
{{code|<pre>/usr/share/doc/exim4-base/examples/exim-gencert</pre>}}
+
MAIN_TLS_ENABLE = true
 +
daemon_smtp_ports = 25 : 587
 +
</source>
  
 +
'''Self-signed:''' Using a self-signed certificate, we prefer to use a wildcard domain for the ''common name'' such as '''*.organicdesign.co.nz''' so that it's possible for different sub-domains to access the service but get handled differently, then generate the certificate with the '''/usr/share/doc/exim4-base/examples/exim-gencert''' command.
  
Add the following to the first section of ''/etc/exim4/exim4.conf.template''. The second directive adds a second SMTP port since many ISP's restrict outgoing traffic on port 25 to their own SMTP server only.
+
'''LetsEncrypt:''' A better way is to use your ''LetsEncrypt'' certificate instead of a self-signed one by setting the ''MAIN_TLS_CERTIFICATE'' to the ''fullchain.pem'' file and ''MAIN_TLS_PRIVATEKEY'' to the ''privkey.pem'' file, and ensuring the files are both accessible by Exim ('''Note:''' the certs are symlinks so the target needs to be readable by Exim, and also all directories in the path need to be executable by Exim to be allowed to follow symlinks). For example:
{{code|<pre>
+
<source>
MAIN_TLS_ENABLE = true
+
MAIN_TLS_CERTIFICATE = /etc/organicdesign/ssl/fullchain.pem
daemon_smtp_ports = 25 : 2525
+
MAIN_TLS_PRIVATEKEY = /etc/organicdesign/ssl/privkey.pem
</pre>}}
+
</source>
 +
'''Note:''' these certs must be readable by the ''Debian-exim'' user, we've found that we need to ''chgrp'' the private key to ''Debian-exim'' and set its mode to ''640'', and that symlinks cab cause trouble. We gave a ''deploy-hook'' for our ''LetsEncrypt'' certificate renewal that puts a copy of the files into ''/etc/organicdesign'' instead of using symlinks.
  
==== Using a global inline SMTP user and password ====
+
==== Cipher and protocol selection ====
Next find the section containing the string "plain_server" and add a similar entry as follows, but replace the ''USER'' and ''PASSWORD'' with the global authentication you want all clients to use.
+
We should also restrict the protocols and ciphers to mitigate POODLE and BEAST attacks etc, the available options are shown in the Exim manual [https://www.exim.org/exim-html-current/doc/html/spec_html/ch-encrypted_smtp_connections_using_tlsssl.html here] and [https://www.gnutls.org/manual/html_node/Encryption-algorithms-used-in-the-record-layer.html#tab_003aciphers here] and a good guide [https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-Hardening_TLS_Configuration#sec-Choosing_Algorithms_to_Enable here], and we use the following selection:
{{code|<pre>
+
<source>
plain_server:
+
tls_require_ciphers = SECURE256:+SECURE128:-VERS-TLS-ALL:+VERS-TLS1.2:-RSA:-DHE-DSS:-CAMELLIA-128-CBC:-CAMELLIA-256-CBC
driver = plaintext
+
</source>
public_name = PLAIN
+
*'''Note:''' ''tls_require_ciphers'' replaces ''gnutls_require_kx'', ''gnutls_require_mac'' and ''gnutls_require_protocols'' which are all obsolete now.
server_condition = "${if and {{eq{$auth2}{USER}}{eq{$auth3}{PASSWORD}}}{1}{0}}"
 
server_set_id = $auth2
 
server_prompts = :
 
</pre>}}
 
  
 
==== Using the native Linux user accounts and passwords ====
 
==== Using the native Linux user accounts and passwords ====
 
To use the native Linux accounts for SMTP authentication, the ''/etc/shadow'' file must be made accessible to the ''Debian-exim'' group:
 
To use the native Linux accounts for SMTP authentication, the ''/etc/shadow'' file must be made accessible to the ''Debian-exim'' group:
{{code|<pre>
+
<source>
 
chgrp Debian-exim /etc/shadow
 
chgrp Debian-exim /etc/shadow
 
chmod g+r /etc/shadow
 
chmod g+r /etc/shadow
</pre>}}
+
</source>
  
 +
Next find the authenticators section containing the string "PLAIN" and add a similar entry as follows, removing any other entry with the public name of ''PLAIN'' or ''LOGIN'':
 +
<source>
 +
plain:
 +
driver = plaintext
 +
public_name = PLAIN
 +
server_condition = "${if crypteq{$auth3}{${extract{1}{:}{${lookup{$auth2}lsearch{/etc/shadow}{$value}}}}}{1}{0}}"
 +
server_set_id = $auth2
 +
server_prompts = :
 +
server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}
 +
</source>
  
Next use the same ''plain_server'' as shown above, but change the ''server_condition'' to the following:
+
If you'd like to use a global username and password for your SMTP authentication instead of the native user accounts, use the following ''server_condition'' instead replacing the ''USER'' and ''PASSWORD'' with the global authentication you want all clients to use.
{{code|<pre>
+
<source>
server_condition = "${if crypteq{$auth3}{${extract{1}{:}{${lookup{$auth2}lsearch{/etc/shadow}{$value}}}}}{1}{0}}"
+
server_condition = "${if and {{eq{$auth2}{{!USER!}}}{eq{$auth3}{{!PASSWORD!}}}}{1}{0}}"
</pre>}}
+
</source>
  
 
==== Test sending mail to an external domain ====
 
==== Test sending mail to an external domain ====
 
Restart the exim4 server and the server should be ready to accept SMTP requests!
 
Restart the exim4 server and the server should be ready to accept SMTP requests!
  
To set up the clients use the user name and password defined above, '''smtp.organicdesign.co.nz''' for the server, '''2525''' for the port and '''TLS''' for security.
+
To set up the clients use the user name and password defined above, '''smtp.organicdesign.co.nz''' for the server, '''587''' for the port and '''TLS''' for security.
  
 
To test that the SMTP is working and is not accessible to spammers, testing sending out emails to non-local recipients (as delivery to local domains is unrestricted). Try sending without authentication to ensure that it fails. Also test sending to a local domain from another SMTP server (such as your ISP's one) to ensure that normal delivery is working correctly.
 
To test that the SMTP is working and is not accessible to spammers, testing sending out emails to non-local recipients (as delivery to local domains is unrestricted). Try sending without authentication to ensure that it fails. Also test sending to a local domain from another SMTP server (such as your ISP's one) to ensure that normal delivery is working correctly.
 +
 +
=== Copying emails into the Sent folder ===
 +
Many Thunderbird users have been having trouble with the "copying to sent folder" process freezing or being extremely slow. Rather than have every mail be effectively sent twice, once too the recipient and again to the sent folder, it's a lot more efficient to have the server make a copy in the user's Sent folder instead.
 +
 +
'''Web-mail note: ''' If this is set up, you should also configure your web-mail to not place copies in the Sent folder. Our method of doing this is shown below in the RoundCube web-mail section.
 +
 +
To do this we need to set up an Exim [http://www.exim.org/exim-html-current/doc/html/spec_html/ch-systemwide_message_filtering.html system filter] which begins by adding the following lines to the beginning of the configuration where the port and other OD settings are put.
 +
<source>
 +
system_filter = /var/www/tools/exim-copy-to-sent
 +
system_filter_directory_transport = copy_to_sent
 +
system_filter_pipe_transport = copy_to_sent_pipe
 +
</source>
 +
 +
 +
Next we need to add the two transport that were specified as the system filter transport above. I put this after '''end&nbsp;transport/30_exim4-config_mail_spool''' in the configuration and it's defined as follows:
 +
<source>
 +
copy_to_sent:
 +
    driver = appendfile
 +
    delivery_date_add
 +
    envelope_to_add
 +
    return_path_add
 +
    group = Debian-exim
 +
    user = ${if match {${local_part:${lookup{$sender_address_local_part@$sender_address_domain}lsearch*@{/etc/exim4/virtual.users}}}}{(.+)}{$1}{Debian-exim}}
 +
    mode = 0660
 +
    maildir_format
 +
    directory = /home/${local_part:${lookup{$sender_address_local_part@$sender_address_domain}lsearch*@{/etc/exim4/virtual.users}}}/Maildir/.Sent/
 +
    create_directory
 +
 +
copy_to_sent_pipe:
 +
    driver = pipe
 +
    user = Debian-exim
 +
    group = Debian-exim
 +
</source>
 +
 +
 +
The filter itself is in our tools repo [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2Fexim-copy-to-sent here] and so will be available in the file system at '''/var/www/tools/exim-copy-to-sent''', it has the following content:
 +
<source>
 +
if
 +
    $sender_address_local_part is not "root"
 +
and
 +
    "${if def:h_X-Spam-Status {def}{undef}}" is "undef"
 +
and
 +
    "${if match {${lookup{$sender_address}lsearch{/etc/exim4/virtual.users}}}{@localhost}{yes}{}}" is "yes"
 +
then
 +
    unseen save $home/Maildir/.Sent/
 +
    unseen pipe "/var/www/tools/copy-to-sent.pl \"$recipients\""
 +
endif
 +
</source>
 +
This filter saves the messages to the user's Sent folder if three conditions apply:
 +
*The message is not from the server itself
 +
*The spam headers have not been added yet (the filter will run both before and after they're added)
 +
*The message is from a local account (i.e. not an incoming message)
 +
 +
One difficulty with using a system filter is that the BCC header is stripped by the time the message arrives at the system filter, but it's quite important for this header to be present in the Sent folder so that users can see who they sent the message to (also it's very useful for sending to a private list and then using the "Edit as new" feature to easily send another message to the same list). The header can be added back again using the ''$recipients'' variable which contains the entire list of recipients from all headers including BCC.
 +
 +
Another issue is that the messages are created as unread and Exim doesn't have any commands for changing the unread status as that's really a job for the mail client, not the transfer agent.
 +
 +
To get around these problems I've created the [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2Fcopy-to-sent.pl copy-to-sent.pl] script that detects which file in the user's ''Sent/new'' Maildir folder is the one just added by checking the headers of all new messages and checking which has the new message ID which Exim sets in the ''MESSAGE_ID'' environment variable. The script then adds a ''Bcc'' header to the new message containing all the recipients that are not present in the ''To'' or ''Cc'' headers, and marks the message as read.
 +
 +
The ''system_filter_pipe_transport'' value in the Exim configuration needs to be set and an appropriate transport created for it before ''pipe'' commands are allowed to execute in system filters.
 +
 +
There is still one issue with this method currently which is that the complex expansion of the sender address into a local user has to be done twice because I can't figure out how to cache the result for later use. Perhaps the ''directory'' directive is not even needed and something else can be used instead.
  
 
=== LAN based SMTP servers ===
 
=== LAN based SMTP servers ===
Line 144: Line 258:
 
Our solution is to set clients up using '''smtp.organicdesign.co.nz''' as their outgoing server using a company-wide user which is set up on the OD server as a zero-privilege user. This works for the external situation, but to allow local mail to be routed directly independently of Internet requires that the local DNS server be set up with an ''organicdesign.co.nz'' zone to be authoritative for just the ''smtp'' sub-domain but forward everything else on. This will make the local ''exim'' instance the first stop for all internal mail sending requests, but will deliver according to the usual rules for all non-local recipients.
 
Our solution is to set clients up using '''smtp.organicdesign.co.nz''' as their outgoing server using a company-wide user which is set up on the OD server as a zero-privilege user. This works for the external situation, but to allow local mail to be routed directly independently of Internet requires that the local DNS server be set up with an ''organicdesign.co.nz'' zone to be authoritative for just the ''smtp'' sub-domain but forward everything else on. This will make the local ''exim'' instance the first stop for all internal mail sending requests, but will deliver according to the usual rules for all non-local recipients.
  
== DoveCot (IMAP & POP3) ==
+
== DoveCot IMAP server ==
The [[organicdesign-server]] [[package]] installs [http://www.dovecot.org DoveCot], an [[w:IMAP|IMAP]] and [[w:POP3|POP3]] server. DoveCot responds to mail client requests by retrieving the mails from file and returning them to the client. It does not deal with the receiving the mails or storing them to disk.
+
We use [http://www.dovecot.org DoveCot] for our [[w:IMAP|IMAP]]. DoveCot responds to mail client requests by retrieving the mails from file and returning them to the client. It does not deal with the receiving the mails or storing them to disk.
  
 
We go with a configuration which is as close to default as possible and gives us both POP3 and IMAP services which both work with either TLS or SSL but not plain text. The users are by default the native linux users and passwords on the system.
 
We go with a configuration which is as close to default as possible and gives us both POP3 and IMAP services which both work with either TLS or SSL but not plain text. The users are by default the native linux users and passwords on the system.
  
Our entire ''/etc/dovecot/dovecot.conf'' configuration file can be replaced with the following extremely minimal one since nearly everything we use is default (remove the POP3 entries if only IMAP access is preferred):
+
Our entire ''/etc/dovecot/dovecot.conf'' configuration file can be replaced with the following extremely minimal one since nearly everything we use is default and we only use IMAP with local UNIX accounts. SSL is required for incoming connections but unencrypted connections are supported locally so that the webmail and other local applications can access the mail without requiring any SSL configuration. For more detail about the configuration format, see [http://wiki2.dovecot.org/SSL/DovecotConfiguration their wiki].
{{code|<pre>
+
 
 +
*'''Note1:''' this configuration is for Dovecot 2.x, if you're still using 1.x check the history if this article and look at the revision prior to the comment about changing to Dovecot 2.x
 +
*'''Note2:''' if you have trouble with your client having frequent login trouble, try ''adding mail_max_userip_connections = 100''
 +
*'''Note3:''' You can use your ''LetsEncrypt'' certificate instead of a self-signed one using the ''fullchain.pem'' and ''privkey.pem'' files and ensuring they're accessible by Dovecot
 +
*'''Note4:''' I'm using Artem Sidorenko's recommended SSL cyphers from [https://www.sidorenko.io/post/2014/02/secure-ssl-configuration-for-apache-postfix-dovecot/ here], except that we don't disable SSLv2 since it's was [https://www.openssl.org/news/changelog.html#x6 removed completely from OpenSSL].
 +
 
 +
<source>
 
log_path = /var/log/dovecot.log
 
log_path = /var/log/dovecot.log
  
protocols = imap imaps pop3 pop3s
+
protocols = imap
 
 
 
protocol imap {
 
protocol imap {
listen = *:143
+
        listen = 127.0.0.1:143
ssl_listen = *:993
+
        ssl_listen = *,[::]:993
}
 
 
 
protocol pop3 {
 
listen = *:110
 
ssl_listen = *:995
 
pop3_uidl_format = %08Xu%08Xv
 
 
}
 
}
  
 
mail_location = maildir:~/Maildir
 
mail_location = maildir:~/Maildir
 +
maildir_very_dirty_syncs = yes
  
auth default {
+
userdb {
userdb passwd {
+
        driver = passwd
}
 
passdb pam {
 
}
 
mechanisms = plain
 
 
}
 
}
</pre>}}
 
  
 +
passdb {
 +
        driver = pam
 +
}
  
=== IMAP Certificate ===
+
ssl = required
{{note|we prefer to use a wildcard domain for the ''common name'' such as '''*.organicdesign.co.nz''' so that it's possible for different sub-domains to access the service but get handled differently.}}
+
ssl_prefer_server_ciphers = yes
The certificate's are build automatically upon installation to match the servers primary domain name. To rebuild them for a different mailserver domain name, run the [[dovecot-cert.sh]] script, enter the organisation details and the mailserver domain in the ''Common Name'' field, then restart dovecot.
+
ssl_cipher_list = EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:
 +
                  EECDH+aRSA+RC4:EECDH:EDH+aRSA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4
 +
ssl_protocols = !SSLv3                        # SSLv2 has been removed from OpenSSL so is not even a valid name to disable
 +
ssl_cert = </etc/dovecot/dovecot.pem          # you can also use your LE cert: /etc/letsencrypt/live/..../fullchain.pem
 +
ssl_key = </etc/dovecot/private/dovecot.pem  # you can also use your LE cert: /etc/letsencrypt/live/..../privkey.pem
 +
</source>
  
 
== Spam Assassin ==
 
== Spam Assassin ==
The ''spamassassin'' and ''spamc'' packages are required, but have now been included in the [[organicdesign-server]] package. The configuration file ''/etc/default/spamassassin'' needs to be edited and the ''ENABLED'' option set to '''1'''. Remember to start ''spamassassin'' from init.d after enabling it.
+
First install the ''spamassassin'' and ''spamc'' packages, then edit the configuration file ''/etc/default/spamassassin'' and set the ''ENABLED'' option set to '''1'''. It's also a good idea to set the ''CRON'' option to '''1''' as well so that the rules are kept up to date on a daily basis. Remember to start ''spamassassin'' after enabling it; The only configuration you may want to change from the default is the ''required_scor'' setting in ''/etc/spamassassin/local.cf'', the default is 5, but this can probably be dropped a bit to filter out more spam without loosing any real messages which are rarely even above zero.
 +
<source>
 +
service spamassassin start
 +
</source>
 +
 
 +
 
 +
Check you have no Perl modules missing with the following command and install them with ''cpanm'' if necessary:
 +
<source lang="bash">
 +
spamassassin -D --lint 2>&1 | grep -i failed
 +
</source>
 +
 
  
 
The ''exim4'' configuration needs to be adjusted which is in the ''/etc/exim4/exim4.conf.template'' file because our servers use the monolithic configuration method.
 
The ''exim4'' configuration needs to be adjusted which is in the ''/etc/exim4/exim4.conf.template'' file because our servers use the monolithic configuration method.
  
Add the following section after the line containing '''end router/800_exim4-config_maildrop'''
+
Add the following block at the end of the '''router/800_exim4-config_maildrop''' section:
{{code|<pre>
+
<source>
# 850: Spamcheck router
 
 
spamcheck_router:
 
spamcheck_router:
 
no_verify
 
no_verify
Line 196: Line 322:
 
driver = accept
 
driver = accept
 
transport = spamcheck_transport
 
transport = spamcheck_transport
</pre>}}
+
</source>
  
  
Add the following section '''before''' the line containing '''end transport/30_exim4-config_remote_smtp_smarthost'''
+
Add the following block at the end of the '''transport/30_exim4-config_remote_smtp_smarthost''' section:
  
 
'''Note:''' be sure to check that the ''spamc'' binary is in the location specified, and that it is running.
 
'''Note:''' be sure to check that the ''spamc'' binary is in the location specified, and that it is running.
{{code|<pre>
+
<source>
 
# 30: Spamcheck transport
 
# 30: Spamcheck transport
 
spamcheck_transport:
 
spamcheck_transport:
Line 217: Line 343:
 
message_prefix =
 
message_prefix =
 
message_suffix =
 
message_suffix =
</pre>}}
+
</source>
  
  
 
At this point, after ''exim4'' is restarted, all messages should have extra headers added by ''spamassassin'' for example:
 
At this point, after ''exim4'' is restarted, all messages should have extra headers added by ''spamassassin'' for example:
{{code|<pre>
+
<source>
X-Spam-Flag: YES
 
 
X-Spam-Checker-Version: SpamAssassin 3.1.7-deb (2006-10-05) on organicdesign.co.nz
 
X-Spam-Checker-Version: SpamAssassin 3.1.7-deb (2006-10-05) on organicdesign.co.nz
 
X-Spam-Level: ***********************
 
X-Spam-Level: ***********************
Line 231: Line 356:
 
URIBL_SBL,URIBL_SC_SURBL,URIBL_WS_SURBL autolearn=spam  
 
URIBL_SBL,URIBL_SC_SURBL,URIBL_WS_SURBL autolearn=spam  
 
version=3.1.7-deb
 
version=3.1.7-deb
</pre>}}
+
</source>
  
 
=== Moving spams to another folder automatically ===
 
=== Moving spams to another folder automatically ===
Line 237: Line 362:
  
 
To make a new filter, create a file in your home directory called ''.forward'' and ensure it is owned by the user in question, then add a rule such as in the following example from our server which forwards the spams straight into the '''Trash''' folder.
 
To make a new filter, create a file in your home directory called ''.forward'' and ensure it is owned by the user in question, then add a rule such as in the following example from our server which forwards the spams straight into the '''Trash''' folder.
{{code|<pre>
+
<source>
 
# Exim filter
 
# Exim filter
 
if
 
if
 
   $h_X-Spam-Status: CONTAINS "Yes"
 
   $h_X-Spam-Status: CONTAINS "Yes"
  or
 
  "${if def:h_X-Spam-Flag {def}{undef}}" is "def"
 
 
then
 
then
 
   save $home/Maildir/.Trash/
 
   save $home/Maildir/.Trash/
 
   finish
 
   finish
 
endif
 
endif
</pre>}}
+
</source>
 
*'''<font color=red>Important:</font>''' do not change the comment in the first line!
 
*'''<font color=red>Important:</font>''' do not change the comment in the first line!
 
*'''<font color=red>Important:</font>''' use '''\40''' for spaces in target directory name (escaped octal!)
 
*'''<font color=red>Important:</font>''' use '''\40''' for spaces in target directory name (escaped octal!)
Line 268: Line 391:
  
 
To check how many spams and hams the system has processed as well as some other internal data, use '''sa-learn --dump magic''', here's the output of our system on 24 Dec 2008 which clearly shows that we haven't been feeding it enough ham! It shows that if we feed it ham at the same rate as spam, then it would start becoming useful in about another couple of weeks :-)
 
To check how many spams and hams the system has processed as well as some other internal data, use '''sa-learn --dump magic''', here's the output of our system on 24 Dec 2008 which clearly shows that we haven't been feeding it enough ham! It shows that if we feed it ham at the same rate as spam, then it would start becoming useful in about another couple of weeks :-)
{{code|<pre>
+
<source>
 
# sa-learn --dump magic
 
# sa-learn --dump magic
 
0.000          0          3          0  non-token data: bayes db version
 
0.000          0          3          0  non-token data: bayes db version
Line 280: Line 403:
 
0.000          0          0          0  non-token data: last expire atime delta
 
0.000          0          0          0  non-token data: last expire atime delta
 
0.000          0          0          0  non-token data: last expire reduction count
 
0.000          0          0          0  non-token data: last expire reduction count
</pre>}}
+
</source>
  
  
 
Here's a couple of lines of Perl which extract and print the number of spams and hams from the output shown above. I added this to our daily housekeeping script so that it appears in our recent changes each day.
 
Here's a couple of lines of Perl which extract and print the number of spams and hams from the output shown above. I added this to our daily housekeeping script so that it appears in our recent changes each day.
{{code|<perl>
+
<source lang="perl">
 
$_ = `sa-learn --dump magic`;
 
$_ = `sa-learn --dump magic`;
 
print m/\s([1-9]+\d*).+?am[\x00-\x1f]+.+?([1-9]+\d*).+?am[\x00-\x1f]+.+?([1-9]+\d*).+?ns$/m
 
print m/\s([1-9]+\d*).+?am[\x00-\x1f]+.+?([1-9]+\d*).+?am[\x00-\x1f]+.+?([1-9]+\d*).+?ns$/m
 
       ? "$1 spams and $2 hams have been processed with $3 tokens."
 
       ? "$1 spams and $2 hams have been processed with $3 tokens."
 
       : "ERROR";
 
       : "ERROR";
</perl>}}
+
</source>
  
  
 
Our training script, ''/var/www/learn-spam.pl'', is run daily from the ''crontab'' and contains the following:
 
Our training script, ''/var/www/learn-spam.pl'', is run daily from the ''crontab'' and contains the following:
{{code|<perl>
+
<source lang="perl">
 
#!/usr/bin/perl
 
#!/usr/bin/perl
  
Line 309: Line 432:
 
         qx "rm -fr $_/*";
 
         qx "rm -fr $_/*";
 
}
 
}
</perl>}}
+
</source>
  
  
 
To test if the script is working properly, run it manually from the shell at such a time as you know there are spams to be processed and you should see messages stating that new tokens have been learned such as in the following example output.
 
To test if the script is working properly, run it manually from the shell at such a time as you know there are spams to be processed and you should see messages stating that new tokens have been learned such as in the following example output.
{{code|<pre>
+
<source>
 
archive-iterator: readdir found no mail in '/home/foo/Maildir/.INBOX.Spam/cur' directory
 
archive-iterator: readdir found no mail in '/home/foo/Maildir/.INBOX.Spam/cur' directory
 
Learned tokens from 0 message(s) (0 message(s) examined)
 
Learned tokens from 0 message(s) (0 message(s) examined)
Line 326: Line 449:
 
archive-iterator: readdir found no mail in '/home/buz/Maildir/.INBOX.Spam/new' directory
 
archive-iterator: readdir found no mail in '/home/buz/Maildir/.INBOX.Spam/new' directory
 
Learned tokens from 0 message(s) (0 message(s) examined)
 
Learned tokens from 0 message(s) (0 message(s) examined)
</pre>}}
+
</source>
  
 
=== Backing & restoring up the Bayesian database ===
 
=== Backing & restoring up the Bayesian database ===
Line 333: Line 456:
 
To back up the database use '''sa-learn --backup > mybackup.txt''' and to restore it again, first clear any existing data if there is any with '''sa-learn --clear''' and then use '''sa-learn --restore mybackup.txt''' to restore from the backup file.
 
To back up the database use '''sa-learn --backup > mybackup.txt''' and to restore it again, first clear any existing data if there is any with '''sa-learn --clear''' and then use '''sa-learn --restore mybackup.txt''' to restore from the backup file.
  
The actual data is stored in ''~/.spamassassin'' in files called ''bayes_tok'' and ''bayes_seen'', so if there's a problem and they dissapear you can forice the location ''sa-learn'' looks for its data with the '''--dbpath''' parameter then do a backup and restore without the parameter.
+
The actual data is stored in ''~/.spamassassin'' in files called ''bayes_tok'' and ''bayes_seen'', so if there's a problem and they dissapear you can force the location ''sa-learn'' looks for its data with the '''--dbpath''' parameter then do a backup and restore without the parameter.
  
=== Updating Spam Asassin's rules ===
+
=== Updating Spam Assassin's rules ===
 
In addition to relying on the Baysean database, it's also useful to run the ''sa-update'' script every month or so which downloads refined rules from the Spam Assassin channel. GPG keys are used to ensure that the rules are coming from the correct source, but I haven't got these working yet. Following is the command to run the update without checking the channel for authenticity since it won't replaced the current rules with the downloaded ones if this is not specified. The ''spamd'' daemon must be restarted after the rules have been updated.
 
In addition to relying on the Baysean database, it's also useful to run the ''sa-update'' script every month or so which downloads refined rules from the Spam Assassin channel. GPG keys are used to ensure that the rules are coming from the correct source, but I haven't got these working yet. Following is the command to run the update without checking the channel for authenticity since it won't replaced the current rules with the downloaded ones if this is not specified. The ''spamd'' daemon must be restarted after the rules have been updated.
{{code|<pre>
+
<source>
 
sa-update --nogpg
 
sa-update --nogpg
/etc/init.d/spamassassin restart
+
service spamassassin restart
</pre>}}
+
</source>
  
== Client setup ==
+
=== Set up a local non-forwarding caching DNS server ===
Any standard mail client such as ''Thunderbird'' or ''Outlook'' should connect with no trouble, but our procedure doesn't yet include the generation of a valid SSL certificate, so you'll get a warning initially which you can specify to be ignored for subsequent connections. We don't recommend using any Microsoft products at all, but if you had no choice, then the [[Set up an IMAP account on Outlook 2007]] procedure may be of some help.
+
'''Note:''' {{h|This step has a major impact on SpamAssassin's effectiveness!}}
  
=== Incoming mail settings (IMAP) ===
+
[[w:DNSBL|DNS block lists]] (DNSBL) are an important tool for blocking spam because the spammers use a variety of different domains all the time to try and combat spam filters (see [https://wiki.apache.org/spamassassin/UsingNetworkTests using network test] in general). The DNS block lists maintain a real-time list of spammer's domains which can be queried using DNS. Spam Assassin is configured to use many different DNSBL services by default, but the requests for the data may be [http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block blocked] if the DNS server you're using is not paying for the DNSBL service and is making too many requests to it. You can easily see if your DNSBL requests are being blocked because you'll see '''URIBL_BLOCKED''' in your ''X-Spam-Status'' message headers, e.g.
We use the IMAP protocol, but it is possible to use both POP3 and IMAP together for the same account because both are simply different protocols for accessing the same mail. There's probably no useful purpose to do that, but if you do then make sure the client settings for the POP setup is configured to leave the messages on the server, otherwise any messages downloaded from the inbox will be removed and will therefore not be available from the IMAP inbox folder either. Note that any messages that have been moved out of the inbox and into another folder using the IMAP protocol will no longer be available for download by the POP3 protocol because it only interacts with the inbox folder which is the root mailbox.
+
<source>
 +
X-Spam-Status: No, score=-1.4 required=5.0 tests=BAYES_00,HTML_IMAGE_ONLY_32,
 +
HTML_MESSAGE,T_DKIM_INVALID,T_RP_MATCHES_RCVD,{!URIBL_BLOCKED!},URI_NOVOWEL
 +
autolearn=ham version=3.3.2
 +
</source>
  
The settings for OrganicDesign IMAP access are: '''imap.organicdesign.co.nz''' on port '''993''' using '''SSL''' encryption.
+
To get around this problem you simply need to set up your own [http://wiki.apache.org/spamassassin/CachingNameserver local caching non-forwarding name server] (see also [http://social.dnsmadeeasy.com/blog/understanding-dns-forwarding/ understanding DNS forwarding]). See [[Configure DNS]] for installation and configuration details.
 
 
=== Outgoing mail settings (SMTP) ===
 
To set the email client to use the organisations SMTP server, go to ''Account settings/Outgoing server (SMTP)'' and add a new one called '''OrganicDesign''' with server address '''smtp.organicdesign.co.nz''', port '''2525''' and '''STARTTLS''' security. Use your IMAP login details with normal password authentication method.
 
  
You may want to keep your ISP's SMTP server as the default since it will work more quickly being more local and not requiring encryption or authentication, but you can easily make the Organic Design SMTP server the default when working in the field.
+
Your ''X-Spam'' header should be looking more like this:
 +
<source>
 +
X-Spam-Status: Yes, score=11.0 required=5.0 tests=ADVANCE_FEE_2_NEW_MONEY,
 +
BAYES_00,HTML_MESSAGE,LOTS_OF_MONEY,MIME_HTML_ONLY,RCVD_IN_BRBL_LASTEXT,
 +
RCVD_IN_XBL,RDNS_NONE,URIBL_BLACK,URIBL_DBL_SPAM,URIBL_SBL,URIBL_SBL_A,
 +
URIBL_WS_SURBL autolearn=no autolearn_force=no version=3.4.0
 +
</source>
  
== Domain setup ==
+
== Domain and IP address configuration ==
 +
Following are some important configurations that should be done on the mail-server's domain and IP address. If these aren't done then emails sent by the server are more likely to be routed to the recipient's junk folder, or may even be rejected by the remote server.
  
 
=== Reverse DNS ===
 
=== Reverse DNS ===
Any site that sends emails should have reverse DNS correctly configured. Having a reverse DNS correctly set up will help to prevent the site's mails being trashed as spam. Many mail-servers will do a reverse lookup on the sending IP address and ensure it matches the senders specified domain.
+
Any site that sends emails should have reverse DNS correctly configured. Most mail-servers will do a reverse lookup on the sending IP address and ensure it matches the senders specified domain. Note that it's best if this domain has a host name part, not just the fully-qualified domain name part, for example '''mail'''.mydomain.com.
  
 
This is not done by the domain registrar, it's done by the company hosting the server (the IP address owner), sometimes they include the ability to set it in the server management interface. If not, raise a support ticket asking them if they can set up a PTR record for the server's IP pointing to the domain specified in '''/etc/mailname''' or Exim's ''primary_domain'' setting.
 
This is not done by the domain registrar, it's done by the company hosting the server (the IP address owner), sometimes they include the ability to set it in the server management interface. If not, raise a support ticket asking them if they can set up a PTR record for the server's IP pointing to the domain specified in '''/etc/mailname''' or Exim's ''primary_domain'' setting.
  
 
You can find out more about what reverse DNS is and why it's important [http://aplawrence.com/Blog/B961.html here]. To check the reverse DNS of any IP address simply do the ''host'' command as you would for a domain, e.g.
 
You can find out more about what reverse DNS is and why it's important [http://aplawrence.com/Blog/B961.html here]. To check the reverse DNS of any IP address simply do the ''host'' command as you would for a domain, e.g.
{{code|<bash>host 1.2.3.4</bash>}}
+
<source lang="bash">
 
+
host 1.2.3.4
=== SPF record ===
+
</source>
A lot of mail-servers now require senders domains to have an [[w:Sender Policy Framework|SPF record]]. I think that the following is a good generic record format to use as the ''IP4'' will fail if the server moves to a new IP address.
 
{{code|<pre>v=spf1 a mx ptr -all</pre>}}
 
This version means that the mail will pass as long as the senders address has a matching ''A'' and ''MX'' record for the domain it says it's from, and the reverse lookup matches one of the domains ''A'' records. The record is simply a ''TXT'' record in the domain on the ''@'' prefix.
 
 
 
=== NZ domains ===
 
Our NZ domains are handled through [http://www.webdrive.co.nz WebDrive]. The main setup is done from the associated domain template. Assign a primary and secondary MX record with a subdomain each. Both subdomains must explicitly exist as A records in the template.
 
  
=== Other domains ===
+
=== Block-lists ===
Our non .co.nz domains are handled through [http://www.namecheap.com NameCheap], so I'll cover the setup for them, but it should be easy to adjust to any name hosting service. In the ''all host records'' page for your domain, go to ''mail settings'' at the bottom, and set it to "User Simplified" then click ''save changes''.
+
You should also check [https://mxtoolbox.com MX Toolbox] to see if your mail-server IP is listed in any block-lists. If it is then that page will tell you which ones and include links on where to go to get the IP removed from the lists.
  
Now scroll to the bottom of the page again and fill in the "User Simplified" form, set ''HOST NAME'' to "mail" (this setting seems to be superfluous), fill in the ''MAILSERVER IP'' and set the ''MX PREF'' to 1, then click ''save changes''.
+
=== TXT records ===
 +
There are a number of different mechanisms for verifying senders that are based on TXT records in the sending domain. These TXT records should be included in all domains that can be used as ''from'' addresses by the MTA.
  
== RoundCube (IMAP-only webmail) ==
+
==== DKIM (Domain Keys Identified Mail) ====
{{h|'''Note:''' there is now a roundcube package available for Debian and Ubuntu via APT, but we need to test where and how exactly it installs before updating this procedure to use it.}}
+
Setting up DKIM allows outgoing messages to be signed and the signature verified by receiving servers by looking up the public key in a TXT record associated with the domain.
  
We need to have access to our IMAP folder structures from a browser, we use the [http://trac.roundcube.net RoundCube] webmail application for this purpose.
+
Create a key pair using ''openssl'' as follows:
 
+
<source lang="bash">
First ensure that the sub-domains which should have webmail access have a rule in the web-server configuration mapping it to the roundcube code base which can be downloaded from [http://roundcube.net/downloads here] and should be saved to ''/var/www/domains/webmail''. See [[set up a new domain name]] for details on mapping a sub-domain to a web application code-base.
+
mkdir /etc/organicdesign/dkim
 
+
cd /etc/organicdesign/dkim
Next, go to ''your.domain/installer'' and follow their installation procedure. Use ''localhost'' for the server addresses (you may need to use ''ssl://localhost'' if non-ssl is denied by the server). You'll need to manually create the MySQL database:
+
openssl genrsa -out dkim-private.pem 2048
{{code|<mysql>create database roundcubemail;</mysql>}}
+
openssl rsa -in dkim-private.pem -out dkim.pub -pubout -outform PEM
 
+
chown root:Debian-exim dkim-private.pem
 
+
chmod 640 dkim-private.pem
The installer tests whether the database exists and is writable, then allows you to initialise it with a button. At this point you can also test the IMAP login and SMTP sending.
+
</source>
  
=== Manually populating the database ===
 
Under some (currently unknown) circumstances the database tables are not created by the web installer, so you may need to manually populate the database by manually logging in to MySQL and creating a database called '''roundcubemail''', and then importing from the shell with the following command:
 
{{code|<pre>
 
mysql -u USER -p PASS roundcubemail < /var/www/domains/webmail/SQL/mysql.initial.sql
 
</pre>}}
 
  
Once all these are working, go to the root of the webmail domain and login.
+
We like to keep the selector in a file too, so that our scripts that update DNS records can access all the DKIM information in one place.
 +
<source lang="bash">
 +
echo "selector: {!YOUR_SELECTOR!}" > dkim-selector
 +
</source>
  
=== Upgrading ===
+
You need to add the DKIM settings to you Exim4 configuration, they can go right at the top with the other specifics added. Setting ''DKIM_STRICT'' means that messages won't be sent and errors will be logged if the outgoing messages cannot be signed for some reason, for example if Exim can't read the private key. Don't forget to check the ''paniclog'' as well.
To upgrade the Roundcube code-base, download and unpack the new version into a temporary location, change into that directory then run the update script with the location of the current installation as the parameter.
+
<source>
{{code|<bash>./bin/installto.sh /var/www/domains/webmail/</bash>}}
+
DKIM_CANON = relaxed
 +
DKIM_SELECTOR = ${lookup{selector}lsearch{/etc/organicdesign/dkim/dkim-selector}}
 +
DKIM_DOMAIN = primary_hostname
 +
DKIM_PRIVATE_KEY = /etc/organicdesign/dkim/dkim-private.pem
 +
DKIM_STRICT = true
 +
</source>
  
=== Apache configuration ===
+
Then the record itself will be a TXT record with host name of:
The webmail is best forced to HTTPS, so we have some configuration in both the plain and secure virtual host containers. In the plain container we simple detect if the request domain starts with "webmail", and if so redirect the request to HTTPS. In the secure container we have the same domain-based rule but instead of redirecting, it rewrites the request to the appropriate file system location of the Roundcube code-base.
+
<source>{!YOUR_SELECTOR!}._domainkey</source>
{{code|<pre>
+
and the content of your public DKIM key included for example:
<VirtualHost *:80>
+
<source>
  ...
+
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC30aRx6rlDA7Lkhs/4XJMo
RewriteCond %{HTTP_HOST} ^(webmail\..+)$
+
1AtuW8LoBrjo6RZH3yS7nC9EgqV5ntFIzQyCo88hNBz72XwwFAAGKuCVIwcxV06lAHWnUTt+ZyjJlP
RewriteRule (.*) https://%1$1 [R,L]
+
9JH76ZJu9vUTaHw753IY3SZR+xEnJuyBr/LZknAEFqHuDP7V3+B6SWuBElSFFnImnP7oeMQQIDAQAB
  ...
+
</source>
</pre>}}
+
'''Note:''' The key should be a single line in the TXT record. I've broken it here for clarity, and also the ''.pem'' file is multi-line as well.
  
{{code|<pre>
+
You can test your DKIM signature at [http://dkimvalidator.com dkimvalidator.com].
<VirtualHost *:443>
 
  ...
 
RewriteCond %{HTTP_HOST} ^webmail\.
 
RewriteRule (.*) /webmail$1 [L]
 
  ...
 
</pre>}}
 
  
=== Troubleshooting ===
+
==== SPF (Sender Policy Framework)  ====
One issue is that on some systems the installation procedure gives an error saying that the ''suhosin.session.encrypt'' option value is incorrect (it should be disabled) even though it says it is disabled in ''php.ini''. The ''suhosin'' settings have their own ''php.ini'' configuration file in ''/etc/php5/apache2/conf.d/suhosin.ini'' which overrides the settings in the default ''php.ini''. Change the line containing the ''suhosin.session.encrypt'' option in this file to disable it (and don't forget to uncomment the line by removing the leading semicolon), then restart the web server and run the Roundcube installer again.
+
A lot of mail-servers now require senders domains to have an [[w:Sender Policy Framework|SPF record]] ([http://www.openspf.org/Implementations official site]). I think that the following is a good clear choice to use, but you'll need to update the record if the server moves to a new IP address.
 +
<source>
 +
v=spf1 ip4:{!SERVER-IP4!} ip6:{!SERVER-IP6!} -all
 +
</source>
 +
This simply ensures that mail can only be sent from your domains if it comes from your mail server's IP address. Before we had a version that used a ''PTR'' rule to make an IP independent rule, but reverse lookups are very resource intensive and cause mail sending to be slow down too. The record is simply a ''TXT'' record in the domain on the ''@'' prefix for a normal domain, or on the appropriate host if the mailname/rDNS of the server is a sub-domain.
  
=== Manual Configuration ===
+
'''Note:''' SPF checking for Exim4 has been supported since version 4.52 and in recent version can be enabled simply by setting the ''CHECK_RCPT_SPF'' flag and installing the ''spf-tools-perl'' package via ''apt''.
In our configuration there is only one setting in ''db.inc.php'' which is the mysql database connection line, and there are a few settings in the ''main.inc.php'', the ''default_host'' is "ssl://localhost", the ''des_key'', the ''product_name'' which is "OrganicDesign Webmail" and finally the ''mail_domain'' should be set to the default domain that usernames should be under to make a valid return address.
 
  
== Exim broken after apt-get upgrade ==
+
[http://dkimvalidator.com dkimvalidator.com] also checks SPF records too.
We had a major problem with exim after doing an ''apt-get upgrade'' where the configuration files were incompatible with the new format. The error was something like the following:
 
{{code|<pre>
 
DEBCONFsomethingDEBCONF found in exim configuration. This is most probably
 
caused by you upgrading to exim4 4.67-3 or later without accepting the
 
suggested conffile changes. Please read
 
/usr/share/doc/exim4-config/NEWS.Debian.gz for 4.67-2 and 4.67-4
 
2007-07-03 02:23:29 Exim configuration error in line 31 of
 
/var/lib/exim4/config.autogenerated.tmp: malformed macro definition
 
Invalid new configfile /var/lib/exim4/config.autogenerated.tmp, not
 
installing /var/lib/exim4/config.autogenerated.tmp to
 
/var/lib/exim4/config.autogenerated
 
</pre>}}
 
  
 +
==== DMARC (Domain-based Message Authentication, Reporting and Conformance) ====
 +
DMARC is a protocol for adding TXT record with the host name of ''_dmarc'' to the sending domains DNS record that instructs receiving servers on what to do with messages that do not have corresponding SPF records or DKIM signatures. We have the following DMARC record which simple instructs receivers to reject messages that do not have DKIM signatures, or matching SPF rules.
  
From the message it would seem that this problem started by deciding to keep my configuration file rather than allow it to be replaced by the new one. I did this because of all the changes shown above that I would have had to do again, but I shouldn't have been so lazy because after it broke it took far longer to figure out how to fix the problem. In the end I just had to completely purge Exim4 and re-install from scratch as follows:
+
The following advises the receiving MTA to reject any messages which fail either SPF or DKIM because the true server has both enabled. Use ''none'' instead of ''reject'' until you've confirmed that your SPF and DKIM configurations are functioning properly. For all the parameters and values see [http://www.zytrax.com/books/dns/ch9/dmarc.html here].
{{code|<pre>
+
<source>
apt-get -y remove --purge exim4-config exim4-daemon-heavy exim4-base
+
v=DMARC1; p=reject
mv /etc/exim4 /home/nad/
+
</source>
apt-get install exim4-daemon-heavy
 
dpkg-reconfigure exim4-config
 
</pre>}}
 
And from there go through all the instructions above to re-apply our configuration from scratch again!
 
  
== Synchronising Mail Folders ==
+
You can test by sending a message to gmail and checking the headers of the message in there. Google adds headers indicating its authentication tests and results. Got to the message and click the menu icon at the top right of the opened message content, then select "show original". You'll see a line like the following:
{{code|<pre>
+
<source>
nad@nad-laptop:~$ apt-cache search imapsync
+
Authentication-Results: mx.google.com;
imapcopy - IMAP backup, copy and migration tool
+
  dkim=pass header.i=@organicdesign.co.nz header.s=201809191 header.b=YuECVckW;
imapsync - IMAP synchronization, copy and migration tool
+
  spf=pass (google.com: domain of aran@organicdesign.co.nz designates 213.5.71.227 as permitted sender) smtp.mailfrom=aran@organicdesign.co.nz;
</pre>}}
+
  dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=organicdesign.co.nz
 +
</source>
  
 +
For more detailed results covering everything, you can test your mail sending at [https://www.mail-tester.com mail-tester.com] which is a pay service, but gives you three tests per day for free.
 +
{{table
 +
|[[File:Mail-test-results-1.jpg|400px]]
 +
|[[File:Mail-test-results-2.jpg|664px]]
 +
}}
  
For synchoronising the data from another backed up Maildir, use '''apt-get install maildirsync''' and the following example syntax:
+
== Filter files ==
{{code|<pre>
+
Exim uses a special scripting language that can be placed in user's ''.forward'' files to specify more complex forwarding and filtering rules. If the ''.forward'' file is using the Exim scripting language it must have ''# Exim filter'' on the first line. The default filter file we use is the one shown above in the Spamassassin configuration, but the file can be extended in numerous way to cater for the specific needs of individual users.
maildirsync -rvvv Source/Maildir/ Dest/Maildir results-info.bz2
 
</pre>}}
 
  
== Auto-responder / Vacation message ==
+
=== Auto-responder / Vacation message ===
 
See the extended ''.forward'' filter [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2Fvacation-filter here], and has been tested successfully. This method extends the existing forward filter, but unfortunately it raises an error if the ''.vacation.msg'' file isn't present, and it can only be ''required'' from within the exim configuration, not from within the filter itself, and there seems to be no condition for testing file existence.
 
See the extended ''.forward'' filter [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2Fvacation-filter here], and has been tested successfully. This method extends the existing forward filter, but unfortunately it raises an error if the ''.vacation.msg'' file isn't present, and it can only be ''required'' from within the exim configuration, not from within the filter itself, and there seems to be no condition for testing file existence.
  
=== Notes ===
+
=== Conditionally delivering messages to specific folders ===
*Exim doesn't need to be restarted for changes to the ''.forward'' or ''virtual.users'' files.
+
Sometimes we need to prevent the inbox getting too cluttered by having messages from some very prolific senders or lists get automatically filed. Here's a simple example.
*You can also forward messages to other recipients by adding one or more ''deliver'' directives to the filter file.
+
<source>
*To debug filter problems, stop the Exim4 service and restart on the command line with '''-bd -d+filter''' options.
+
# Exim filter
*Remember that vacation messages are only sent to the same user once per week, and deleting the ''.vacation.log'' file does not reset this, see [http://www.exim.org/exim-html-3.30/doc/html/filter_16.html manual for details].
+
if $header_from matches "Harry McProlific" then
*{{h|the '''foranyaddress $h_to: ( $thisaddress contains "$local_part@" )''' rule wasn't working last time I installed this - needs checking}}
+
    save $home/Maildir/.Lists.Unread.Harry/
 +
    finish
 +
endif
 +
</source>
 +
{{h|'''Note:''' don't forget the trailing slash or you'll get the "mailbox has too many links" error on delivery}}
  
== Setting up mailing lists ==
+
=== Setting up mailing lists ===
 
To create a simple mailing list, you must have the ''message_body_visible'' set to a reasonable number and the ''message_body_newlines'' directive set to ''true'' in ''/etc/exim4/exim4.conf.template'' (or in ''/etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs'' if you're using split-file config).
 
To create a simple mailing list, you must have the ''message_body_visible'' set to a reasonable number and the ''message_body_newlines'' directive set to ''true'' in ''/etc/exim4/exim4.conf.template'' (or in ''/etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs'' if you're using split-file config).
{{code|<pre>
+
<source>
 
message_body_newlines = true
 
message_body_newlines = true
 
message_body_visible = 5000
 
message_body_visible = 5000
</pre>}}
+
</source>
 
{{h|'''Note:''' this number of lines should be very large if you want to be able to send attachments in the mailing list}}
 
{{h|'''Note:''' this number of lines should be very large if you want to be able to send attachments in the mailing list}}
  
 
Next create a normal email account using the '''adduser''' command, and add an entry in ''/etc/exim4/virtual.users'' for the new address. Then create a ''.forward'' file (owned by the user) in their home directory as shown in the following example:
 
Next create a normal email account using the '''adduser''' command, and add an entry in ''/etc/exim4/virtual.users'' for the new address. Then create a ''.forward'' file (owned by the user) in their home directory as shown in the following example:
{{code|<pre>
+
<source>
 
# Exim filter
 
# Exim filter
 
if $h_subject contains "[Our List]" then
 
if $h_subject contains "[Our List]" then
Line 504: Line 624:
 
extra_headers "Content-type: $h_content-type\nContent-transfer-encoding: $h_Content-transfer-encoding"
 
extra_headers "Content-type: $h_content-type\nContent-transfer-encoding: $h_Content-transfer-encoding"
 
endif
 
endif
</pre>}}
+
</source>
 
The name of the list and its email address is added to the ''reply-to'' field, and the list of recipients is in the ''bcc'' field. Later these directives can be added to the files automatically. The ''to'' field contains just a dummy address that should be set up to get black-holed.
 
The name of the list and its email address is added to the ''reply-to'' field, and the list of recipients is in the ''bcc'' field. Later these directives can be added to the files automatically. The ''to'' field contains just a dummy address that should be set up to get black-holed.
  
 
We also have a MediaWiki extension called [http://svn.organicdesign.co.nz/filedetails.php?repname=extensions&path=%2FEximMailList%2FEximMailList.php EximMailList] that can populate a forward file automatically using the email addresses of the wiki users. It should be called from a cron job on the local host, and requires two settings in the ''LocalSettings.php'' file to be set, ''$wgEximMailListName'' and ''$wgEximMailListAddress''. The ''crontab'' entry should be something like this:
 
We also have a MediaWiki extension called [http://svn.organicdesign.co.nz/filedetails.php?repname=extensions&path=%2FEximMailList%2FEximMailList.php EximMailList] that can populate a forward file automatically using the email addresses of the wiki users. It should be called from a cron job on the local host, and requires two settings in the ''LocalSettings.php'' file to be set, ''$wgEximMailListName'' and ''$wgEximMailListAddress''. The ''crontab'' entry should be something like this:
{{code|<pre>5 * * * * root wget -q -O /home/foo/.forward "http://foo.com/wiki/index.php?title=MediaWiki:Common.css&action=eximfilter"</pre>}}
+
<source>
 +
5 * * * * root wget -q -O /home/foo/.forward "http://foo.com/wiki/index.php?title=MediaWiki:Common.css&action=eximfilter"
 +
</source>
 
I've used the article title ''MediaWiki:Common.css'' to cater for the case of the wiki being locked down, since the CSS article will always be in the white list.
 
I've used the article title ''MediaWiki:Common.css'' to cater for the case of the wiki being locked down, since the CSS article will always be in the white list.
  
== Delivering emails directly to web-sites or other local programs ==
+
;See also
Often sites require the ability to respond to incoming emails, for example the site may have the ability to reply to site notifications of in-site messages. We've previously used the [[MW:Extension:EmailToWiki|EmailToWiki extension]] for this, but the extension requires that the emails first be delivered to a mailbox and then a cron-job polls the mailbox and removes/processes messages using the IMAP protocol. This is a very unresponsive and resource consuming approach.
+
*[https://www.bettertechtips.com/linux/mailchimp-alternatives-open-source/ Open source alternatives to MailChimp]
 +
 
 +
=== Sending certain messages to multiple recipients ===
 +
Sometimes it's useful to be able to have some messages that arrive for a specific user send to a number of different people. This is a little different than simple setting up a number of forwarding addresses in the ''virtual.users'' file for the address, because in this case we want the rule to only apply to messages that match certain characteristics such as having a specific subject or From address. One example of where this can come in handy is for a company that's subscribed to a paid mailing list and needs all the staff to receive the information.
 +
 
 +
If you have mail lists set up in the way described in the previous section, then this can be achieved simply by using a slightly modified version of the mail-list filter rules as follows.
 +
<source>
 +
# Exim filter
 +
if $h_from contains "updates@expensive-info.com" then
 +
seen mail
 +
from $reply_address
 +
subject $h_subject
 +
text $message_body
 +
to "Organic Design staff<manager@organicdesign.co.nz>"
 +
bcc "manager@organicdesign.co.nz,sales@organicdesign.co.nz,dev@organicdesign.co.nz"
 +
extra_headers "Content-type: $h_content-type\nContent-transfer-encoding: $h_Content-transfer-encoding"
 +
endif
 +
</source>
 +
 
 +
=== Delivering emails directly to web-sites or other local programs ===
 +
Often sites require the ability to respond to incoming emails, for example the site may have the ability for users to reply to site notifications sent by email from the site. We've previously used the [[MW:Extension:EmailToWiki|EmailToWiki extension]] for this, but the extension requires that the emails first be delivered to a mailbox and then a cron-job polls the mailbox and removes/processes messages using the IMAP or POP3 protocol. This is a very unresponsive and resource consuming approach.
 +
 
 +
A new script has been made called [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2Fpost-email.pl post-email.pl] which allows emails to be delivered directly to a site via HTTP instead of being delivered to a mailbox. This script is invoked via an Exim filter in the recipient email-user's ''.forward'' file. The filter is a single line consisting of a [http://www.exim.org/exim-html-current/doc/html/spec_html/filter_ch03.html#SECTpipe pipe command] which sends the email content to the specified program in the ''STDIN'' stream, the URL that the extracted body text and headers will be posted to is specified as a command-line parameter to the program.
  
A new script has been made called [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2Fpost-email.pl post-email.pl] which allows emails to be delivered directly to a site via HTTP instead of being delivered to a mailbox. This script is invoked via an Exim filter in the recipient email users ''.forward'' file. The filter is a single line consisting of a [http://www.exim.org/exim-html-current/doc/html/spec_html/filter_ch03.html#SECTpipe pipe command] which sends the email content to the specified program in the ''STDIN'' stream, the URL that the extracted body text and headers will be posted to is specified as a command-line parameter to the program.
+
The script extracts the useful headers (id, date, from, to, subject) and the email body from the message and formats them appropriately using the same code as the original ''EmailToWiki'' extension used and then posts the data to the URL specified in the Exim filter's ''pipe'' command.
  
In this example filter, the ''post-email.pl'' script is expected to be in the same directory and has the following content:
+
The script has a feature allowing the '''$header_to''' value from the Exim filter to be used in the URL instead of a normal domain so that it can decide what to do with the message based on the recipient address. The domain part of the email address will be used as the site domain. The following example demonstrates this feature.
{{code|<pre># Exim filter
+
<source>
pipe "perl $home/post-email.pl \"http://www.example.com\""</pre>}}
+
# Exim filter
 +
pipe "perl $home/post-email.pl \"http://$header_to:/index.php\""
 +
</source>
 +
*The ''post-email.pl'' script, or a symlink to it,  is expected to be in the same directory as the filter
 
*Explicitly specifying the perl interpreter means there's no need to worry about the script's executable permissions
 
*Explicitly specifying the perl interpreter means there's no need to worry about the script's executable permissions
 
*The quotes are needed if the URL contains a query-string with ampersands
 
*The quotes are needed if the URL contains a query-string with ampersands
Line 524: Line 671:
  
 
This example allows some local addresses to deliver normally while all others go into the pipe:
 
This example allows some local addresses to deliver normally while all others go into the pipe:
{{code|<pre># Exim filter
+
<source>
 +
# Exim filter
 
if
 
if
 
   $header_to does not match "(info|admin)@"
 
   $header_to does not match "(info|admin)@"
 
then
 
then
 
   pipe "perl $home/post-email.pl \"http://www.example.com\""
 
   pipe "perl $home/post-email.pl \"http://www.example.com\""
endif</pre>}}
+
endif
 +
</source>
 +
 
 +
=== Notes on Exim Filters ===
 +
*Exim doesn't need to be restarted for changes to the ''.forward'' or ''virtual.users'' files.
 +
*You can also forward messages to other recipients by adding one or more ''deliver'' directives to the filter file.
 +
*To debug filter problems, stop the Exim4 service and restart on the command line with '''-bd -d+filter''' options.
 +
*Remember that vacation messages are only sent to the same user once per week, and deleting the ''.vacation.log'' file does not reset this, see [http://www.exim.org/exim-html-3.30/doc/html/filter_16.html manual for details].
 +
*{{h|the '''foranyaddress $h_to: ( $thisaddress contains "$local_part@" )''' rule wasn't working last time I installed this - needs checking}}
 +
 
 +
== RoundCube (IMAP-only webmail) ==
 +
We need to have access to our IMAP folder structures from a browser, we use the [http://trac.roundcube.net RoundCube] webmail application for this purpose.
 +
 
 +
First ensure that the sub-domains which should have webmail access have a rule in the web-server configuration mapping it to the roundcube code base which can be downloaded from [http://roundcube.net/downloads here] and should be saved to ''/var/www/domains/webmail''. See [[set up a new domain name]] for details on mapping a sub-domain to a web application code-base.
 +
 
 +
Next, go to ''your.domain/installer'' and follow their installation procedure which generates the configuration files for you (don't use real passwords as you can change these after you paste the configuration into the real files on the server). Set the '''default_host''' option to ''localhost'' to prevent the login from having a ''host'' input. Use ''localhost'' for the server addresses (you may need to use ''ssl://localhost'' if non-ssl is denied by the server). You'll need to manually create the MySQL database:
 +
<source lang="mysql">
 +
create database roundcubemail;
 +
</source>
 +
 
 +
 
 +
The installer tests whether the database exists and is writable, then allows you to initialise it with a button. At this point you can also test the IMAP login and SMTP sending.
 +
 
 +
=== Manually populating the database ===
 +
Under some (currently unknown) circumstances the database tables are not created by the web installer, so you may need to manually populate the database by manually logging in to MySQL and creating a database called '''roundcubemail''', and then importing from the shell with the following command:
 +
<source>
 +
mysql -u USER -p PASS roundcubemail < /var/www/domains/webmail/SQL/mysql.initial.sql
 +
</source>
 +
 
 +
Once all these are working, go to the root of the webmail domain and login.
 +
 
 +
=== Upgrading ===
 +
To upgrade the Roundcube code-base, download and unpack the new version into a temporary location, change into that directory then run the update script with the location of the current installation as the parameter.
 +
<source lang="bash">
 +
./bin/installto.sh /var/www/domains/webmail/
 +
</source>
 +
 
 +
'''Note:''' This process will rebuild the configuration and the hack shown below to prevent outgoing mail being saves to "Sent" will need to be added again. It would be best to check that the same method still works after the upgrade as well, but checking with Firebug that the ''save-to'' option in the ''compose'' view still uses the same key and value method in its posted data.
 +
 
 +
=== Nginx configuration ===
 +
The web-mail is best forced to HTTPS, so we have some configuration in both the plain and secure server blocks. In the plain block we simply detect if the request domain starts with "webmail", and if so redirect the request to HTTPS. In the secure block we have the same domain-based rule but instead of redirecting, it rewrites the request to the appropriate file system location of the RoundCube code-base.
 +
<source lang=nginx>
 +
server {
 +
listen 80;
 +
...
 +
server_name ~^webmail\.;
 +
rewrite ^ https://$host:443$uri last;
 +
...
 +
}
 +
</source>
 +
 
 +
<source lang=nginx>
 +
server {
 +
listen 443;
 +
ssl on;
 +
...
 +
if ( $host ~ ^webmail\. ) {
 +
rewrite ^/$ /webmail/index.php last;
 +
rewrite ^ /webmail$uri last;
 +
}
 +
...
 +
}
 +
</source>
 +
 
 +
=== Manual Configuration ===
 +
In our configuration there is only one setting in ''db.inc.php'' which is the mysql database connection line, and there are a few settings in the ''main.inc.php'', the ''default_host'' is "ssl://localhost", the ''des_key'', the ''product_name'' which is "OrganicDesign Webmail" in our case, and finally the ''mail_domain'' should be set to the default domain that user-names should be under to make a valid return address.
 +
 
 +
=== Disabling the copying to Sent folder ===
 +
If your server already handles the copying of sent messages into the "Sent" folder (for example using the procedure shown above), then you'll want to stop RoundCube from also making a copy of the message there. This is easily by un-setting the "Sent" option in RoundCube's "special folders" settings page, but if you do this then the "Sent" folder will be listed alphabetically amongst all the other normal folders. So it's preferable to keep treating "Sent" as a special folder, but disabling the copying functionality.
 +
 
 +
In RoundCube's "compose message" page there is a drop-down list that allows the message to be saved in a different folder, or no folder at all. If the "Sent" folder is special, then this drop-down list will be set to "Sent" by default. All we need to do to disable the copying-to-sent functionality is to add the following snippet into the beginning ''config.inc.php'' configuration file so that a "Sent" target for newly sent messages is changed to no target.
 +
<source lang="php">
 +
if( array_key_exists( '_store_target', $_POST ) && $_POST['_store_target'] == 'Sent' ) {
 +
$_POST['_store_target'] = '';
 +
}
 +
</source>
 +
 
 +
== Client setup ==
 +
Any standard mail client such as ''Thunderbird'' or ''Outlook'' should connect with no trouble. We don't recommend using any Microsoft products at all, but if you had no choice, then the [[Set up an IMAP account on Outlook 2007]] procedure may be of some help.
 +
 
 +
'''Privacy:''' You should set up PGP so you can communicate privately. This won't work or webmail, but works on desktop clients, or on Android you can use [https://www.openkeychain.org/ OpenKeyChain.org]
 +
 
 +
=== Incoming mail settings (IMAP) ===
 +
We use the IMAP protocol, but it is possible to use both POP3 and IMAP together for the same account because both are simply different protocols for accessing the same mail. There's probably no useful purpose to do that, but if you do then make sure the client settings for the POP setup is configured to leave the messages on the server, otherwise any messages downloaded from the inbox will be removed and will therefore not be available from the IMAP inbox folder either. Note that any messages that have been moved out of the inbox and into another folder using the IMAP protocol will no longer be available for download by the POP3 protocol because it only interacts with the inbox folder which is the root mailbox.
 +
 
 +
The settings for OrganicDesign IMAP access are: '''od.nz''' on port '''993''' (default) using '''SSL''' encryption.
 +
 
 +
=== Outgoing mail settings (SMTP) ===
 +
To set the email client to use the organisations SMTP server, go to ''Account settings/Outgoing server (SMTP)'' and add a new one called '''OrganicDesign''' with server address '''od.nz''', port '''587''' (default) and '''STARTTLS''' security. Use your IMAP login details with normal password authentication method.
 +
 
 +
You may want to keep your ISP's SMTP server as the default since it will work more quickly being more local and not requiring encryption or authentication, but you can easily make the Organic Design SMTP server the default when working in the field.
 +
 
 +
=== Using other servers as clients ===
 +
Mail is very complex to set up reliably these days (hence this huge procedure!). So if you run a number of servers that all need to have mail sending capability, you most likely don't want to set up rDNS, SPF, DKIM and DMARC for all of them. A far simpler solution is to use a simpler package like [https://marlam.de/msmtp/ msmtp] to present itself to the system as an MTA that the ''sendmail'' command can be symlinked to, but all it actually does is refers all requests to your main mail server.
 +
 
 +
First make sure that you don't have any other mail servers installed by default such as ''Exim4'' or ''Postfix'' and then install ''MSMTP''.
 +
<source lang="bash">
 +
which sendmail
 +
ls -al /usr/sbin/sendmail
 +
dpkg -l |grep exim
 +
apt remove exim4-daemon-light
 +
apt install msmtp
 +
</source>
 +
 
 +
Make sure that your sendmail command is linked to your new msmtp:
 +
<source lang="bash">
 +
ln -s /usr/bin/msmtp /usr/sbin/sendmail
 +
</source>
 +
 
 +
Create a minimal configuration in ''/etc/msmtprc'' with the following settings (based on your own SMTP servers configuration):
 +
<source>
 +
account default
 +
syslog LOG_MAIL
 +
maildomain your.remote.smtp.server
 +
host your.remote.smtp.server
 +
auto_from on
 +
port 587
 +
tls on
 +
tls_starttls on
 +
auth plain
 +
user your_smtp_server_user
 +
password *******
 +
</source>
 +
 
 +
And test the commonly required forms of mail sending capability:
 +
<source lang="bash">
 +
echo "testing" | mail -s "mail test" foo@bar.baz
 +
echo "Subject: sendmail test" | sendmail -v "foo@bar.baz"
 +
echo '<?php mail("foo@bar.baz","php test","testing");' | php
 +
</source>
 +
 
 +
== Backing up and synchronising Maildirs ==
 +
<source>
 +
nad@nad-laptop:~$ apt-cache search imapsync
 +
imapcopy - IMAP backup, copy and migration tool
 +
imapsync - IMAP synchronization, copy and migration tool
 +
</source>
 +
 
 +
 
 +
For synchoronising the data from another backed up Maildir, use '''apt-get install maildirsync''' and the following example syntax:
 +
<source>
 +
maildirsync -rvvv Source/Maildir/ Dest/Maildir results-info.bz2
 +
</source>
 +
 
 +
 
 +
You can also use [[rsync]] to maintain a backup of Maildirs on another server without having to copy the entire structure every time. But beware that Maildir uses colons in its filenames which can cause problems with some filesystems. See the [[rsync]] article for a solution to this.
  
 
== Troubleshooting ==
 
== Troubleshooting ==
 
Stop the exim service and run again from the shell with debugging enabled,
 
Stop the exim service and run again from the shell with debugging enabled,
{{code|<pre>exim4 -bd -d+filter</pre>}}
+
<source>
 +
exim4 -bd -d+filter
 +
</source>
 
For more information on the command line options, see [http://www.exim.org/exim-html-current/doc/html/spec_html/ch05.html chapter 5 of the manual].
 
For more information on the command line options, see [http://www.exim.org/exim-html-current/doc/html/spec_html/ch05.html chapter 5 of the manual].
  
=== Problems sending to hotmail ===
+
=== suhosin.session.encrypt option value is incorrect ===
Hotmail have increased their spam security and require a number of steps to be taken for smtp servers to be able to send mail to them without being blocked as spammers.
+
One issue is that on some systems the Roundcube installation procedure gives an error saying that the ''suhosin.session.encrypt'' option value is incorrect (it should be disabled) even though it says it is disabled in ''php.ini''. The ''suhosin'' settings have their own ''php.ini'' configuration file in ''/etc/php5/apache2/conf.d/suhosin.ini'' which overrides the settings in the default ''php.ini''. Change the line containing the ''suhosin.session.encrypt'' option in this file to disable it (and don't forget to uncomment the line by removing the leading semicolon), then restart the web server and run the Roundcube installer again.
 +
 
 +
=== Problems sending to Hotmail/Microsoft ===
 +
Hotmail have increased their spam security and require a number of steps to be taken for smtp servers to be able to send mail to them without being blocked as spammers. You can de-list blocked IPs at [https://sender.office.com sender.office.com].
 +
 
 +
=== .forward files not being processed ===
 +
The most common reason for this is that the messages that you're expecting to be forwarded do not have a local destination, check that the domain they're under is a local domain, you may need to add it to the ''dc_other_hostnames'' setting in ''/etc/exim4/update-exim4.conf.conf'' or to your ''virtual.domains'' file if you're using the above configuration.
 +
 
 +
=== Panic log contains "User 0 set for maildir_home transport is on the fixed_never_users list" ===
 +
This means that your configuration has required Exim to run as root which it does not allow. The most common reason for this is a local delivery to the ''root'' user. Either the domain of the local host should have its email handled by an external server so that the ''root@yourdomain'' address does not attempt to be delivered to the local ''root'' user, or there should be an alias for the local ''root'' user set in ''/etc/aliases''.
 +
 
 +
=== Our system has detected that this message does not meet IPv6 sending guidelines regarding PTR records and authentication ===
 +
This is an error that is often seen in the logs when Exim sends messages to Gmail. You'll need the domain to have reverse DNS setting for the IPv6 address, if your ISP can't provide this, then you can disable SMTP connections over IPv6 by adding the ''disable_ipv6'' directive into the ''main/02_exim4-config_options'' section.
 +
<source>
 +
## main/02_exim4-config_options
 +
#################################
 +
 
 +
disable_ipv6
 +
</source>
 +
 
 +
=== Spammers clogging up the server ===
 +
Today I had a problem whereby I couldn't send an email due to there being too many connections to the mail server. I've never had this before and it shouldn't happen since we're a very small operation with only twenty or thirty active email users. Using '''netstat -5 | grep smtp''' revealed that indeed there were many concurrent SMTP connections and they were all from a couple of IP addresses. I checked the Exim log to see if they were doing anything, but fortunately Exim was rejecting them due to them not having any valid host name associated with them.
 +
<source>
 +
2015-12-01 06:38:49 no host name found for IP address 223.240.93.244
 +
2015-12-01 06:38:51 no host name found for IP address 223.240.93.244
 +
2015-12-01 06:38:53 no host name found for IP address 223.240.93.244
 +
2015-12-01 06:38:59 no host name found for IP address 117.68.193.8
 +
2015-12-01 06:39:01 no host name found for IP address 117.68.193.8
 +
2015-12-01 06:39:03 no host name found for IP address 117.68.193.8
 +
2015-12-01 06:39:10 no host name found for IP address 117.68.193.8
 +
2015-12-01 06:39:16 no host name found for IP address 117.68.193.8
 +
2015-12-01 06:39:21 no host name found for IP address 117.68.193.8
 +
2015-12-01 06:39:28 no host name found for IP address 223.240.78.176
 +
2015-12-01 06:39:30 no host name found for IP address 223.240.78.176
 +
</source>
 +
 
 +
But since they're still clogging up the server I needed to block them at a lower level. The only problem is that the IP address keeps changing. So I wrote [{{repo|tools|ip-block.pl}} ip-block.pl] which is called every 5 min from the ''crontab'' to check the Exim log of recent activity for items of the format shown above, and any IP addresses that reoccur more than ten times are blocked from accessing port 25 by ''iptables''. Use '''iptables --list''' to check what filter rules are currently in place.
 +
<source>
 +
# iptables --list | grep smtp
 +
DROP      tcp  --  223.240.84.70        anywhere            tcp dpt:smtp
 +
DROP      tcp  --  175.45.186.150      anywhere            tcp dpt:smtp
 +
DROP      tcp  --  114.96.76.14        anywhere            tcp dpt:smtp
 +
DROP      tcp  --  223.240.75.50        anywhere            tcp dpt:smtp
 +
DROP      tcp  --  114.96.70.100        anywhere            tcp dpt:smtp
 +
DROP      tcp  --  94.249.127.124      anywhere            tcp dpt:smtp
 +
DROP      tcp  --  70.45.94.194        anywhere            tcp dpt:smtp
 +
DROP      tcp  --  179.127.166.29      anywhere            tcp dpt:smtp
 +
</source>
 +
 
 +
=== Message does not meet IPv6 sending guidelines regarding PTR records and authentication ===
 +
This is an error that is often seen in the logs when Exim sends messages to Gmail. Having an SPF record may fix it, also try adding an IPv6 rDNS record, but if you can'y do that or it still continues, you can disable SMTP connections over IPv6 by adding the ''disable_ipv6'' directive into the ''main/02_exim4-config_options'' section.
 +
 
 +
=== Warning: Unexpected error in SPF check ===
 +
This most likely means that you don't have the ''spf-tools-perl'' package installed which ''Exim4'' now relies on to do SPF checking.
 +
 
 +
=== Rejected EHLO, syntactically invalid argument ===
 +
The most common reason for this is a hostname having an underscore in it, e.g.
 +
<source>
 +
2019-02-13 08:23:25 1gtuVZ-0005gN-9n <= foo@bar.baz H=(_) [2a02:908::b419] P=esmtpsa X=TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128 CV=no A=plain_server:fred S=15273
 +
</source>
 +
It would be best to figure out why there's an underscore, or sometimes ONLY an underscore in the hostname, but you can allow the character in the Exim config with the following directive:
 +
<source>
 +
helo_allow_chars = _
 +
</source>
 +
 
 +
=== R=dnslookup T=remote_smtp defer (port 25 blocked - configure Exim to route all requests to a remote server) ===
 +
This most likely means that the Exim server is running behind a firewall, or on a service that has port 25 blocked. A mail server cannot operate without being able to send on port 25, because all MTA-MTA traffic occurs on port 25 (other mail ports such as 465 or 587 are only used for MUA-MTA traffic). The only solution is to route mail to another external SMTP server. In the following example, we configure the firewalled Exim to route mail to the Organic Design mail server which uses the PLAIN authentication method.
 +
 
 +
'''Note:''' If the firewalled server is only being used for sending then it may be easiest to use something more light-weight than Exim such as MSMTP, see the [[#Using other servers as clients|using other servers as clients]] section for details about this.
 +
 
 +
First add a new router '''before''' the current ''dnslookup'' router (the one that's failing in the log). We use ''auth_relay'' transport which we'll create next instead of the normal ''remote_smtp'', because if we don't authenticate, the remote Exim will send all mail to trash as spam. Also be sure to set the port in the ''route_list'' to '''587''' since 25 is blocked (the double colon is not a typo).
 +
<source>
 +
smart_route:
 +
    driver = manualroute
 +
    domains = ! +local_domains
 +
    ignore_target_hosts = 127.0.0.0/8
 +
    transport = {!auth_relay!}
 +
    route_list = * smtp.organicdesign.co.nz{!::587!}
 +
    no_more
 +
</source>
 +
 
 +
 
 +
Now we need to create the ''auth_relay'' referred to in the previous block:
 +
<source>
 +
auth_relay:
 +
    driver = smtp
 +
    port = {!587!}
 +
    hosts_require_auth = $host_address
 +
    hosts_require_tls = $host_address
 +
</source>
 +
'''Note''' that actually the port doesn't seem to matter here, maybe it's been overridden by being specified in ''route_list''?
 +
 
  
First an SPF ([[w:Sender Policy Framework|Sender Policy Framework]]) record must be added to the domains DNS record. I used the MS [http://www.microsoft.com/senderid/wizard Sender ID wizard] which gave me this following content for the record which is added as a TXT record with the @ prefix:
+
Then we need to add a new ''PLAIN'' authentication method to replace the current one, search for "PLAIN" to find the current (non-commented-out one) and then either comment it out or change its ''public_name'' to ''PLAINFOO'' or something to avoid there being two authenticators with the same name.
{{code|<pre>v=spf1 ptr ip4:69.64.87.188 -all</pre>}}
+
<source>
 +
auth_plain:
 +
    driver = plaintext
 +
    public_name = PLAIN
 +
    hide client_send = ^{!USER!}^{!PASS!}
 +
</source>
  
In addition to this a form must be filled out [https://support.msn.com/eform.aspx?productKey=edfsjmrpp&page=support_home_options_form_byemail&ct=eformts&scrx=1 here] to be sent to MS. They got back to me via email quite promptly and got me to fill in [https://support.msn.com/eform.aspx?productKey=edfsmsbl2&ct=eformts this form].
 
  
After reading up on Wikipedia about the [[w:Sender Policy Framework|Sender Policy Framework]], I think that the following is probably a better record to use as the ''PTR'' is a bit confusing, and ''IP4'' will fail if the server moves to a new IP address.
+
Finally restart Exim, send a test message and check the log to see if your new items are being used:
{{code|<pre>v=spf1 a mx -all</pre>}}
+
<source>
This version means that the mail will pass as long as the senders address matches at least one ''A'' or ''MX'' record for the domain.
+
2019-02-16 12:38:18 1guzEV-00058l-Kl => foo@bar.baz R={!smart_route!} T={!auth_relay!} H={!smtp.organicdesign.co.nz!} [213.5.71.227] X=TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256 CV=yes DN="CN=organicdesign.co.nz" A={!auth_plain!} C="250 OK id=1guzEY-000263-0q"
 +
2019-02-16 12:38:18 1guzEV-00058l-Kl {!Completed!}
 +
</source>
  
 
== See also ==
 
== See also ==
*[[Wiki daemon]] ''- The wiki daemon can be configured to synchronise distributed maildirs and accounts''
+
*[[Configure mail sending]] ''- set up minimal sending capability having a functional mail command''
 +
*[[PGP]]
 +
*[[SSL]] ''- our info on SSL and LetsEncrypt certs''
 +
*[https://help.directadmin.com/item.php?id=153 Routing exim through a remote smtp server]
 
*[http://wiki.dovecot.org/MailServerOverview Mail Server Overview] ''- in DoveCot manual''
 
*[http://wiki.dovecot.org/MailServerOverview Mail Server Overview] ''- in DoveCot manual''
 
*[http://www.datadisk.co.uk/html_docs/exim/exim_cs.htm Exim cheatsheet]
 
*[http://www.datadisk.co.uk/html_docs/exim/exim_cs.htm Exim cheatsheet]
Line 555: Line 950:
 
**[http://www.exim-new-users.co.uk/content/view/25/28/ Exim4 configuration functions and variables]
 
**[http://www.exim-new-users.co.uk/content/view/25/28/ Exim4 configuration functions and variables]
 
**[http://www.exim-new-users.co.uk/content/view/39/1/ Exim4 configuration directives]
 
**[http://www.exim-new-users.co.uk/content/view/39/1/ Exim4 configuration directives]
 +
**[https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_plaintext_authenticator.html The PLAIN and LOGIN authenticators]
 
**[http://www.exim-new-users.co.uk/content/view/40/1/ Exim4 generic router options]
 
**[http://www.exim-new-users.co.uk/content/view/40/1/ Exim4 generic router options]
 
**[http://www.exim-new-users.co.uk/content/view/46/1/ The Exim4 queryprogram router] ''- routing messages according to an external program''
 
**[http://www.exim-new-users.co.uk/content/view/46/1/ The Exim4 queryprogram router] ''- routing messages according to an external program''
Line 561: Line 957:
 
**[http://www.exim.org/exim-html-current/doc/html/spec_html/filter_ch03.html Filter files]
 
**[http://www.exim.org/exim-html-current/doc/html/spec_html/filter_ch03.html Filter files]
 
***[http://www.exim.org/exim-html-3.10/doc/html/filter_20.html String conditions in filter files]
 
***[http://www.exim.org/exim-html-3.10/doc/html/filter_20.html String conditions in filter files]
 +
*[http://arstechnica.com/information-technology/2014/02/how-to-run-your-own-e-mail-server-with-your-own-domain-part-1/ Arstechnica's email procedure] ''- see also [http://arstechnica.com/information-technology/2014/03/taking-e-mail-back-part-2-arming-your-server-with-postfix-dovecot/ part 2], [http://arstechnica.com/business/2014/03/taking-e-mail-back-part-3-fortifying-your-box-against-spammers/ part 3] and [http://arstechnica.com/information-technology/2014/04/taking-e-mail-back-part-4-the-finale-with-webmail-everything-after/ part 4]''
 +
*[https://www.cyberciti.biz/faq/exim-remove-all-messages-from-the-mail-queue/ Viewing and clearing the mail queue]

Latest revision as of 14:47, 9 January 2024

Procedure.svg Configure mail server
Organic Design procedure


Mail Transfer Agents (MTA) connect to one another on port 25 to route mail to its destination. Other ports such as 465 or 587 are also used, but these are used exclusively for connections from Mail User Agents (MUA) to MTAs. All ports are the same as far as the listening MTA is concerned, any of them can use plain of encrypted connection which is negotiated as part of the SMTP protocol's handshake phase.

In our configuration described here, the MTA listens on ports 25 and 587 (encrypted) for incoming emails and decides whether to relay them somewhere else for delivery, or to deliver them locally. In our configuration we have no relaying at all since all recipient accounts we're handling are accounts on the same host as the MTA. So if an incoming email is destined for a local recipient, we pass it through a spam filter and then deliver it locally without any other restrictions.

But the local clients (MUAs) need to be able to use the MTA for sending mails from their local machines out to other external recipients. To cater for this situation, the MTA is configured so that it will allow delivery of mail to remote recipients as well, but only if the connection is encrypted and uses authentication with credentials that match one of the local UNIX accounts.

If authentication is used, the spam filter is bypassed even if deliver is to a local client, which means that the MTA can also be used as a "smart host" by other MTAs that are unable to send traffic on port 25, which is often then case when hosting on a DSL connection for example. In this case, the firewalled MTA can be configured to connect to an MTA configured as described here on port 587 with authentication.

Contents

Exim4 (MTA & MDA)

Exim4 is the default mail transfer and delivery agent for Debian, but it's only the light version which is insufficient for running a mail server, so first we need to install the exim4-daemon-heavy package which will replace the light version, and a few other packages we'll be needing.

sudo apt-get install exim4-daemon-heavy dovecot-common dovecot-imapd spamassassin spamc spf-tools-perl


Exim is designed to move messages from one e-mail server to another and to deliver messages to local users mailboxes. It has nothing to do with POP3 or IMAP as those are protocols relating to the retrieval of the mail by a user and their mail client software. The default Exim configuration allows the server to send mail, but if you plan on running an IMAP or POP3 server, then additional configuration is required which is discussed here.

First, run through the standard Exim4 configuration script, say no splitting configuration files, set general mail configuration to internet, use MailDir delivery method, and keep any non-self-explanatory settings as default.

dpkg-reconfigure exim4-config


When changing configuration in Exim4 there are three different levels:

  • Making changes to update-exim4.conf.conf requires running update-exim4.conf and service exim4 restart.
  • Making changes to exim4.conf.template requires only service exim4 restart.
  • Making changes to virtual.users, virtual.domains or any users .forward file requires no other action.

Mail name, hostname, rDNS and blacklists

It's important that the domain part of the MAIL FROM field in outgoing messages has a proper domain with a valid reverse lookup, otherwise some SMTP servers will reject them. To ensure this, check that the domain name specified in the first line of /etc/mailname is the correct one, and set it if not. Alternatively you can set the primary_host value in the exim configuration to the proper domain, this is best set at the top of the configuration file, and you may need to comment out other occurrences further down.

The hostname should be set to the domain with the hostname -b command and can also be set in /etc/hostname and added as a localhost name in /etc/hosts.

The reverse DNS should be set up in your server host's configuration (e.g. this is done in the Remote Access tab of the Linode configuration), the server host may need to do this for you if their client app doesn't support it. Note that you should have reverse DNS set up for your IPv6 address as well, and if your ISP can't provide this then you'll need to add the disable_ipv6 directive to your configuration (see troubleshooting section below).

You may find that your server's IP is listed in various spam or abuse blacklists, a good service to check all the common blacklists in one go at is MX Toolbox. They also offer a free notification service, or a more advanced paid service.

Maildir delivery method

This should have been set in the package configuration above, but if not it can be set manually as follows. The primary advantage of maildirs is that multiple applications can access the same Maildir simultaneously without requiring any kind of locking whatsoever, but aside from this, Maildir format is more efficient than mbox, for more information see mbox-vs-maildir. Check the /etc/exim4/update-exim4.conf.conf and append dc_localdelivery='maildir_home' if it doesn't already exist.

Main configuration for multiple domains

You may want to handle mail for a few domains on one server, in which case some generic usernames like "accounts" will conflict with the local user names, or with the same names used by other domains. This is based on Blair Harrison's method from this Waikato LUG article which adds virtual domain support to Exim4. This allows any incoming email address to be mapped to any other internal user mailbox, or external email address.

The main configuration changes to be made are in /etc/exim4/exim4.conf.template (or in /etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs if you're using split-file config). Here's a summary of the main settings we use at the top of this file, these are explained in detail below along with their associated specific configurations throughout the file.

primary_hostname = organicdesign.co.nz
message_body_newlines = true
message_body_visible = 500000
daemon_smtp_ports = 25 : 587 : 2525
system_filter = /var/www/tools/exim-copy-to-sent
system_filter_directory_transport = copy_to_sent
system_filter_pipe_transport = copy_to_sent_pipe
helo_allow_chars = _
MAIN_TLS_ENABLE = true
IGNORE_SMTP_LINE_LENGTH_LIMIT = true
CHECK_RCPT_SPF = true

Next, adjust the local_domains setting to the following:

domainlist local_domains = @ : @[] : localhost : partial-lsearch;/etc/exim4/virtual.domains


Next go to the section starting with "real_local" and add a new section as follows (if in split configuration, this section should be added in a new file called /etc/exim4/conf.d/router/300_exim4-config_virtual)

virtual:
	driver = redirect
	allow_defer
	allow_fail
	data = ${lookup{$local_part@$domain}lsearch*@{/etc/exim4/virtual.users}}
	domains = partial-lsearch;/etc/exim4/virtual.domains
	retry_use_local_part


Now you will need file called /etc/exim4/virtual.domains that is simply a list of all the domains for which you accept mail in the following format:

example.com
example.net


Don't forget to restart the mail server,

/etc/init.d/exim4 restart

Setting up mail users

To add or modify the mail users, edit the /etc/exim4/virtual.users file. Following are some examples to highlight some of the most popular scenarios:

regularuser@example.com     : localuser@localhost
forwardinguser@example.com  : someuser@example.org
foo@example.com             : :fail: Foo no longer lives here.
bar@example.com             : :blackhole:
*@example.com               : catchall1@localhost

regularuser@example.net     : localuser2@localhost
forwardinguser@example.net  : someuser2@example.org
*@example.net               : catchall2@localhost
  • Note: There is no need for Exim to be restarted after changing the virtual.users or virtual.domains files.

Testing delivery

Before moving on to SMTP or IMAP configuration, you should test that email sent to your local users/domains are being delivered properly. The mails will be delivered to the users home directories in ~/Maildir/cur/. You can cat the files to see the textual content of the messages stored there. You can send an email from the command line:

/path/to/exim -v 'user@domain'
message here
^D ( control D )


Another important thing to check before considering the system ready for production use is to send a test message to another mail server that you have access to the logs on. This way you can see exactly what other mail servers are seeing and ensure that everything matches up as it should. Here's an example of a remote server's Exim log entry showing a message received from the Organic Design server.

2013-04-25 11:37:49 1UVPB7-0008Q2-J8 <= aran@organicdesign.co.nz H=organicdesign.co.nz [37.17.226.20]
    P=esmtps X=TLS1.0:RSA_AES_256_CBC_SHA1:32 S=944 id=51795B7E.2020802@organicdesign.co.nz
2013-04-25 11:37:49 1UVPB7-0008Q2-J8 => nad <nad@foo.com> R=local_user T=maildir_home
2013-04-25 11:37:49 1UVPB7-0008Q2-J8 Completed

The most important part to look at here is the H value. The IP in the square brackets is the real IP address that the message has been received from, and the domain directly following the H is the name found after doing a reverse-DNS lookup on the IP. If the name is surrounded by round brackets it means that no reverse-DNS entry was found, and the name that the server is publishing in response to an SMTP HELO or EHLO command (exim's primary_domain value) is being used instead. If there's both a reverse_DNS name and also a bracketed name, then this means the reverse-DNS name is not the same as the name returned from the HELO command. It's very important that there be a reverse-DNS entry and that it exactly match the HELO domain because otherwise many mail servers will reject the message as spam. More information about the Exim log format of received messages can be found here in the Exim documentation.

To test what you server's domain is in the ELHO response connect via SMTP using telnet DOMAIN 25 then type ELHO. If you're connecting from a consumer connection you may need to use the 2525 port since many of them block 25 to prevent spammers.

Setting up a secure SMTP server with authentication

Since many people in the organisation may be working with laptops in the field, it's useful for the server to run it's own SMTP server so that settings don't need to be changed when moving from one ISP to another for internet access. The SMTP server will need to use authentication and encryption so that it can't be used for spamming.

Setting up a secure SMTP server with authentication is quite simple using Exim4's plaintext authenticator with TLS encryption.

SMTP Certificates

First add the following to the first section of /etc/exim4/exim4.conf.template to define the ports we'll listen on. Using port 587 is not only important for accepting encrypted connections, but also because many ISP's restrict outgoing traffic on port 25 to their own SMTP server only. All other TLS options are added under the main/03_exim4-config_tlsoptions section.

MAIN_TLS_ENABLE = true
daemon_smtp_ports = 25 : 587

Self-signed: Using a self-signed certificate, we prefer to use a wildcard domain for the common name such as *.organicdesign.co.nz so that it's possible for different sub-domains to access the service but get handled differently, then generate the certificate with the /usr/share/doc/exim4-base/examples/exim-gencert command.

LetsEncrypt: A better way is to use your LetsEncrypt certificate instead of a self-signed one by setting the MAIN_TLS_CERTIFICATE to the fullchain.pem file and MAIN_TLS_PRIVATEKEY to the privkey.pem file, and ensuring the files are both accessible by Exim (Note: the certs are symlinks so the target needs to be readable by Exim, and also all directories in the path need to be executable by Exim to be allowed to follow symlinks). For example:

MAIN_TLS_CERTIFICATE = /etc/organicdesign/ssl/fullchain.pem
MAIN_TLS_PRIVATEKEY = /etc/organicdesign/ssl/privkey.pem

Note: these certs must be readable by the Debian-exim user, we've found that we need to chgrp the private key to Debian-exim and set its mode to 640, and that symlinks cab cause trouble. We gave a deploy-hook for our LetsEncrypt certificate renewal that puts a copy of the files into /etc/organicdesign instead of using symlinks.

Cipher and protocol selection

We should also restrict the protocols and ciphers to mitigate POODLE and BEAST attacks etc, the available options are shown in the Exim manual here and here and a good guide here, and we use the following selection:

tls_require_ciphers = SECURE256:+SECURE128:-VERS-TLS-ALL:+VERS-TLS1.2:-RSA:-DHE-DSS:-CAMELLIA-128-CBC:-CAMELLIA-256-CBC
  • Note: tls_require_ciphers replaces gnutls_require_kx, gnutls_require_mac and gnutls_require_protocols which are all obsolete now.

Using the native Linux user accounts and passwords

To use the native Linux accounts for SMTP authentication, the /etc/shadow file must be made accessible to the Debian-exim group:

chgrp Debian-exim /etc/shadow
chmod g+r /etc/shadow

Next find the authenticators section containing the string "PLAIN" and add a similar entry as follows, removing any other entry with the public name of PLAIN or LOGIN:

plain:
	driver = plaintext
	public_name = PLAIN
	server_condition = "${if crypteq{$auth3}{${extract{1}{:}{${lookup{$auth2}lsearch{/etc/shadow}{$value}}}}}{1}{0}}"
	server_set_id = $auth2
	server_prompts = :
	server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}

If you'd like to use a global username and password for your SMTP authentication instead of the native user accounts, use the following server_condition instead replacing the USER and PASSWORD with the global authentication you want all clients to use.

server_condition = "${if and {{eq{$auth2}{USER}}{eq{$auth3}{PASSWORD}}}{1}{0}}"

Test sending mail to an external domain

Restart the exim4 server and the server should be ready to accept SMTP requests!

To set up the clients use the user name and password defined above, smtp.organicdesign.co.nz for the server, 587 for the port and TLS for security.

To test that the SMTP is working and is not accessible to spammers, testing sending out emails to non-local recipients (as delivery to local domains is unrestricted). Try sending without authentication to ensure that it fails. Also test sending to a local domain from another SMTP server (such as your ISP's one) to ensure that normal delivery is working correctly.

Copying emails into the Sent folder

Many Thunderbird users have been having trouble with the "copying to sent folder" process freezing or being extremely slow. Rather than have every mail be effectively sent twice, once too the recipient and again to the sent folder, it's a lot more efficient to have the server make a copy in the user's Sent folder instead.

Web-mail note: If this is set up, you should also configure your web-mail to not place copies in the Sent folder. Our method of doing this is shown below in the RoundCube web-mail section.

To do this we need to set up an Exim system filter which begins by adding the following lines to the beginning of the configuration where the port and other OD settings are put.

system_filter = /var/www/tools/exim-copy-to-sent
system_filter_directory_transport = copy_to_sent
system_filter_pipe_transport = copy_to_sent_pipe


Next we need to add the two transport that were specified as the system filter transport above. I put this after end transport/30_exim4-config_mail_spool in the configuration and it's defined as follows:

copy_to_sent:
    driver = appendfile
    delivery_date_add
    envelope_to_add
    return_path_add
    group = Debian-exim
    user = ${if match {${local_part:${lookup{$sender_address_local_part@$sender_address_domain}lsearch*@{/etc/exim4/virtual.users}}}}{(.+)}{$1}{Debian-exim}}
    mode = 0660
    maildir_format
    directory = /home/${local_part:${lookup{$sender_address_local_part@$sender_address_domain}lsearch*@{/etc/exim4/virtual.users}}}/Maildir/.Sent/
    create_directory

copy_to_sent_pipe:
    driver = pipe
    user = Debian-exim
    group = Debian-exim


The filter itself is in our tools repo here and so will be available in the file system at /var/www/tools/exim-copy-to-sent, it has the following content:

if
    $sender_address_local_part is not "root"
and 
    "${if def:h_X-Spam-Status {def}{undef}}" is "undef"
and
    "${if match {${lookup{$sender_address}lsearch{/etc/exim4/virtual.users}}}{@localhost}{yes}{}}" is "yes"
then
    unseen save $home/Maildir/.Sent/
    unseen pipe "/var/www/tools/copy-to-sent.pl \"$recipients\""
endif

This filter saves the messages to the user's Sent folder if three conditions apply:

  • The message is not from the server itself
  • The spam headers have not been added yet (the filter will run both before and after they're added)
  • The message is from a local account (i.e. not an incoming message)

One difficulty with using a system filter is that the BCC header is stripped by the time the message arrives at the system filter, but it's quite important for this header to be present in the Sent folder so that users can see who they sent the message to (also it's very useful for sending to a private list and then using the "Edit as new" feature to easily send another message to the same list). The header can be added back again using the $recipients variable which contains the entire list of recipients from all headers including BCC.

Another issue is that the messages are created as unread and Exim doesn't have any commands for changing the unread status as that's really a job for the mail client, not the transfer agent.

To get around these problems I've created the copy-to-sent.pl script that detects which file in the user's Sent/new Maildir folder is the one just added by checking the headers of all new messages and checking which has the new message ID which Exim sets in the MESSAGE_ID environment variable. The script then adds a Bcc header to the new message containing all the recipients that are not present in the To or Cc headers, and marks the message as read.

The system_filter_pipe_transport value in the Exim configuration needs to be set and an appropriate transport created for it before pipe commands are allowed to execute in system filters.

There is still one issue with this method currently which is that the complex expansion of the sender address into a local user has to be done twice because I can't figure out how to cache the result for later use. Perhaps the directory directive is not even needed and something else can be used instead.

LAN based SMTP servers

Often clients wish to run a local mail server on their LAN with the reasoning that they'd like all connection to their mail server to be extremely quick, to be independent of internet connectivity so that they can still access their mail and send mail internally independently of Internet connectivity. One complication with this is that some of the client computers on the LAN are laptops which must still be able to connect from outside the LAN from arbitrary Internet connections without any change of settings.

Here are some limitations and notes:

  • Most ISP's block outgoing traffic on port 25 unless it's to their own SMTP server
  • SSL certificate's domains must match the request even if self-signed
  • Our SMTP connections require authentication using Unix account details

Our solution is to set clients up using smtp.organicdesign.co.nz as their outgoing server using a company-wide user which is set up on the OD server as a zero-privilege user. This works for the external situation, but to allow local mail to be routed directly independently of Internet requires that the local DNS server be set up with an organicdesign.co.nz zone to be authoritative for just the smtp sub-domain but forward everything else on. This will make the local exim instance the first stop for all internal mail sending requests, but will deliver according to the usual rules for all non-local recipients.

DoveCot IMAP server

We use DoveCot for our IMAP. DoveCot responds to mail client requests by retrieving the mails from file and returning them to the client. It does not deal with the receiving the mails or storing them to disk.

We go with a configuration which is as close to default as possible and gives us both POP3 and IMAP services which both work with either TLS or SSL but not plain text. The users are by default the native linux users and passwords on the system.

Our entire /etc/dovecot/dovecot.conf configuration file can be replaced with the following extremely minimal one since nearly everything we use is default and we only use IMAP with local UNIX accounts. SSL is required for incoming connections but unencrypted connections are supported locally so that the webmail and other local applications can access the mail without requiring any SSL configuration. For more detail about the configuration format, see their wiki.

  • Note1: this configuration is for Dovecot 2.x, if you're still using 1.x check the history if this article and look at the revision prior to the comment about changing to Dovecot 2.x
  • Note2: if you have trouble with your client having frequent login trouble, try adding mail_max_userip_connections = 100
  • Note3: You can use your LetsEncrypt certificate instead of a self-signed one using the fullchain.pem and privkey.pem files and ensuring they're accessible by Dovecot
  • Note4: I'm using Artem Sidorenko's recommended SSL cyphers from here, except that we don't disable SSLv2 since it's was removed completely from OpenSSL.
log_path = /var/log/dovecot.log

protocols = imap
protocol imap {
        listen = 127.0.0.1:143
        ssl_listen = *,[::]:993
}

mail_location = maildir:~/Maildir
maildir_very_dirty_syncs = yes

userdb {
        driver = passwd
}

passdb {
        driver = pam
}

ssl = required
ssl_prefer_server_ciphers = yes
ssl_cipher_list = EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:
                  EECDH+aRSA+RC4:EECDH:EDH+aRSA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4
ssl_protocols = !SSLv3                        # SSLv2 has been removed from OpenSSL so is not even a valid name to disable
ssl_cert = </etc/dovecot/dovecot.pem          # you can also use your LE cert: /etc/letsencrypt/live/..../fullchain.pem
ssl_key = </etc/dovecot/private/dovecot.pem   # you can also use your LE cert: /etc/letsencrypt/live/..../privkey.pem

Spam Assassin

First install the spamassassin and spamc packages, then edit the configuration file /etc/default/spamassassin and set the ENABLED option set to 1. It's also a good idea to set the CRON option to 1 as well so that the rules are kept up to date on a daily basis. Remember to start spamassassin after enabling it; The only configuration you may want to change from the default is the required_scor setting in /etc/spamassassin/local.cf, the default is 5, but this can probably be dropped a bit to filter out more spam without loosing any real messages which are rarely even above zero.

service spamassassin start


Check you have no Perl modules missing with the following command and install them with cpanm if necessary:

spamassassin -D --lint 2>&1 | grep -i failed


The exim4 configuration needs to be adjusted which is in the /etc/exim4/exim4.conf.template file because our servers use the monolithic configuration method.

Add the following block at the end of the router/800_exim4-config_maildrop section:

spamcheck_router:
	no_verify
	check_local_user
	condition = "${if and { {!def:h_X-Spam-Flag:} {!eq {$received_protocol}{spam-scanned}}} {1}{0}}"
	driver = accept
	transport = spamcheck_transport


Add the following block at the end of the transport/30_exim4-config_remote_smtp_smarthost section:

Note: be sure to check that the spamc binary is in the location specified, and that it is running.

# 30: Spamcheck transport
spamcheck_transport:
	debug_print = "T: spamassassin_pipe for $local_part@$domain"
	driver = pipe
	command = /usr/sbin/exim4 -oMr spam-scanned -bS
	use_bsmtp
	transport_filter = /usr/bin/spamc
	home_directory = "/tmp"
	current_directory = "/tmp"
	user = Debian-exim
	group = Debian-exim
	return_fail_output
	message_prefix =
	message_suffix =


At this point, after exim4 is restarted, all messages should have extra headers added by spamassassin for example:

X-Spam-Checker-Version: SpamAssassin 3.1.7-deb (2006-10-05) on organicdesign.co.nz
X-Spam-Level: ***********************
X-Spam-Status: Yes, score=23.9 required=5.0 tests=NO_DNS_FOR_FROM,
	RAZOR2_CF_RANGE_51_100,RAZOR2_CF_RANGE_E4_51_100,
	RAZOR2_CF_RANGE_E8_51_100,RAZOR2_CHECK,RCVD_IN_BL_SPAMCOP_NET,
	RCVD_IN_XBL,UNRESOLVED_TEMPLATE,URIBL_JP_SURBL,URIBL_OB_SURBL,
	URIBL_SBL,URIBL_SC_SURBL,URIBL_WS_SURBL autolearn=spam 
	version=3.1.7-deb

Moving spams to another folder automatically

Mails which have been marked as spam by spamassassin can be automatically moved into a specific mail folder by using an exim filter. First you may have to edit the exim4 configuration again and uncomment the allow_filter option in the 600_exim4-config_userforward section. We haven't had to do this so far because it is currently enabled by default.

To make a new filter, create a file in your home directory called .forward and ensure it is owned by the user in question, then add a rule such as in the following example from our server which forwards the spams straight into the Trash folder.

# Exim filter
if
   $h_X-Spam-Status: CONTAINS "Yes"
then
   save $home/Maildir/.Trash/
   finish
endif
  • Important: do not change the comment in the first line!
  • Important: use \40 for spaces in target directory name (escaped octal!)
  • Note: You can use the make-forwards.pl to create them automatically for all users who don't already have them

Teaching spamassassin

Spamassassin comes with a script called sa-learn which is designed to parse mail directories with the information that the messages within them are either spam or "ham" (not spam). We have a Perl script called learn-spam.pl which resides in the /var/www directory and is run each morning. It scans all our user's Inbox/Spam and Inbox/Not Spam mail folders into which users should place their incorrectly assigned spam and non-spam messages. Note: the messages are removed after being processed, so the messages in your Not Spam folder should be copies, whereas those in the Spam folder can just be moved since you won't need them.

The sa-learn man page has some good information about Spamassassin's Bayesian learning mechanism, the following snippet is from there.

Learning filters require training to be effective. If you don't train them, they won't work. In addition, you need to train them with new messages regularly to keep them up-to-date, or their data will become stale and impact accuracy.

You need to train with both spam and ham mails. One type of mail alone will not have any effect.

Note that if your mail folders contain things like forwarded spam, discussions of spam-catching rules, etc., this will cause trouble. You should avoid scanning those messages if possible. (An easy way to do this is to move them aside, into a folder which is not scanned.)

If the messages you are learning from have already been filtered through SpamAssassin, the learner will compensate for this. In effect, it learns what each message would look like if you had run "spamassassin -d" over it in advance.

Another thing to be aware of, is that typically you should aim to train with at least 1000 messages of spam, and 1000 ham messages, if possible. More is better, but anything over about 5000 messages does not improve accuracy significantly in our tests.

To check how many spams and hams the system has processed as well as some other internal data, use sa-learn --dump magic, here's the output of our system on 24 Dec 2008 which clearly shows that we haven't been feeding it enough ham! It shows that if we feed it ham at the same rate as spam, then it would start becoming useful in about another couple of weeks :-)

# sa-learn --dump magic
0.000          0          3          0  non-token data: bayes db version
0.000          0        733          0  non-token data: nspam
0.000          0         22          0  non-token data: nham
0.000          0      29962          0  non-token data: ntokens
0.000          0 1228202529          0  non-token data: oldest atime
0.000          0 1230047836          0  non-token data: newest atime
0.000          0          0          0  non-token data: last journal sync atime
0.000          0          0          0  non-token data: last expiry atime
0.000          0          0          0  non-token data: last expire atime delta
0.000          0          0          0  non-token data: last expire reduction count


Here's a couple of lines of Perl which extract and print the number of spams and hams from the output shown above. I added this to our daily housekeeping script so that it appears in our recent changes each day.

$_ = `sa-learn --dump magic`;
print m/\s([1-9]+\d*).+?am[\x00-\x1f]+.+?([1-9]+\d*).+?am[\x00-\x1f]+.+?([1-9]+\d*).+?ns$/m
      ? "$1 spams and $2 hams have been processed with $3 tokens."
      : "ERROR";


Our training script, /var/www/learn-spam.pl, is run daily from the crontab and contains the following:

#!/usr/bin/perl

# Handle false positives
for (glob "/home/*/Maildir/.INBOX.Not\\ Spam/[cn]??") {
        s/ /\\ /;
        print qx "sa-learn --ham $_";
        qx "rm -fr $_/*";
}

# Handle false negatives
for (glob "/home/*/Maildir/.INBOX.Spam/[cn]??") {
        s/ /\\ /;
        print qx "sa-learn --spam $_";
        qx "rm -fr $_/*";
}


To test if the script is working properly, run it manually from the shell at such a time as you know there are spams to be processed and you should see messages stating that new tokens have been learned such as in the following example output.

archive-iterator: readdir found no mail in '/home/foo/Maildir/.INBOX.Spam/cur' directory
Learned tokens from 0 message(s) (0 message(s) examined)
Learned tokens from 16 message(s) (16 message(s) examined)
archive-iterator: readdir found no mail in '/home/bar/Maildir/.INBOX.Spam/cur' directory
Learned tokens from 0 message(s) (0 message(s) examined)
Learned tokens from 7 message(s) (7 message(s) examined)
Learned tokens from 6 message(s) (6 message(s) examined)
archive-iterator: readdir found no mail in '/home/baz/Maildir/.INBOX.Spam/new' directory
Learned tokens from 0 message(s) (0 message(s) examined)
Learned tokens from 2 message(s) (2 message(s) examined)
archive-iterator: readdir found no mail in '/home/buz/Maildir/.INBOX.Spam/new' directory
Learned tokens from 0 message(s) (0 message(s) examined)

Backing & restoring up the Bayesian database

Since the Bayesian database takes a month or so to build up to a useful state, it's important to be able to back it up in case it gets corrupted or if the server needs to be rebuilt. Such a backup can also be used in the installation process of any new server so that there's no need to go through the initial learning process for each new installation.

To back up the database use sa-learn --backup > mybackup.txt and to restore it again, first clear any existing data if there is any with sa-learn --clear and then use sa-learn --restore mybackup.txt to restore from the backup file.

The actual data is stored in ~/.spamassassin in files called bayes_tok and bayes_seen, so if there's a problem and they dissapear you can force the location sa-learn looks for its data with the --dbpath parameter then do a backup and restore without the parameter.

Updating Spam Assassin's rules

In addition to relying on the Baysean database, it's also useful to run the sa-update script every month or so which downloads refined rules from the Spam Assassin channel. GPG keys are used to ensure that the rules are coming from the correct source, but I haven't got these working yet. Following is the command to run the update without checking the channel for authenticity since it won't replaced the current rules with the downloaded ones if this is not specified. The spamd daemon must be restarted after the rules have been updated.

sa-update --nogpg
service spamassassin restart

Set up a local non-forwarding caching DNS server

Note: This step has a major impact on SpamAssassin's effectiveness!

DNS block lists (DNSBL) are an important tool for blocking spam because the spammers use a variety of different domains all the time to try and combat spam filters (see using network test in general). The DNS block lists maintain a real-time list of spammer's domains which can be queried using DNS. Spam Assassin is configured to use many different DNSBL services by default, but the requests for the data may be blocked if the DNS server you're using is not paying for the DNSBL service and is making too many requests to it. You can easily see if your DNSBL requests are being blocked because you'll see URIBL_BLOCKED in your X-Spam-Status message headers, e.g.

X-Spam-Status: No, score=-1.4 required=5.0 tests=BAYES_00,HTML_IMAGE_ONLY_32,
	HTML_MESSAGE,T_DKIM_INVALID,T_RP_MATCHES_RCVD,URIBL_BLOCKED,URI_NOVOWEL
	autolearn=ham version=3.3.2

To get around this problem you simply need to set up your own local caching non-forwarding name server (see also understanding DNS forwarding). See Configure DNS for installation and configuration details.

Your X-Spam header should be looking more like this:

X-Spam-Status: Yes, score=11.0 required=5.0 tests=ADVANCE_FEE_2_NEW_MONEY,
	BAYES_00,HTML_MESSAGE,LOTS_OF_MONEY,MIME_HTML_ONLY,RCVD_IN_BRBL_LASTEXT,
	RCVD_IN_XBL,RDNS_NONE,URIBL_BLACK,URIBL_DBL_SPAM,URIBL_SBL,URIBL_SBL_A,
	URIBL_WS_SURBL autolearn=no autolearn_force=no version=3.4.0

Domain and IP address configuration

Following are some important configurations that should be done on the mail-server's domain and IP address. If these aren't done then emails sent by the server are more likely to be routed to the recipient's junk folder, or may even be rejected by the remote server.

Reverse DNS

Any site that sends emails should have reverse DNS correctly configured. Most mail-servers will do a reverse lookup on the sending IP address and ensure it matches the senders specified domain. Note that it's best if this domain has a host name part, not just the fully-qualified domain name part, for example mail.mydomain.com.

This is not done by the domain registrar, it's done by the company hosting the server (the IP address owner), sometimes they include the ability to set it in the server management interface. If not, raise a support ticket asking them if they can set up a PTR record for the server's IP pointing to the domain specified in /etc/mailname or Exim's primary_domain setting.

You can find out more about what reverse DNS is and why it's important here. To check the reverse DNS of any IP address simply do the host command as you would for a domain, e.g.

host 1.2.3.4

Block-lists

You should also check MX Toolbox to see if your mail-server IP is listed in any block-lists. If it is then that page will tell you which ones and include links on where to go to get the IP removed from the lists.

TXT records

There are a number of different mechanisms for verifying senders that are based on TXT records in the sending domain. These TXT records should be included in all domains that can be used as from addresses by the MTA.

DKIM (Domain Keys Identified Mail)

Setting up DKIM allows outgoing messages to be signed and the signature verified by receiving servers by looking up the public key in a TXT record associated with the domain.

Create a key pair using openssl as follows:

mkdir /etc/organicdesign/dkim
cd /etc/organicdesign/dkim
openssl genrsa -out dkim-private.pem 2048
openssl rsa -in dkim-private.pem -out dkim.pub -pubout -outform PEM
chown root:Debian-exim dkim-private.pem
chmod 640 dkim-private.pem


We like to keep the selector in a file too, so that our scripts that update DNS records can access all the DKIM information in one place.

echo "selector: YOUR_SELECTOR" > dkim-selector

You need to add the DKIM settings to you Exim4 configuration, they can go right at the top with the other specifics added. Setting DKIM_STRICT means that messages won't be sent and errors will be logged if the outgoing messages cannot be signed for some reason, for example if Exim can't read the private key. Don't forget to check the paniclog as well.

DKIM_CANON = relaxed
DKIM_SELECTOR = ${lookup{selector}lsearch{/etc/organicdesign/dkim/dkim-selector}}
DKIM_DOMAIN = primary_hostname
DKIM_PRIVATE_KEY = /etc/organicdesign/dkim/dkim-private.pem
DKIM_STRICT = true

Then the record itself will be a TXT record with host name of:

YOUR_SELECTOR._domainkey

and the content of your public DKIM key included for example:

v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC30aRx6rlDA7Lkhs/4XJMo
1AtuW8LoBrjo6RZH3yS7nC9EgqV5ntFIzQyCo88hNBz72XwwFAAGKuCVIwcxV06lAHWnUTt+ZyjJlP
9JH76ZJu9vUTaHw753IY3SZR+xEnJuyBr/LZknAEFqHuDP7V3+B6SWuBElSFFnImnP7oeMQQIDAQAB

Note: The key should be a single line in the TXT record. I've broken it here for clarity, and also the .pem file is multi-line as well.

You can test your DKIM signature at dkimvalidator.com.

SPF (Sender Policy Framework)

A lot of mail-servers now require senders domains to have an SPF record (official site). I think that the following is a good clear choice to use, but you'll need to update the record if the server moves to a new IP address.

v=spf1 ip4:SERVER-IP4 ip6:SERVER-IP6 -all

This simply ensures that mail can only be sent from your domains if it comes from your mail server's IP address. Before we had a version that used a PTR rule to make an IP independent rule, but reverse lookups are very resource intensive and cause mail sending to be slow down too. The record is simply a TXT record in the domain on the @ prefix for a normal domain, or on the appropriate host if the mailname/rDNS of the server is a sub-domain.

Note: SPF checking for Exim4 has been supported since version 4.52 and in recent version can be enabled simply by setting the CHECK_RCPT_SPF flag and installing the spf-tools-perl package via apt.

dkimvalidator.com also checks SPF records too.

DMARC (Domain-based Message Authentication, Reporting and Conformance)

DMARC is a protocol for adding TXT record with the host name of _dmarc to the sending domains DNS record that instructs receiving servers on what to do with messages that do not have corresponding SPF records or DKIM signatures. We have the following DMARC record which simple instructs receivers to reject messages that do not have DKIM signatures, or matching SPF rules.

The following advises the receiving MTA to reject any messages which fail either SPF or DKIM because the true server has both enabled. Use none instead of reject until you've confirmed that your SPF and DKIM configurations are functioning properly. For all the parameters and values see here.

v=DMARC1; p=reject

You can test by sending a message to gmail and checking the headers of the message in there. Google adds headers indicating its authentication tests and results. Got to the message and click the menu icon at the top right of the opened message content, then select "show original". You'll see a line like the following:

Authentication-Results: mx.google.com;
   dkim=pass header.i=@organicdesign.co.nz header.s=201809191 header.b=YuECVckW;
   spf=pass (google.com: domain of aran@organicdesign.co.nz designates 213.5.71.227 as permitted sender) smtp.mailfrom=aran@organicdesign.co.nz;
   dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=organicdesign.co.nz

For more detailed results covering everything, you can test your mail sending at mail-tester.com which is a pay service, but gives you three tests per day for free.

Mail-test-results-1.jpg
Mail-test-results-2.jpg

Filter files

Exim uses a special scripting language that can be placed in user's .forward files to specify more complex forwarding and filtering rules. If the .forward file is using the Exim scripting language it must have # Exim filter on the first line. The default filter file we use is the one shown above in the Spamassassin configuration, but the file can be extended in numerous way to cater for the specific needs of individual users.

Auto-responder / Vacation message

See the extended .forward filter here, and has been tested successfully. This method extends the existing forward filter, but unfortunately it raises an error if the .vacation.msg file isn't present, and it can only be required from within the exim configuration, not from within the filter itself, and there seems to be no condition for testing file existence.

Conditionally delivering messages to specific folders

Sometimes we need to prevent the inbox getting too cluttered by having messages from some very prolific senders or lists get automatically filed. Here's a simple example.

# Exim filter
if $header_from matches "Harry McProlific" then
    save $home/Maildir/.Lists.Unread.Harry/
    finish
endif

Note: don't forget the trailing slash or you'll get the "mailbox has too many links" error on delivery

Setting up mailing lists

To create a simple mailing list, you must have the message_body_visible set to a reasonable number and the message_body_newlines directive set to true in /etc/exim4/exim4.conf.template (or in /etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs if you're using split-file config).

message_body_newlines = true
message_body_visible = 5000

Note: this number of lines should be very large if you want to be able to send attachments in the mailing list

Next create a normal email account using the adduser command, and add an entry in /etc/exim4/virtual.users for the new address. Then create a .forward file (owned by the user) in their home directory as shown in the following example:

# Exim filter
if $h_subject contains "[Our List]" then
	seen mail
	from $reply_address
	reply_to "Our List<our-list@foo.bar>"
	subject $h_subject
	text $message_body
	to "Our List<our-list@foo.bar>"
	bcc "john@foo.bar,mary@foo.bar"
	extra_headers "Content-type: $h_content-type\nContent-transfer-encoding: $h_Content-transfer-encoding"
else
	seen mail
	from $reply_address
	reply_to "Our List<our-list@foo.bar>"
	subject "[Our List] $h_subject"
	text $message_body
	to "Our List<our-list@foo.bar>"
	bcc "john@foo.bar,mary@foo.bar"
	extra_headers "Content-type: $h_content-type\nContent-transfer-encoding: $h_Content-transfer-encoding"
endif

The name of the list and its email address is added to the reply-to field, and the list of recipients is in the bcc field. Later these directives can be added to the files automatically. The to field contains just a dummy address that should be set up to get black-holed.

We also have a MediaWiki extension called EximMailList that can populate a forward file automatically using the email addresses of the wiki users. It should be called from a cron job on the local host, and requires two settings in the LocalSettings.php file to be set, $wgEximMailListName and $wgEximMailListAddress. The crontab entry should be something like this:

5 * * * * root wget -q -O /home/foo/.forward "http://foo.com/wiki/index.php?title=MediaWiki:Common.css&action=eximfilter"

I've used the article title MediaWiki:Common.css to cater for the case of the wiki being locked down, since the CSS article will always be in the white list.

See also

Sending certain messages to multiple recipients

Sometimes it's useful to be able to have some messages that arrive for a specific user send to a number of different people. This is a little different than simple setting up a number of forwarding addresses in the virtual.users file for the address, because in this case we want the rule to only apply to messages that match certain characteristics such as having a specific subject or From address. One example of where this can come in handy is for a company that's subscribed to a paid mailing list and needs all the staff to receive the information.

If you have mail lists set up in the way described in the previous section, then this can be achieved simply by using a slightly modified version of the mail-list filter rules as follows.

# Exim filter
if $h_from contains "updates@expensive-info.com" then
	seen mail
	from $reply_address
	subject $h_subject
	text $message_body
	to "Organic Design staff<manager@organicdesign.co.nz>"
	bcc "manager@organicdesign.co.nz,sales@organicdesign.co.nz,dev@organicdesign.co.nz"
	extra_headers "Content-type: $h_content-type\nContent-transfer-encoding: $h_Content-transfer-encoding"
endif

Delivering emails directly to web-sites or other local programs

Often sites require the ability to respond to incoming emails, for example the site may have the ability for users to reply to site notifications sent by email from the site. We've previously used the EmailToWiki extension for this, but the extension requires that the emails first be delivered to a mailbox and then a cron-job polls the mailbox and removes/processes messages using the IMAP or POP3 protocol. This is a very unresponsive and resource consuming approach.

A new script has been made called post-email.pl which allows emails to be delivered directly to a site via HTTP instead of being delivered to a mailbox. This script is invoked via an Exim filter in the recipient email-user's .forward file. The filter is a single line consisting of a pipe command which sends the email content to the specified program in the STDIN stream, the URL that the extracted body text and headers will be posted to is specified as a command-line parameter to the program.

The script extracts the useful headers (id, date, from, to, subject) and the email body from the message and formats them appropriately using the same code as the original EmailToWiki extension used and then posts the data to the URL specified in the Exim filter's pipe command.

The script has a feature allowing the $header_to value from the Exim filter to be used in the URL instead of a normal domain so that it can decide what to do with the message based on the recipient address. The domain part of the email address will be used as the site domain. The following example demonstrates this feature.

# Exim filter
pipe "perl $home/post-email.pl \"http://$header_to:/index.php\""
  • The post-email.pl script, or a symlink to it, is expected to be in the same directory as the filter
  • Explicitly specifying the perl interpreter means there's no need to worry about the script's executable permissions
  • The quotes are needed if the URL contains a query-string with ampersands


This example allows some local addresses to deliver normally while all others go into the pipe:

# Exim filter
if
   $header_to does not match "(info|admin)@"
then
   pipe "perl $home/post-email.pl \"http://www.example.com\""
endif

Notes on Exim Filters

  • Exim doesn't need to be restarted for changes to the .forward or virtual.users files.
  • You can also forward messages to other recipients by adding one or more deliver directives to the filter file.
  • To debug filter problems, stop the Exim4 service and restart on the command line with -bd -d+filter options.
  • Remember that vacation messages are only sent to the same user once per week, and deleting the .vacation.log file does not reset this, see manual for details.
  • the foranyaddress $h_to: ( $thisaddress contains "$local_part@" ) rule wasn't working last time I installed this - needs checking

RoundCube (IMAP-only webmail)

We need to have access to our IMAP folder structures from a browser, we use the RoundCube webmail application for this purpose.

First ensure that the sub-domains which should have webmail access have a rule in the web-server configuration mapping it to the roundcube code base which can be downloaded from here and should be saved to /var/www/domains/webmail. See set up a new domain name for details on mapping a sub-domain to a web application code-base.

Next, go to your.domain/installer and follow their installation procedure which generates the configuration files for you (don't use real passwords as you can change these after you paste the configuration into the real files on the server). Set the default_host option to localhost to prevent the login from having a host input. Use localhost for the server addresses (you may need to use ssl://localhost if non-ssl is denied by the server). You'll need to manually create the MySQL database:

create database roundcubemail;


The installer tests whether the database exists and is writable, then allows you to initialise it with a button. At this point you can also test the IMAP login and SMTP sending.

Manually populating the database

Under some (currently unknown) circumstances the database tables are not created by the web installer, so you may need to manually populate the database by manually logging in to MySQL and creating a database called roundcubemail, and then importing from the shell with the following command:

mysql -u USER -p PASS roundcubemail < /var/www/domains/webmail/SQL/mysql.initial.sql

Once all these are working, go to the root of the webmail domain and login.

Upgrading

To upgrade the Roundcube code-base, download and unpack the new version into a temporary location, change into that directory then run the update script with the location of the current installation as the parameter.

./bin/installto.sh /var/www/domains/webmail/

Note: This process will rebuild the configuration and the hack shown below to prevent outgoing mail being saves to "Sent" will need to be added again. It would be best to check that the same method still works after the upgrade as well, but checking with Firebug that the save-to option in the compose view still uses the same key and value method in its posted data.

Nginx configuration

The web-mail is best forced to HTTPS, so we have some configuration in both the plain and secure server blocks. In the plain block we simply detect if the request domain starts with "webmail", and if so redirect the request to HTTPS. In the secure block we have the same domain-based rule but instead of redirecting, it rewrites the request to the appropriate file system location of the RoundCube code-base.

server {
	listen 80;
	...
	server_name ~^webmail\.;
	rewrite ^ https://$host:443$uri last;
	...
}
server {
	listen 443;
	ssl on;
	...
	if ( $host ~ ^webmail\. ) {
		rewrite ^/$ /webmail/index.php last;
		rewrite ^ /webmail$uri last;
	}
	...
}

Manual Configuration

In our configuration there is only one setting in db.inc.php which is the mysql database connection line, and there are a few settings in the main.inc.php, the default_host is "ssl://localhost", the des_key, the product_name which is "OrganicDesign Webmail" in our case, and finally the mail_domain should be set to the default domain that user-names should be under to make a valid return address.

Disabling the copying to Sent folder

If your server already handles the copying of sent messages into the "Sent" folder (for example using the procedure shown above), then you'll want to stop RoundCube from also making a copy of the message there. This is easily by un-setting the "Sent" option in RoundCube's "special folders" settings page, but if you do this then the "Sent" folder will be listed alphabetically amongst all the other normal folders. So it's preferable to keep treating "Sent" as a special folder, but disabling the copying functionality.

In RoundCube's "compose message" page there is a drop-down list that allows the message to be saved in a different folder, or no folder at all. If the "Sent" folder is special, then this drop-down list will be set to "Sent" by default. All we need to do to disable the copying-to-sent functionality is to add the following snippet into the beginning config.inc.php configuration file so that a "Sent" target for newly sent messages is changed to no target.

if( array_key_exists( '_store_target', $_POST ) && $_POST['_store_target'] == 'Sent' ) {
	$_POST['_store_target'] = '';
}

Client setup

Any standard mail client such as Thunderbird or Outlook should connect with no trouble. We don't recommend using any Microsoft products at all, but if you had no choice, then the Set up an IMAP account on Outlook 2007 procedure may be of some help.

Privacy: You should set up PGP so you can communicate privately. This won't work or webmail, but works on desktop clients, or on Android you can use OpenKeyChain.org

Incoming mail settings (IMAP)

We use the IMAP protocol, but it is possible to use both POP3 and IMAP together for the same account because both are simply different protocols for accessing the same mail. There's probably no useful purpose to do that, but if you do then make sure the client settings for the POP setup is configured to leave the messages on the server, otherwise any messages downloaded from the inbox will be removed and will therefore not be available from the IMAP inbox folder either. Note that any messages that have been moved out of the inbox and into another folder using the IMAP protocol will no longer be available for download by the POP3 protocol because it only interacts with the inbox folder which is the root mailbox.

The settings for OrganicDesign IMAP access are: od.nz on port 993 (default) using SSL encryption.

Outgoing mail settings (SMTP)

To set the email client to use the organisations SMTP server, go to Account settings/Outgoing server (SMTP) and add a new one called OrganicDesign with server address od.nz, port 587 (default) and STARTTLS security. Use your IMAP login details with normal password authentication method.

You may want to keep your ISP's SMTP server as the default since it will work more quickly being more local and not requiring encryption or authentication, but you can easily make the Organic Design SMTP server the default when working in the field.

Using other servers as clients

Mail is very complex to set up reliably these days (hence this huge procedure!). So if you run a number of servers that all need to have mail sending capability, you most likely don't want to set up rDNS, SPF, DKIM and DMARC for all of them. A far simpler solution is to use a simpler package like msmtp to present itself to the system as an MTA that the sendmail command can be symlinked to, but all it actually does is refers all requests to your main mail server.

First make sure that you don't have any other mail servers installed by default such as Exim4 or Postfix and then install MSMTP.

which sendmail
ls -al /usr/sbin/sendmail
dpkg -l |grep exim
apt remove exim4-daemon-light
apt install msmtp

Make sure that your sendmail command is linked to your new msmtp:

ln -s /usr/bin/msmtp /usr/sbin/sendmail

Create a minimal configuration in /etc/msmtprc with the following settings (based on your own SMTP servers configuration):

account default
syslog LOG_MAIL
maildomain your.remote.smtp.server
host your.remote.smtp.server
auto_from on
port 587
tls on
tls_starttls on
auth plain
user your_smtp_server_user
password *******

And test the commonly required forms of mail sending capability:

echo "testing" | mail -s "mail test" foo@bar.baz
echo "Subject: sendmail test" | sendmail -v "foo@bar.baz"
echo '<?php mail("foo@bar.baz","php test","testing");' | php

Backing up and synchronising Maildirs

nad@nad-laptop:~$ apt-cache search imapsync
 imapcopy - IMAP backup, copy and migration tool
 imapsync - IMAP synchronization, copy and migration tool


For synchoronising the data from another backed up Maildir, use apt-get install maildirsync and the following example syntax:

maildirsync -rvvv Source/Maildir/ Dest/Maildir results-info.bz2


You can also use rsync to maintain a backup of Maildirs on another server without having to copy the entire structure every time. But beware that Maildir uses colons in its filenames which can cause problems with some filesystems. See the rsync article for a solution to this.

Troubleshooting

Stop the exim service and run again from the shell with debugging enabled,

exim4 -bd -d+filter

For more information on the command line options, see chapter 5 of the manual.

suhosin.session.encrypt option value is incorrect

One issue is that on some systems the Roundcube installation procedure gives an error saying that the suhosin.session.encrypt option value is incorrect (it should be disabled) even though it says it is disabled in php.ini. The suhosin settings have their own php.ini configuration file in /etc/php5/apache2/conf.d/suhosin.ini which overrides the settings in the default php.ini. Change the line containing the suhosin.session.encrypt option in this file to disable it (and don't forget to uncomment the line by removing the leading semicolon), then restart the web server and run the Roundcube installer again.

Problems sending to Hotmail/Microsoft

Hotmail have increased their spam security and require a number of steps to be taken for smtp servers to be able to send mail to them without being blocked as spammers. You can de-list blocked IPs at sender.office.com.

.forward files not being processed

The most common reason for this is that the messages that you're expecting to be forwarded do not have a local destination, check that the domain they're under is a local domain, you may need to add it to the dc_other_hostnames setting in /etc/exim4/update-exim4.conf.conf or to your virtual.domains file if you're using the above configuration.

Panic log contains "User 0 set for maildir_home transport is on the fixed_never_users list"

This means that your configuration has required Exim to run as root which it does not allow. The most common reason for this is a local delivery to the root user. Either the domain of the local host should have its email handled by an external server so that the root@yourdomain address does not attempt to be delivered to the local root user, or there should be an alias for the local root user set in /etc/aliases.

Our system has detected that this message does not meet IPv6 sending guidelines regarding PTR records and authentication

This is an error that is often seen in the logs when Exim sends messages to Gmail. You'll need the domain to have reverse DNS setting for the IPv6 address, if your ISP can't provide this, then you can disable SMTP connections over IPv6 by adding the disable_ipv6 directive into the main/02_exim4-config_options section.

## main/02_exim4-config_options
#################################

disable_ipv6

Spammers clogging up the server

Today I had a problem whereby I couldn't send an email due to there being too many connections to the mail server. I've never had this before and it shouldn't happen since we're a very small operation with only twenty or thirty active email users. Using netstat -5 | grep smtp revealed that indeed there were many concurrent SMTP connections and they were all from a couple of IP addresses. I checked the Exim log to see if they were doing anything, but fortunately Exim was rejecting them due to them not having any valid host name associated with them.

2015-12-01 06:38:49 no host name found for IP address 223.240.93.244
2015-12-01 06:38:51 no host name found for IP address 223.240.93.244
2015-12-01 06:38:53 no host name found for IP address 223.240.93.244
2015-12-01 06:38:59 no host name found for IP address 117.68.193.8
2015-12-01 06:39:01 no host name found for IP address 117.68.193.8
2015-12-01 06:39:03 no host name found for IP address 117.68.193.8
2015-12-01 06:39:10 no host name found for IP address 117.68.193.8
2015-12-01 06:39:16 no host name found for IP address 117.68.193.8
2015-12-01 06:39:21 no host name found for IP address 117.68.193.8
2015-12-01 06:39:28 no host name found for IP address 223.240.78.176
2015-12-01 06:39:30 no host name found for IP address 223.240.78.176

But since they're still clogging up the server I needed to block them at a lower level. The only problem is that the IP address keeps changing. So I wrote ip-block.pl which is called every 5 min from the crontab to check the Exim log of recent activity for items of the format shown above, and any IP addresses that reoccur more than ten times are blocked from accessing port 25 by iptables. Use iptables --list to check what filter rules are currently in place.

# iptables --list | grep smtp
DROP       tcp  --  223.240.84.70        anywhere             tcp dpt:smtp
DROP       tcp  --  175.45.186.150       anywhere             tcp dpt:smtp
DROP       tcp  --  114.96.76.14         anywhere             tcp dpt:smtp
DROP       tcp  --  223.240.75.50        anywhere             tcp dpt:smtp
DROP       tcp  --  114.96.70.100        anywhere             tcp dpt:smtp
DROP       tcp  --  94.249.127.124       anywhere             tcp dpt:smtp
DROP       tcp  --  70.45.94.194         anywhere             tcp dpt:smtp
DROP       tcp  --  179.127.166.29       anywhere             tcp dpt:smtp

Message does not meet IPv6 sending guidelines regarding PTR records and authentication

This is an error that is often seen in the logs when Exim sends messages to Gmail. Having an SPF record may fix it, also try adding an IPv6 rDNS record, but if you can'y do that or it still continues, you can disable SMTP connections over IPv6 by adding the disable_ipv6 directive into the main/02_exim4-config_options section.

Warning: Unexpected error in SPF check

This most likely means that you don't have the spf-tools-perl package installed which Exim4 now relies on to do SPF checking.

Rejected EHLO, syntactically invalid argument

The most common reason for this is a hostname having an underscore in it, e.g.

2019-02-13 08:23:25 1gtuVZ-0005gN-9n <= foo@bar.baz H=(_) [2a02:908::b419] P=esmtpsa X=TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128 CV=no A=plain_server:fred S=15273

It would be best to figure out why there's an underscore, or sometimes ONLY an underscore in the hostname, but you can allow the character in the Exim config with the following directive:

helo_allow_chars = _

R=dnslookup T=remote_smtp defer (port 25 blocked - configure Exim to route all requests to a remote server)

This most likely means that the Exim server is running behind a firewall, or on a service that has port 25 blocked. A mail server cannot operate without being able to send on port 25, because all MTA-MTA traffic occurs on port 25 (other mail ports such as 465 or 587 are only used for MUA-MTA traffic). The only solution is to route mail to another external SMTP server. In the following example, we configure the firewalled Exim to route mail to the Organic Design mail server which uses the PLAIN authentication method.

Note: If the firewalled server is only being used for sending then it may be easiest to use something more light-weight than Exim such as MSMTP, see the using other servers as clients section for details about this.

First add a new router before the current dnslookup router (the one that's failing in the log). We use auth_relay transport which we'll create next instead of the normal remote_smtp, because if we don't authenticate, the remote Exim will send all mail to trash as spam. Also be sure to set the port in the route_list to 587 since 25 is blocked (the double colon is not a typo).

smart_route:
     driver = manualroute
     domains = ! +local_domains
     ignore_target_hosts = 127.0.0.0/8
     transport = auth_relay
     route_list = * smtp.organicdesign.co.nz::587
     no_more


Now we need to create the auth_relay referred to in the previous block:

auth_relay:
    driver = smtp
    port = 587
    hosts_require_auth = $host_address
    hosts_require_tls = $host_address

Note that actually the port doesn't seem to matter here, maybe it's been overridden by being specified in route_list?


Then we need to add a new PLAIN authentication method to replace the current one, search for "PLAIN" to find the current (non-commented-out one) and then either comment it out or change its public_name to PLAINFOO or something to avoid there being two authenticators with the same name.

auth_plain:
    driver = plaintext
    public_name = PLAIN
    hide client_send = ^USER^PASS


Finally restart Exim, send a test message and check the log to see if your new items are being used:

2019-02-16 12:38:18 1guzEV-00058l-Kl => foo@bar.baz R=smart_route T=auth_relay H=smtp.organicdesign.co.nz [213.5.71.227] X=TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256 CV=yes DN="CN=organicdesign.co.nz" A=auth_plain C="250 OK id=1guzEY-000263-0q"
2019-02-16 12:38:18 1guzEV-00058l-Kl Completed

See also