I wanted to block repeated failed attempts against SMTP auth using fail2ban, and the easiest way for to achieve that was to have fail2ban block based on the exim4 mainlog.
2017-09-09 13:19:03 login authenticator failed for (User) [80.82.77.175]: 535 Incorrect authentication data ([email protected]) 2017-09-09 13:24:19 login authenticator failed for (User) [80.82.77.175]: 535 Incorrect authentication data ([email protected]) 2017-09-09 13:29:34 login authenticator failed for (User) [80.82.77.175]: 535 Incorrect authentication data ([email protected]) 2017-09-09 13:34:49 login authenticator failed for (User) [80.82.77.175]: 535 Incorrect authentication data ([email protected])
All the mail users are stored in LDAP, anonymous bind searching is enabled, and I wanted to re-bind to check the authentication. I had to get the DN from the UID supplied by the SMTP connection. This is a pretty common method (used by dovecot, etc) but it's not directly supported by Exim without some extra lookups.
Fortunately exim has all the bits and pieces needed to do the LDAP lookups, it's just a matter of stringing them together.
First we need to find the user's LDAP DN based on a uid lookup of the supplied username:
${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:[email protected]})}}
Connect to the LDAP server on localhost, use a BaseDN of ou=mail,dc=domain,dc=ca and return all attributes using ??, and a subtree search, with a filter (uid=…)
:~$ exim -be > ${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:[email protected]})}} cn=Test User,ou=domain.ca,ou=mail,dc=domain,dc=ca > ^D
And a little sub-shell substitution to check the result with the 'ldapsearch' command
:~$ ldapsearch -xvvv -D$(exim -be '${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:[email protected]})}}') -W uid=test\* dn
Based heavily on the example from the Exim documentation, the result is:
plain: driver = plaintext public_name = PLAIN server_condition = ${if and{{ !eq{}{$auth2} }{ \ ldapauth{\ user="${quote_ldap:${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:$auth2})}}}" \ pass=${quote:$auth3} \ ldap://localhost/} }} } server_set_id = $auth2 server_prompts = : login: driver = plaintext public_name = LOGIN server_prompts = “Username:: : Password::” server_condition = ${if and{{ !eq{}{$auth1} }{ \ ldapauth{\ user="${quote_ldap:${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:$auth1})}}}" \ pass=${quote:$auth2} \ ldap://localhost/} }} } server_set_id = $auth1
This can be tested first by using exim debug:
$ echo -en '\[email protected]\0testpass' | base64 AHRlc3R1c2VyQGRvbWFpbi5jYQB0ZXN0cGFzcw==
auth login uses 2 separate base64 strings for username (4oCcVXNlcm5hbWU6) and password (UGFzc3dvcmQ64oCd)
$ echo -n '[email protected]' | base64 dGVzdHVzZXJAZG9tYWluLmNh $ echo -n 'testpass' | base64 dGVzdHBhc3M=
:~$ exim4 -bhc 66.55.44.33 -d+all ... 220 mail.domain.ca ESMTP Sat, 09 Sep 2017 17:14:32 -0400 17:14:32 22572 smtp_setup_msg entered ehlo mailhost.com ... 250-mail.domain.ca Hello mailhost.com [66.55.44.33] 250-SIZE 52428800 250-8BITMIME 250-PIPELINING 250-AUTH PLAIN LOGIN 250-STARTTLS 250 HELP 17:14:40 22572 SMTP>> 250-mail.domain.ca Hello mailhost.com [66.55.44.33] 17:14:40 22572 250-SIZE 52428800 17:14:40 22572 250-8BITMIME 17:14:40 22572 250-PIPELINING 17:14:40 22572 250-AUTH PLAIN LOGIN 17:14:40 22572 250-STARTTLS 17:14:40 22572 250 HELP auth plain 17:14:53 22572 SMTP<< auth plain 17:14:53 22572 SMTP>> 334 334 ... AHRlc3R1c2VyQGRvbWFpbi5jYQB0ZXN0cGFzcw== ... 17:19:47 22572 expanding: $auth2 17:19:47 22572 result: [email protected] 17:19:47 22572 SMTP>> 235 Authentication succeeded 235 Authentication succeeded quit
And then remotely using gnutls-bin for starttls
:~$ gnutls-cli --starttls --port 25 mail.domain.ca Processed 174 CA certificate(s). Resolving 'mail.domain.ca'... Connecting to '77.66.55.44:25'... - Simple Client Mode: 220 mail.domain.ca ESMTP Sat, 09 Sep 2017 16:34:53 -0400 ehlo mailhost.com 250-mail.domain.ca Hello dhcp.provider.com [44.33.22.11] 250-SIZE 52428800 250-8BITMIME 250-PIPELINING 250-AUTH PLAIN LOGIN 250-STARTTLS 250 HELP starttls 220 TLS go ahead ^d *** Starting TLS handshake ... ehlo mailhost.com 250-mail.domain.ca Hello dhcp.provider.com [44.33.22.11] 250-SIZE 52428800 250-8BITMIME 250-PIPELINING 250-AUTH PLAIN LOGIN 250-STARTTLS 250 HELP auth login 334 4oCcVXNlcm5hbWU6 dGVzdHVzZXJAZG9tYWluLmNh 334 UGFzc3dvcmQ64oCd dGVzdHBhc3M= 235 Authentication succeeded quit
This fail2ban already had a /etc/fail2ban/filter.d/exim.conf file, so I could just use it as filter in a new section of /etc/fail2ban/jail.local
[exim] enabled = true port = all filter = exim logpath = /var/log/exim4/mainlog
You will probably want to have Exim listen to alternate ports (465/587), and allow auth only on encrypted connections:
daemon_smtp_ports = 25 : 465 : 587 tls_on_connect_ports = 465 auth_advertise_hosts = localhost : ${if eq{$tls_cipher}{}{}{*}}
Ref: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_plaintext_authenticator.html