| 1 | ### -*-m4-*- |
| 2 | ### |
| 3 | ### Basic settings for distorted.org.uk Exim configuration |
| 4 | ### |
| 5 | ### (c) 2012 Mark Wooding |
| 6 | ### |
| 7 | |
| 8 | ###----- Licensing notice --------------------------------------------------- |
| 9 | ### |
| 10 | ### This program is free software; you can redistribute it and/or modify |
| 11 | ### it under the terms of the GNU General Public License as published by |
| 12 | ### the Free Software Foundation; either version 2 of the License, or |
| 13 | ### (at your option) any later version. |
| 14 | ### |
| 15 | ### This program is distributed in the hope that it will be useful, |
| 16 | ### but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | ### GNU General Public License for more details. |
| 19 | ### |
| 20 | ### You should have received a copy of the GNU General Public License |
| 21 | ### along with this program; if not, write to the Free Software Foundation, |
| 22 | ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 23 | |
| 24 | ###-------------------------------------------------------------------------- |
| 25 | ### Global settings. |
| 26 | |
| 27 | SECTION(global, priv)m4_dnl |
| 28 | admin_groups = CONF_admin_groups |
| 29 | trusted_groups = CONF_trusted_groups |
| 30 | prod_requires_admin = false |
| 31 | |
| 32 | SECTION(global, logging)m4_dnl |
| 33 | log_file_path = : syslog |
| 34 | log_selector = \ |
| 35 | +smtp_confirmation \ |
| 36 | +tls_peerdn |
| 37 | log_timezone = true |
| 38 | syslog_duplication = false |
| 39 | syslog_timestamp = false |
| 40 | |
| 41 | SECTION(global, daemon)m4_dnl |
| 42 | local_interfaces = <; CONF_interfaces |
| 43 | extra_local_interfaces = <; 0.0.0.0 ; ::0 |
| 44 | |
| 45 | SECTION(global, resource)m4_dnl |
| 46 | deliver_queue_load_max = 8 |
| 47 | queue_only_load = 12 |
| 48 | smtp_accept_max = 16 |
| 49 | smtp_accept_queue = 32 |
| 50 | smtp_accept_reserve = 4 |
| 51 | smtp_load_reserve = 10 |
| 52 | smtp_reserve_hosts = +trusted |
| 53 | |
| 54 | SECTION(global, policy)m4_dnl |
| 55 | host_lookup = * |
| 56 | |
| 57 | SECTION(global, users)m4_dnl |
| 58 | gecos_name = $1 |
| 59 | gecos_pattern = ([^,:]*) |
| 60 | |
| 61 | SECTION(global, incoming)m4_dnl |
| 62 | received_header_text = Received: \ |
| 63 | ${if def:sender_rcvhost \ |
| 64 | {from $sender_rcvhost\n\t} \ |
| 65 | {${if def:sender_ident \ |
| 66 | {from ${quote_local_part:$sender_ident} }}}}\ |
| 67 | by $primary_hostname \ |
| 68 | (Exim $version_number)\ |
| 69 | ${if def:tls_cipher {\n\t} { }}\ |
| 70 | ${if def:received_protocol \ |
| 71 | {with $received_protocol \ |
| 72 | ${if def:tls_cipher {(cipher=$tls_cipher)}}}}\n\t\ |
| 73 | ${if def:sender_address \ |
| 74 | {(envelope-from $sender_address\ |
| 75 | ${if def:authenticated_id \ |
| 76 | {; auth=${quote_local_part:$authenticated_id}} \ |
| 77 | {${if and {{def:authenticated_sender} \ |
| 78 | {match_address{$authenticated_sender} \ |
| 79 | {*@CONF_master_domain}}} \ |
| 80 | {; auth=${quote_local_part:\ |
| 81 | ${local_part:\ |
| 82 | $authenticated_sender}}}}}})\n\t}}\ |
| 83 | id $message_exim_id\ |
| 84 | ${if def:received_for {\n\tfor $received_for}} |
| 85 | |
| 86 | SECTION(global, smtp)m4_dnl |
| 87 | smtp_return_error_details = true |
| 88 | accept_8bitmime = true |
| 89 | |
| 90 | SECTION(global, env)m4_dnl |
| 91 | keep_environment = |
| 92 | |
| 93 | SECTION(global, process)m4_dnl |
| 94 | extract_addresses_remove_arguments = false |
| 95 | headers_charset = utf-8 |
| 96 | qualify_domain = CONF_master_domain |
| 97 | untrusted_set_sender = * |
| 98 | local_from_check = false |
| 99 | local_sender_retain = true |
| 100 | |
| 101 | SECTION(global, bounce)m4_dnl |
| 102 | delay_warning = 1h : 24h : 2d |
| 103 | |
| 104 | SECTION(global, tls)m4_dnl |
| 105 | tls_certificate = CONF_sysconf_dir/server.certlist |
| 106 | tls_privatekey = CONF_sysconf_dir/server.key |
| 107 | tls_advertise_hosts = ${if exists {CONF_sysconf_dir/server.key} {*}{}} |
| 108 | tls_dhparam = CONF_ca_dir/dh-param-2048.pem |
| 109 | tls_require_ciphers = ${if or {{={$received_port}{CONF_submission_port}} \ |
| 110 | {match_ip {$sender_host_address}{+trusted}}} \ |
| 111 | {CONF_good_ciphers} \ |
| 112 | {CONF_acceptable_ciphers}} |
| 113 | tls_verify_certificates = CONF_ca_dir/ca.cert |
| 114 | tls_verify_hosts = ${if eq{$acl_c_mode}{submission} {} {+allnets}} |
| 115 | |
| 116 | DIVERT(null) |
| 117 | ###-------------------------------------------------------------------------- |
| 118 | ### Access control lists. |
| 119 | |
| 120 | SECTION(global, acl-after) |
| 121 | SECTION(global, acl)m4_dnl |
| 122 | acl_smtp_helo = helo |
| 123 | SECTION(acl, misc)m4_dnl |
| 124 | helo: |
| 125 | ## Check that the caller's claimed identity is actually plausible. |
| 126 | ## This seems like it's a fairly effective filter on spamminess, but |
| 127 | ## it's too blunt a tool. Rather than reject, add a warning header. |
| 128 | ## Only we can't do this the easy way, so save it up for use in MAIL. |
| 129 | ## Also, we're liable to get a subsequent HELO (e.g., after STARTTLS) |
| 130 | ## and we should only care about the most recent one. |
| 131 | warn set acl_c_helo_warning = false |
| 132 | !condition = \ |
| 133 | ${if and {{match_ip {$sender_host_address} \ |
| 134 | {<; 127.0.0.0/8 ; ::1}} \ |
| 135 | {match_domain {$sender_helo_name} \ |
| 136 | {localhost : +thishost}}}} |
| 137 | !condition = \ |
| 138 | ${if exists {CONF_sysconf_dir/helo.conf} \ |
| 139 | {${lookup {$sender_helo_name} \ |
| 140 | partial0-lsearch \ |
| 141 | {CONF_sysconf_dir/helo.conf} \ |
| 142 | {${if match_ip \ |
| 143 | {$sender_host_address} \ |
| 144 | {<; $value}}}}}} |
| 145 | !verify = helo |
| 146 | set acl_c_helo_warning = true |
| 147 | |
| 148 | accept |
| 149 | |
| 150 | SECTION(global, acl)m4_dnl |
| 151 | acl_not_smtp_start = not_smtp_start |
| 152 | SECTION(acl, misc)m4_dnl |
| 153 | not_smtp_start: |
| 154 | ## Record the user's name. |
| 155 | warn set acl_c_user = $sender_ident |
| 156 | set acl_m_user = $sender_ident |
| 157 | |
| 158 | ## Done. |
| 159 | accept |
| 160 | |
| 161 | SECTION(global, acl)m4_dnl |
| 162 | acl_smtp_mail = mail |
| 163 | SECTION(acl, mail)m4_dnl |
| 164 | mail: |
| 165 | |
| 166 | ## If we stashed a warning header about HELO from earlier, we should |
| 167 | ## add it now. Only don't bother if the client has authenticated |
| 168 | ## successfully for submission (because we can't expect mobile |
| 169 | ## clients to be properly set up knowing their names), or it's one of |
| 170 | ## our own satellites (because they're either properly set up anyway, |
| 171 | ## or satellites using us as a smarthost). |
| 172 | warn condition = $acl_c_helo_warning |
| 173 | !condition = ${if eq{$acl_c_mode}{submission}} |
| 174 | !hosts = +allnets |
| 175 | WARNING_HEADER(BADHELO, |
| 176 | <:Client's HELO doesn't match its IP address.\n\t\ |
| 177 | helo-name=$sender_helo_name \ |
| 178 | address=$sender_host_address:>) |
| 179 | |
| 180 | ## Always allow the empty sender, so that we can receive bounces. |
| 181 | accept senders = : |
| 182 | |
| 183 | ## Ensure that the sender looks valid. |
| 184 | require acl = mail_check_sender |
| 185 | |
| 186 | ## If this is directly from a client then hack on it for a while. |
| 187 | warn condition = ${if eq{$acl_c_mode}{submission}} |
| 188 | control = submission/sender_retain |
| 189 | |
| 190 | ## Insist that a local client connect through TLS. |
| 191 | deny message = Hosts within CONF_master_domain must use TLS |
| 192 | !condition = ${if eq{$acl_c_mode}{submission}} |
| 193 | hosts = +allnets |
| 194 | !encrypted = * |
| 195 | |
| 196 | ## Check that a submitted message's sender address is allowable. |
| 197 | require acl = mail_check_auth |
| 198 | |
| 199 | SECTION(acl, mail-tail)m4_dnl |
| 200 | ## And we're done. |
| 201 | accept |
| 202 | |
| 203 | SECTION(acl, misc)m4_dnl |
| 204 | mail_check_sender: |
| 205 | |
| 206 | ## See whether there's a special exception for this sender domain. |
| 207 | accept senders = ${LOOKUP_DOMAIN($sender_address_domain, |
| 208 | {KV(senders, {$value}{})}, |
| 209 | {})} |
| 210 | |
| 211 | ## Ensure that the sender is routable. This is important to prevent |
| 212 | ## undeliverable bounces. |
| 213 | require message = Invalid sender; \ |
| 214 | ($sender_verify_failure; $acl_verify_message) |
| 215 | verify = sender |
| 216 | |
| 217 | ## We're good, then. |
| 218 | accept |
| 219 | |
| 220 | SECTION(global, acl)m4_dnl |
| 221 | acl_smtp_connect = connect |
| 222 | SECTION(acl, connect)m4_dnl |
| 223 | connect: |
| 224 | SECTION(acl, connect-tail)m4_dnl |
| 225 | ## Configure variables according to the submission mode. |
| 226 | warn acl = check_submission |
| 227 | |
| 228 | ## Done. |
| 229 | accept |
| 230 | |
| 231 | check_submission: |
| 232 | ## See whether this message needs hacking on. |
| 233 | accept !hosts = +thishost |
| 234 | !condition = ${if ={$received_port}{CONF_submission_port}} |
| 235 | set acl_c_mode = relay |
| 236 | |
| 237 | ## Remember to apply submission controls. |
| 238 | warn set acl_c_mode = submission |
| 239 | |
| 240 | ## Done. |
| 241 | accept |
| 242 | |
| 243 | SECTION(global, acl)m4_dnl |
| 244 | acl_smtp_rcpt = rcpt |
| 245 | SECTION(acl, rcpt)m4_dnl |
| 246 | rcpt: |
| 247 | |
| 248 | ## Reject if the client isn't allowed to relay and the recipient |
| 249 | ## isn't in one of our known domains. |
| 250 | require message = Relaying not permitted |
| 251 | acl = check_relay |
| 252 | |
| 253 | ## Ensure that the recipient is routable. |
| 254 | require message = Invalid recipient \ |
| 255 | ($recipient_verify_failure; $acl_verify_message) |
| 256 | verify = recipient |
| 257 | |
| 258 | SECTION(acl, misc)m4_dnl |
| 259 | check_relay: |
| 260 | ## Accept either if the client is allowed to relay through us, or if |
| 261 | ## we're the correct place to send this mail. |
| 262 | |
| 263 | ## Known clients and authenticated users are OK. |
| 264 | accept hosts = CONF_relay_clients |
| 265 | accept authenticated = * |
| 266 | |
| 267 | ## Known domains are OK. |
| 268 | accept domains = +public |
| 269 | |
| 270 | ## Finally, domains in our table are OK, unless they say they aren't. |
| 271 | accept domains = \ |
| 272 | ${if exists{CONF_sysconf_dir/domains.conf} \ |
| 273 | {partial0-lsearch; CONF_sysconf_dir/domains.conf}} |
| 274 | condition = DOMKV(service, {$value}{true}) |
| 275 | |
| 276 | ## Nope, that's not allowed. |
| 277 | deny |
| 278 | |
| 279 | SECTION(acl, rcpt-tail)m4_dnl |
| 280 | ## Everything checks out OK: let this one go through. |
| 281 | accept |
| 282 | |
| 283 | SECTION(global, acl)m4_dnl |
| 284 | acl_smtp_data = data |
| 285 | SECTION(acl, data)m4_dnl |
| 286 | data: |
| 287 | |
| 288 | SECTION(acl, data-tail)m4_dnl |
| 289 | accept |
| 290 | |
| 291 | SECTION(global, acl)m4_dnl |
| 292 | acl_smtp_expn = expn_vrfy |
| 293 | acl_smtp_vrfy = expn_vrfy |
| 294 | SECTION(acl, misc)m4_dnl |
| 295 | expn_vrfy: |
| 296 | accept hosts = +trusted |
| 297 | deny message = Suck it and see |
| 298 | |
| 299 | DIVERT(null) |
| 300 | ###-------------------------------------------------------------------------- |
| 301 | ### Verification of sender address. |
| 302 | |
| 303 | SECTION(acl, misc)m4_dnl |
| 304 | mail_check_auth: |
| 305 | |
| 306 | ## If this isn't a submission then it doesn't need checking. |
| 307 | accept condition = ${if !eq{$acl_c_mode}{submission}} |
| 308 | |
| 309 | ## If the caller hasn't formally authenticated, but this is a |
| 310 | ## loopback connection, then we can trust identd to tell us the right |
| 311 | ## answer. So we should stash the right name somewhere consistent. |
| 312 | warn set acl_c_user = $authenticated_id |
| 313 | hosts = +thishost |
| 314 | !authenticated = * |
| 315 | condition = ${if def:sender_ident} |
| 316 | set acl_c_user = $sender_ident |
| 317 | |
| 318 | ## User must be authenticated by now. |
| 319 | deny message = Sender not authenticated |
| 320 | condition = ${if !def:acl_c_user} |
| 321 | |
| 322 | ## Set the per-message authentication flag, since we now know that |
| 323 | ## there's a sensible value. |
| 324 | warn set acl_m_user = $acl_c_user |
| 325 | |
| 326 | ## All done. |
| 327 | accept |
| 328 | |
| 329 | DIVERT(null) |
| 330 | ###-------------------------------------------------------------------------- |
| 331 | ### Common options for forwarding routers. |
| 332 | |
| 333 | ## We're pretty permissive here. |
| 334 | m4_define(<:FILTER_BASE:>, |
| 335 | <:driver = redirect |
| 336 | modemask = 002 |
| 337 | check_owner = false |
| 338 | check_group = false |
| 339 | allow_filter = true |
| 340 | allow_defer = true |
| 341 | allow_fail = true |
| 342 | forbid_blackhole = false |
| 343 | check_ancestor = true:>) |
| 344 | |
| 345 | ## Common options for forwarding routers at verification time. |
| 346 | m4_define(<:FILTER_VERIFY:>, |
| 347 | <:verify_only = true |
| 348 | user = CONF_filter_user |
| 349 | forbid_filter_dlfunc = true |
| 350 | forbid_filter_logwrite = true |
| 351 | forbid_filter_perl = true |
| 352 | forbid_filter_readsocket = true |
| 353 | forbid_filter_run = true |
| 354 | file_transport = dummy |
| 355 | directory_transport = dummy |
| 356 | pipe_transport = dummy |
| 357 | reply_transport = dummy:>) |
| 358 | |
| 359 | ## Transports for redirection filters. |
| 360 | m4_define(<:FILTER_TRANSPORTS:>, |
| 361 | <:file_transport = mailbox |
| 362 | directory_transport = maildir |
| 363 | pipe_transport = pipe |
| 364 | reply_transport = reply:>) |
| 365 | |
| 366 | m4_define(<:FILTER_ROUTER:>, |
| 367 | <:$1_vrf: |
| 368 | $2 |
| 369 | FILTER_VERIFY<::>$3 |
| 370 | $1: |
| 371 | $2 |
| 372 | verify = no |
| 373 | FILTER_TRANSPORTS<::>$4:>) |
| 374 | |
| 375 | DIVERT(null) |
| 376 | ###-------------------------------------------------------------------------- |
| 377 | ### Common routers. |
| 378 | |
| 379 | SECTION(routers, alias)m4_dnl |
| 380 | ## Look up the local part in the address map. |
| 381 | alias: |
| 382 | driver = redirect |
| 383 | allow_fail = true |
| 384 | allow_defer = true |
| 385 | user = CONF_filter_user |
| 386 | FILTER_TRANSPORTS |
| 387 | local_parts = nwildlsearch; CONF_alias_file |
| 388 | data = ${expand:$local_part_data} |
| 389 | SECTION(routers, alias-opts)m4_dnl |
| 390 | |
| 391 | DIVERT(null) |
| 392 | ###-------------------------------------------------------------------------- |
| 393 | ### Some standard transports. |
| 394 | |
| 395 | m4_define(<:USER_DELIVERY:>, |
| 396 | <:delivery_date_add = true |
| 397 | envelope_to_add = true |
| 398 | return_path_add = true:>) |
| 399 | |
| 400 | m4_define(<:APPLY_HEADER_CHANGES:>, |
| 401 | <:headers_add = m4_ifelse(<:$1:>, <::>, |
| 402 | <:$acl_m_hdradd:>, |
| 403 | <:${if def:acl_m_hdradd{$acl_m_hdradd\n}}\ |
| 404 | $1:>) |
| 405 | headers_remove = m4_ifelse(<:$2:>, <::>, |
| 406 | <:$acl_m_hdrrm:>, |
| 407 | <:${if def:acl_m_hdrrm{$acl_m_hdrrm:}}\ |
| 408 | $2:>):>) |
| 409 | |
| 410 | SECTION(transports)m4_dnl |
| 411 | ## A standard transport for remote delivery. By default, try to do TLS, and |
| 412 | ## don't worry too much if it's not very secure: the alternative is sending |
| 413 | ## in plaintext anyway. But all of this can be overridden from the |
| 414 | ## `domains.conf' file. Annoyingly, the `tls_dh_min_bits' setting isn't |
| 415 | ## expanded before use, so we can't set it the obvious way. Instead, encode |
| 416 | ## it into the transport name. This is very unpleasant, of course. |
| 417 | smtp: |
| 418 | driver = smtp |
| 419 | APPLY_HEADER_CHANGES |
| 420 | tls_require_ciphers = CONF_acceptable_ciphers |
| 421 | tls_dh_min_bits = 508 |
| 422 | tls_tempfail_tryclear = true |
| 423 | |
| 424 | m4_define(<:SMTP_TRANS_DHBITS:>, |
| 425 | <:driver = smtp |
| 426 | APPLY_HEADER_CHANGES |
| 427 | hosts_try_auth = * |
| 428 | hosts_require_tls = DOMKV(tls-peer-ca, {*}{}) |
| 429 | hosts_require_auth = \ |
| 430 | ${if bool {DOMKV(require-auth, {$value}{false})} {*}{}} |
| 431 | tls_certificate = DOMKV(tls-certificate, {${expand:$value}}fail) |
| 432 | tls_privatekey = DOMKV(tls-private-key, {${expand:$value}}fail) |
| 433 | tls_verify_certificates = DOMKV(tls-peer-ca, {${expand:$value}}fail) |
| 434 | tls_require_ciphers = \ |
| 435 | DOMKV(tls-ciphers, |
| 436 | {${extract {${expand:$value}} \ |
| 437 | { good = CONF_good_ciphers \ |
| 438 | any = CONF_acceptable_ciphers } \ |
| 439 | {$value} \ |
| 440 | {${expand:$value}}}} \ |
| 441 | {CONF_acceptable_ciphers}) |
| 442 | tls_dh_min_bits = $1 |
| 443 | tls_tempfail_tryclear = true:>)m4_dnl |
| 444 | smtp_dhbits_512: |
| 445 | SMTP_TRANS_DHBITS(508) |
| 446 | smtp_dhbits_768: |
| 447 | SMTP_TRANS_DHBITS(764) |
| 448 | smtp_dhbits_1024: |
| 449 | SMTP_TRANS_DHBITS(1020) |
| 450 | smtp_dhbits_2048: |
| 451 | SMTP_TRANS_DHBITS(2044) |
| 452 | |
| 453 | ## Transport to a local SMTP server; use TLS and perform client |
| 454 | ## authentication. |
| 455 | smtp_local: |
| 456 | driver = smtp |
| 457 | APPLY_HEADER_CHANGES |
| 458 | hosts_require_tls = * |
| 459 | tls_certificate = CONF_sysconf_dir/client.certlist |
| 460 | tls_privatekey = CONF_sysconf_dir/client.key |
| 461 | tls_verify_certificates = CONF_ca_dir/ca.cert |
| 462 | tls_require_ciphers = CONF_good_ciphers |
| 463 | tls_dh_min_bits = 2046 |
| 464 | tls_tempfail_tryclear = false |
| 465 | authenticated_sender_force = true |
| 466 | authenticated_sender = \ |
| 467 | ${if def:acl_m_user {$acl_m_user@CONF_master_domain} \ |
| 468 | {${if def:authenticated_sender {$authenticated_sender} \ |
| 469 | fail}}} |
| 470 | |
| 471 | ## A standard transport for local delivery. |
| 472 | deliver: |
| 473 | driver = appendfile |
| 474 | APPLY_HEADER_CHANGES |
| 475 | file = /var/mail/$local_part |
| 476 | group = mail |
| 477 | mode = 0600 |
| 478 | mode_fail_narrower = false |
| 479 | USER_DELIVERY |
| 480 | |
| 481 | ## Transports for user filters. |
| 482 | mailbox: |
| 483 | driver = appendfile |
| 484 | APPLY_HEADER_CHANGES |
| 485 | initgroups = true |
| 486 | USER_DELIVERY |
| 487 | |
| 488 | maildir: |
| 489 | driver = appendfile |
| 490 | APPLY_HEADER_CHANGES |
| 491 | maildir_format = true |
| 492 | initgroups = true |
| 493 | USER_DELIVERY |
| 494 | |
| 495 | pipe: |
| 496 | driver = pipe |
| 497 | APPLY_HEADER_CHANGES |
| 498 | path = ${if and {{def:home} {exists{$home/bin}}} {$home/bin:} {}}\ |
| 499 | /usr/local/bin:/usr/local/sbin:\ |
| 500 | /usr/bin:/usr/sbin:/bin:/sbin |
| 501 | initgroups = true |
| 502 | umask = 002 |
| 503 | return_fail_output = true |
| 504 | log_output = true |
| 505 | |
| 506 | ## A special dummy transport for use during address verification. |
| 507 | dummy: |
| 508 | driver = appendfile |
| 509 | file = /dev/null |
| 510 | |
| 511 | DIVERT(null) |
| 512 | ###-------------------------------------------------------------------------- |
| 513 | ### Retry configuration. |
| 514 | |
| 515 | SECTION(retry, default)m4_dnl |
| 516 | ## Default. |
| 517 | * * \ |
| 518 | F,2h,15m; G,16h,2h,1.5; F,4d,6h |
| 519 | |
| 520 | DIVERT(null) |
| 521 | ###----- That's all, folks -------------------------------------------------- |