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