# Exim Client Authentication Recipe In the past I had a fairly complex setup with [Exim](https://www.exim.org/), where it routed email out via various smart hosts which use authenticated SMTP. I don't have that setup any more, but the configuration I used is still in place, so I thought I'd share it. I wanted a simple setup where I could drop a list of ip:username:password combinations into a file, and Exim would do the rest. Here's how to do it: First of all, create a file containing the following details about each of your smart hosts. ```text 192.168.0.1/32 : require_ssl="true" type="cram" user="myuser" pass="mypass" 72.14.221.109/32: require_ssl="true" type="login,plain" user="myuser@googlemail.com" pass="mypass" 72.14.221.111/32: require_ssl="true" type="login,plain" user="myuser@googlemail.com" pass="mypass" ``` I think the contents of that file are fairly self explanatory. You must always provide all 4 options, require_ssl, type, user and pass. Make sure there is no whitespace adjecent to the equals symbols, as per my example. At the top of your Exim configuration, add a macro which defines the path to the file you've just created called AUTH_CLIENT_DATA ```text AUTH_CLIENT_DATA = /etc/exim4/client_smtp_auth.txt ``` You'll obviously want to change that to point to the location of your own file. Copy and paste the following list of macros, unchanged, below the AUTH_CLIENT_DATA macro. You don't need to know what these do, they just make the transport and authenticator changes very simple. ```ini AUTH_CLIENT_USERNAME = ${extract{user}{AUTH_CLIENT_SEND_DATA}} AUTH_CLIENT_PASSWORD = ${extract{pass}{AUTH_CLIENT_SEND_DATA}} AUTH_CLIENT_REQUIRED = ${filter{${readfile{AUTH_CLIENT_DATA}{:}}}{match{$item}{\N^\s*\d{1,3}(?:\.\d{1,3}){3}(?:/[0-9]{1,2})?\s*$\N}}} AUTH_CLIENT_REQUIRE_SSL = ${filter{${sg{${filter{<\n${readfile{AUTH_CLIENT_DATA}}}{match{${extract{require_ssl}{$item}}}{\N^(?i)\s*(true|yes|1)\s*$\N}}}}{\N\n\N}{:}}}{match{$item}{\N^\s*\d{1,3}(?:\.\d{1,3}){3}\s*$\N}}} AUTH_CLIENT_SEND_DATA = ${lookup{$host_address}iplsearch{AUTH_CLIENT_DATA}} AUTH_CLIENT_ENABLED_PLAIN = ${if match{${extract{type}{AUTH_CLIENT_SEND_DATA}}}{\N^(?i)(.+,)*plain(,.+)*$\N}{true}{false}} AUTH_CLIENT_ENABLED_LOGIN = ${if match{${extract{type}{AUTH_CLIENT_SEND_DATA}}}{\N^(?i)(.+,)*login(,.+)*$\N}{true}{false}} AUTH_CLIENT_ENABLED_CRAM = ${if match{${extract{type}{AUTH_CLIENT_SEND_DATA}}}{\N^(?i)(.+,)*cram(,.+)*$\N}{true}{false}} AUTH_CLIENT_SEND_CRAM_USER = AUTH_CLIENT_USERNAME AUTH_CLIENT_SEND_CRAM_PASS = AUTH_CLIENT_PASSWORD AUTH_CLIENT_SEND_LOGIN = : AUTH_CLIENT_USERNAME : AUTH_CLIENT_PASSWORD AUTH_CLIENT_SEND_PLAIN = ^AUTH_CLIENT_USERNAME^AUTH_CLIENT_PASSWORD ``` To inform your remote_smtp transport about when it should use authentication and when it should require ssl, add hosts_require_tls and hosts_require_auth options as follows. ```ini remote_smtp: driver = smtp hosts_require_tls = AUTH_CLIENT_REQUIRE_SSL hosts_require_auth = AUTH_CLIENT_REQUIRED ``` In the authenticators section of your Exim config, create three authenticators for plain, login and cram. ```ini CRAM: driver = cram_md5 public_name = CRAM-MD5 client_condition = AUTH_CLIENT_ENABLED_CRAM client_name = AUTH_CLIENT_SEND_CRAM_USER client_secret = AUTH_CLIENT_SEND_CRAM_PASS LOGIN: driver = plaintext client_condition = AUTH_CLIENT_ENABLED_LOGIN client_send = AUTH_CLIENT_SEND_LOGIN PLAIN: driver = plaintext client_condition = AUTH_CLIENT_ENABLED_PLAIN client_send = AUTH_CLIENT_SEND_PLAIN ``` Finally, create a manualroute router to define which messages should be routed out via a smarthost. Here's an example which would route out all non local messages via 192.168.0.1. Because there's an entry for 192.168.0.1 in the file you created at the beginning, remote_smtp would know to use authentication and ssl. ```ini smart_route: driver = manualroute domains = !+local_domains transport = remote_smtp route_data = 192.168.0.1 ``` Job done.