### -*-m4-*- ### ### Basic settings for distorted.org.uk Exim configuration ### ### (c) 2012 Mark Wooding ### ###----- Licensing notice --------------------------------------------------- ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation; either version 2 of the License, or ### (at your option) any later version. ### ### This program is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with this program; if not, write to the Free Software Foundation, ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ###-------------------------------------------------------------------------- ### Global settings. SECTION(global, priv)m4_dnl admin_groups = CONF_admin_groups trusted_groups = CONF_trusted_groups prod_requires_admin = false SECTION(global, logging)m4_dnl log_file_path = : syslog log_selector = \ +smtp_confirmation \ +tls_peerdn log_timezone = true syslog_duplication = false syslog_timestamp = false SECTION(global, daemon)m4_dnl local_interfaces = <; CONF_interfaces extra_local_interfaces = <; 0.0.0.0 ; ::0 SECTION(global, resource)m4_dnl deliver_queue_load_max = 8 queue_only_load = 12 smtp_accept_max = 16 smtp_accept_queue = 32 smtp_accept_reserve = 4 smtp_load_reserve = 10 smtp_reserve_hosts = +trusted SECTION(global, policy)m4_dnl host_lookup = * SECTION(global, users)m4_dnl gecos_name = $1 gecos_pattern = ([^,:]*) SECTION(global, incoming)m4_dnl received_header_text = Received: \ ${if def:sender_rcvhost \ {from $sender_rcvhost\n\t} \ {${if def:sender_ident \ {from ${quote_local_part:$sender_ident} }}}}\ by $primary_hostname \ (Exim $version_number)\ ${if def:tls_cipher {\n\t} { }}\ ${if def:received_protocol \ {with $received_protocol \ ${if def:tls_cipher {(cipher=$tls_cipher)}}}}\n\t\ ${if def:sender_address \ {(envelope-from $sender_address\ ${if def:authenticated_id \ {; auth=$authenticated_id}})\n\t}}\ id $message_exim_id\ ${if def:received_for {\n\tfor $received_for}} SECTION(global, smtp)m4_dnl smtp_return_error_details = true accept_8bitmime = true SECTION(global, process)m4_dnl extract_addresses_remove_arguments = false headers_charset = utf-8 qualify_domain = CONF_master_domain untrusted_set_sender = * SECTION(global, bounce)m4_dnl delay_warning = 1h : 24h : 2d SECTION(global, tls)m4_dnl tls_certificate = CONF_sysconf_dir/server.certlist tls_privatekey = CONF_sysconf_dir/server.key tls_advertise_hosts = ${if exists {CONF_sysconf_dir/server.key} {*}{}} tls_dhparam = CONF_ca_dir/dh-param-2048.pem tls_require_ciphers = ${if or {{={$received_port}{CONF_submission_port}} \ {match_ip {$sender_host_address}{+trusted}}} \ {CONF_good_ciphers} \ {CONF_acceptable_ciphers}} tls_verify_certificates = CONF_ca_dir/ca.cert tls_verify_hosts = ${if eq{$acl_c_mode}{submission} {} {+allnets}} DIVERT(null) ###-------------------------------------------------------------------------- ### Access control lists. SECTION(global, acl-after) SECTION(global, acl)m4_dnl acl_smtp_helo = helo SECTION(acl, misc)m4_dnl helo: ## Check that the caller's claimed identity is actually plausible. ## This seems like it's a fairly effective filter on spamminess, but ## it's too blunt a tool. Rather than reject, add a warning header. ## Only we can't do this the easy way, so save it up for use in MAIL. ## Also, we're liable to get a subsequent HELO (e.g., after STARTTLS) ## and we should only care about the most recent one. warn set acl_c_helo_warning = false !condition = \ ${if and {{match_ip {$sender_host_address} \ {<; 127.0.0.0/8 ; ::1}} \ {match_domain {$sender_helo_name} \ {localhost : +thishost}}}} !condition = \ ${if exists {CONF_sysconf_dir/helo.conf} \ {${lookup {$sender_helo_name} \ partial0-lsearch \ {CONF_sysconf_dir/helo.conf} \ {${if match_ip \ {$sender_host_address} \ {<; $value}}}}}} !verify = helo set acl_c_helo_warning = true accept SECTION(global, acl)m4_dnl acl_not_smtp_start = not_smtp_start SECTION(acl, misc)m4_dnl not_smtp_start: ## Record the user's name. warn set acl_c_user = $sender_ident ## Done. accept SECTION(global, acl)m4_dnl acl_smtp_mail = mail SECTION(acl, mail)m4_dnl mail: ## If we stashed a warning header about HELO from earlier, we should ## add it now. Only don't bother if the client has authenticated ## successfully for submission (because we can't expect mobile ## clients to be properly set up knowing their names), or it's one of ## our own satellites (because they're either properly set up anyway, ## or satellites using us as a smarthost). warn condition = $acl_c_helo_warning !condition = ${if eq{$acl_c_mode}{submission}} !hosts = +allnets add_header = :after_received:X-Distorted-Warning: \ BADHELO \ Client's HELO doesn't match its IP address.\n\t\ helo-name=$sender_helo_name \ address=$sender_host_address ## Always allow the empty sender, so that we can receive bounces. accept senders = : ## Ensure that the sender looks valid. require acl = mail_check_sender ## If this is directly from a client then hack on it for a while. warn condition = ${if eq{$acl_c_mode}{submission}} control = submission/sender_retain ## Insist that a local client connect through TLS. deny message = Hosts within CONF_master_domain must use TLS !condition = ${if eq{$acl_c_mode}{submission}} hosts = +allnets !encrypted = * ## Check that a submitted message's sender address is allowable. require acl = mail_check_auth SECTION(acl, mail-tail)m4_dnl ## And we're done. accept SECTION(acl, misc)m4_dnl mail_check_sender: ## See whether there's a special exception for this sender domain. accept senders = ${LOOKUP_DOMAIN($sender_address_domain, {KV(senders, {$value}{})}, {})} ## Ensure that the sender is routable. This is important to prevent ## undeliverable bounces. require message = Invalid sender; \ ($sender_verify_failure; $acl_verify_message) verify = sender ## We're good, then. accept SECTION(global, acl)m4_dnl acl_smtp_connect = connect SECTION(acl, connect)m4_dnl connect: SECTION(acl, connect-tail)m4_dnl ## Configure variables according to the submission mode. warn acl = check_submission ## Done. accept check_submission: ## See whether this message needs hacking on. accept !hosts = +thishost !condition = ${if ={$received_port}{CONF_submission_port}} set acl_c_mode = relay ## Remember to apply submission controls. warn set acl_c_mode = submission ## Done. accept SECTION(global, acl)m4_dnl acl_smtp_rcpt = rcpt SECTION(acl, rcpt)m4_dnl rcpt: ## Reject if the client isn't allowed to relay and the recipient ## isn't in one of our known domains. require message = Relaying not permitted acl = check_relay ## Ensure that the recipient is routable. require message = Invalid recipient \ ($recipient_verify_failure; $acl_verify_message) verify = recipient SECTION(acl, misc)m4_dnl check_relay: ## Accept either if the client is allowed to relay through us, or if ## we're the correct place to send this mail. ## Known clients and authenticated users are OK. accept hosts = CONF_relay_clients accept authenticated = * ## Known domains are OK. accept domains = +public ## Finally, domains in our table are OK, unless they say they aren't. accept domains = \ ${if exists{CONF_sysconf_dir/domains.conf} \ {partial0-lsearch; CONF_sysconf_dir/domains.conf}} condition = DOMKV(service, {$value}{true}) ## Nope, that's not allowed. deny SECTION(acl, rcpt-tail)m4_dnl ## Everything checks out OK: let this one go through. accept SECTION(global, acl)m4_dnl acl_smtp_data = data SECTION(acl, data)m4_dnl data: SECTION(acl, data-tail)m4_dnl accept SECTION(global, acl)m4_dnl acl_smtp_expn = expn_vrfy acl_smtp_vrfy = expn_vrfy SECTION(acl, misc)m4_dnl expn_vrfy: accept hosts = +trusted deny message = Suck it and see DIVERT(null) ###-------------------------------------------------------------------------- ### Verification of sender address. SECTION(acl, misc)m4_dnl mail_check_auth: ## If this isn't a submission then it doesn't need checking. accept condition = ${if !eq{$acl_c_mode}{submission}} ## If the caller hasn't formally authenticated, but this is a ## loopback connection, then we can trust identd to tell us the right ## answer. So we should stash the right name somewhere consistent. warn set acl_c_user = $authenticated_id hosts = +thishost !authenticated = * condition = ${if def:sender_ident} set acl_c_user = $sender_ident ## User must be authenticated by now. deny message = Sender not authenticated condition = ${if !def:acl_c_user} ## All done. accept DIVERT(null) ###-------------------------------------------------------------------------- ### Common options for forwarding routers. ## We're pretty permissive here. m4_define(<:FILTER_BASE:>, <:driver = redirect modemask = 002 check_owner = false check_group = false allow_filter = true allow_defer = true allow_fail = true forbid_blackhole = false check_ancestor = true:>) ## Common options for forwarding routers at verification time. m4_define(<:FILTER_VERIFY:>, <:verify_only = true user = CONF_filter_user forbid_filter_dlfunc = true forbid_filter_logwrite = true forbid_filter_perl = true forbid_filter_readsocket = true forbid_filter_run = true file_transport = dummy directory_transport = dummy pipe_transport = dummy reply_transport = dummy:>) ## Transports for redirection filters. m4_define(<:FILTER_TRANSPORTS:>, <:file_transport = mailbox directory_transport = maildir pipe_transport = pipe reply_transport = reply:>) m4_define(<:FILTER_ROUTER:>, <:$1_vrf: $2 FILTER_VERIFY<::>$3 $1: $2 verify = no FILTER_TRANSPORTS<::>$4:>) DIVERT(null) ###-------------------------------------------------------------------------- ### Common routers. SECTION(routers, alias)m4_dnl ## Look up the local part in the address map. alias: driver = redirect allow_fail = true allow_defer = true user = CONF_filter_user FILTER_TRANSPORTS local_parts = nwildlsearch; CONF_alias_file data = ${expand:$local_part_data} SECTION(routers, alias-opts)m4_dnl DIVERT(null) ###-------------------------------------------------------------------------- ### Some standard transports. m4_define(<:USER_DELIVERY:>, <:delivery_date_add = true envelope_to_add = true return_path_add = true:>) SECTION(transports)m4_dnl ## A standard transport for remote delivery. By default, try to do TLS, and ## don't worry too much if it's not very secure: the alternative is sending ## in plaintext anyway. But all of this can be overridden from the ## `domains.conf' file. Annoyingly, the `tls_dh_min_bits' setting isn't ## expanded before use, so we can't set it the obvious way. Instead, encode ## it into the transport name. This is very unpleasant, of course. smtp: driver = smtp tls_require_ciphers = CONF_acceptable_ciphers tls_dh_min_bits = 1020 tls_tempfail_tryclear = true m4_define(<:SMTP_TRANS_DHBITS:>, <:driver = smtp hosts_try_auth = * hosts_require_tls = DOMKV(tls-peer-ca, {*}{}) hosts_require_auth = \ ${if bool {DOMKV(require-auth, {$value}{false})} {*}{}} tls_certificate = DOMKV(tls-certificate, {${expand:$value}}fail) tls_privatekey = DOMKV(tls-private-key, {${expand:$value}}fail) tls_verify_certificates = DOMKV(tls-peer-ca, {${expand:$value}}fail) tls_require_ciphers = \ DOMKV(tls-ciphers, {${extract {${expand:$value}} \ { good = CONF_good_ciphers \ any = CONF_acceptable_ciphers } \ {$value} \ {${expand:$value}}}} \ {CONF_acceptable_ciphers}) tls_dh_min_bits = $1 tls_tempfail_tryclear = true:>)m4_dnl smtp_dhbits_1024: SMTP_TRANS_DHBITS(1020) smtp_dhbits_2048: SMTP_TRANS_DHBITS(2046) ## Transport to a local SMTP server; use TLS and perform client ## authentication. smtp_local: driver = smtp hosts_require_tls = * tls_certificate = CONF_sysconf_dir/client.certlist tls_privatekey = CONF_sysconf_dir/client.key tls_verify_certificates = CONF_ca_dir/ca.cert tls_require_ciphers = CONF_good_ciphers tls_dh_min_bits = 2046 tls_tempfail_tryclear = false authenticated_sender = ${if def:authenticated_id \ {$authenticated_id@CONF_master_domain} \ fail} ## A standard transport for local delivery. deliver: driver = appendfile file = /var/mail/$local_part group = mail mode = 0600 mode_fail_narrower = false USER_DELIVERY ## Transports for user filters. mailbox: driver = appendfile initgroups = true USER_DELIVERY maildir: driver = appendfile maildir_format = true initgroups = true USER_DELIVERY pipe: driver = pipe path = ${if and {{def:home} {exists{$home/bin}}} {$home/bin:} {}}\ /usr/local/bin:/usr/local/sbin:\ /usr/bin:/usr/sbin:/bin:/sbin initgroups = true umask = 002 return_fail_output = true log_output = true ## A special dummy transport for use during address verification. dummy: driver = appendfile file = /dev/null DIVERT(null) ###-------------------------------------------------------------------------- ### Retry configuration. SECTION(retry, default)m4_dnl ## Default. * * \ F,2h,15m; G,16h,2h,1.5; F,4d,6h DIVERT(null) ###----- That's all, folks --------------------------------------------------