From: Mark Wooding Date: Sun, 29 Dec 2013 19:39:09 +0000 (+0000) Subject: Initial commit. X-Git-Url: https://git.distorted.org.uk/~mdw/exim-config/commitdiff_plain/185b5456076ca86959643ce2f19c98c0f82f281e Initial commit. --- 185b5456076ca86959643ce2f19c98c0f82f281e diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d2c7cb1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.conf +hacks.m4 +local.mk diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a88c324 --- /dev/null +++ b/Makefile @@ -0,0 +1,86 @@ +### -*-makefile-*- +### +### Build script for 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. + +V = 0 +v_tag = $(call v_tag_$V,$1) +v_tag_0 = @printf " %-6s %s\n" $1 $@; + +V_GEN = $(call v_tag,GEN) +V_AT = $(V_AT_$V) +V_AT_0 = @ + +dir-nosl = $(patsubst %/,%,$(dir $1)) + +all: +.SECONDEXPANSION: # sorry + +CLEANFILES += $(TARGETS) + +EARLY = defs.m4 divmap.m4 config.m4 +MAIN = lists.m4 base.m4 + +MODES = + +MODES += satellite +OPTIONS_satellite = satellite.m4 + +MODES += hub +OPTIONS_hub = auth.m4 exchange.m4 local.m4 spam.m4 vhost.m4 + +MODES += usersat +OPTIONS_usersat = auth.m4 local.m4 satellite.m4 + +-include local.mk + +HOST_MODES += $(foreach m, $(MODES), \ + $(foreach h, $(HOSTS_$m), $h/$m)) + +CONFIGS = $(foreach m, $(MODES), exim4-$m.conf) +TARGETS += $(CONFIGS) +$(CONFIGS): exim4-%.conf: $(EARLY) $$(HOOKS_$$*) $(MAIN) $$(OPTIONS_$$*) + $(V_GEN)m4 -P -DMODE=$* $^ >$@.new + $(V_AT)exim4 -C$@.new -bV >/dev/null + $(V_AT)mv $@.new $@ + +all: $(TARGETS) + +THISHOST = $(shell hostname) + +ROOT = sudo + +INSTALL_TARGETS = $(addprefix install-, $(HOST_MODES)) + +$(filter install-$(THISHOST)/%, $(INSTALL_TARGETS)): \ +install-$(THISHOST)/%: exim4-%.conf + $(ROOT) install -m644 $< /etc/exim4/exim4.conf + $(ROOT) service exim4 reload + +$(filter-out install-$(THISHOST)/%, $(INSTALL_TARGETS)): \ +install-%: exim4-$$(notdir $$*).conf + $(ROOT) scp $< root@$(call dir-nosl,$*):/etc/exim4/exim4.conf + $(ROOT) ssh root@$(call dir-nosl,$*) service exim4 reload + +install: $(INSTALL_TARGETS) + +clean:; rm -f $(CLEANFILES) +.PHONY: clean diff --git a/README b/README new file mode 100644 index 0000000..25149fd --- /dev/null +++ b/README @@ -0,0 +1,188 @@ +The =distorted.org.uk= mail system + +* Delivery + +The mail delivery agent is Exim. If you don't do anything special, mail +is delivered into =/var/mail/USER= on stratocaster, in mbox format. + +There are a number of ways you can affect mail delivery. + +** The =~/.forward= file + +In traditional Unix style, you can write delivery instructions into a +file named =.forward= in your home directory. This file can contain a +comma-separated list of email address and/or file or directory names to +which your mail should be sent. Mail is written to files in traditional +Unix `mbox' format, and to directories in `Maildir' format. The +=:fail:= and =:defer:= items are permitted, but may not be very useful. + +This file can instead be an Exim or Sieve filter file, as marked by a +special comment on the first line. See the document `Exim's interfaces +to mail filtering', available via the command =info filter=, for details +about these files. + +** The =~/.mail/forward= file + +If you prefer, you can write delivery instructions to =~/.mail/forward= +instead. If you have lots of mail configuration files, you may find it +tidier to keep them all together in =~/.mail=. + +** The =~/.mail/forward.suffix= file + +You will receive mail sent to =USER@distorted.org.uk=. You can also +receive mail sent to =USER-SUFFIX@distorted.org.uk= or +=USER+SUFFIX@distorted.org.uk=, for any =SUFFIX= string if you create a +file =~/.mail/forward.suffix=. While this can be a simple forward file, +it's probably much more useful to write an Exim filter file to analyse +the suffix string and take appropriate action. + +If this file exists, it should be world-readable, because it will be +used by the mail server at SMTP time in order to decide whether a +particular =SUFFIX= string is valid. + + +* Reading mail + +** Reading mail locally + +The servers =stratocaster= and =jem= have a few mail user agents +installed, most notably trad BSD =mail=, =mutt=, and Emacs's various +mail-reading interfaces; more can be added. + +** Fetching mail through IMAP + +There's an IMAP server running on =mail.distorted.org.uk=. ... + +** Forwarding mail off-site + + +* Spam filtering + +The mail server checks incoming mail using SpamAssassin at SMTP time. +Suspected spam is rejected immediately. There are no `junk' mail +folders. Legitimate senders will likely receive bounces; spammers will +probably ignore the error and continue. + +** SpamAssassin + +SpamAssassin works by having a large collection of rules: it tests an +incoming message against these rules, and adds up the /scores/ for the +rules that match. If the total score is above a given threshold then +the message is declared to be probably spam, and rejected. + +If the mail server accepts a message, it adds two headers to it. + + + =X-SpamAssassin-Score= has the form =SCORE/LIMIT (BAR)=, where + =SCORE= is the actual score for the message, =LIMIT= is the maximum + score allowed, and =BAR= is a little bar chart showing the score in + a way which can be matched easily using regular expressions. The + bar chart uses =+= or =-= signs, depending on whether the score is + positive or negative, or consists of a single =/= sign if it's close + to zero. + + + =X-SpamAssassin-Status= consists of space-separated =KEY=VAUE= + pairs. The keys currently are: =score= and =limit=, which are the + message's score and limit again; and =tests=, which lists the rules + which matched the message and their individual scores, as a + comma-separated list of items of the form =RULE:SCORE=. + +** Custom spam limits + +The default spam limit is currently 5 points. However, you can override +this limit for mail sent to you by creating a world-readable file +=~/.mail/spam-limit= in your home directory on stratocaster. This file +should contain lines of the form + +: PATTERN: LIMIT + +where =PATTERN= is an Exim =nwildlsearch= pattern matched against a +string of the form =RECIPIENT/SENDER=, and the =LIMIT= is ten times the +maximum SpamAssassin score you're willing to tolerate for this message. +See the Exim manual for full details; in short, the pattern may be a +literal string, a string beginning with a =*= to match a particular +suffix (usually a sender address or domain, which is why the sender is +on the right), or a Perl-style regular expression starting with =^=. + +You may not want information about who is sending you spam (or honest +but spamlike mail) to be public knowledge, so instead you can make a +file =~/.mail/spam-limit.userv= of the same format. This file need not +be readable by anyone other than you. + +Be careful with this facility: if a single incoming message has multiple +recipients, and they assign it different spam score limits (either +explicitly, or implicitly by accepting the system default) then the +sender will be told to defer delivery to some recipients. It's +therefore probably a bad idea to apply custom spam score limits for mail +for popular mailing lists, for example. + +** SAUCE + +I'm not currently running SAUCE, but I'm giving it some consideration. +If you have comments on the matter, either way, I'm interested. + + +* Sending mail + +** Submission mechanisms + +Mail can be sent in a number of ways. + + + The =sendmail= program. This is really Exim in disguise. + + + SMTP to =localhost= port 25. This doesn't require explicit + authentication, since it relies on an identd, which is running on + all =distorted.org.uk= hosts. + + + SMTP to =mail.distorted.org.uk= port 587. You must establish TLS, + and authenticate using a username and password; the server uses a + short-lived certificate signed by the =distorted.org.uk= certificate + authority, whose root certificate is at =/etc/ca/ca.cert= on all + servers. Use [[https://www.distorted.org.uk/chpwd/][Chopwood]] to set or change this password. + +** Sender authenticity + +It is my intention that it be very hard for one =distorted.org.uk= user +to impersonate another to a third. To this end, the mail server is +rather picky about envelope sender addresses. + + + It won't accept an apparently local sender address from an external + mail server at all. + + + It will check locally submitted mail against the submitter's user + name. The precise details vary according to the submission + mechanism: mail submitted through =sendmail= will have additional + headers added; mail submitted through SMTP will be rejected unless + the envelope sender is acceptable. + +If I see something like DKIM catching on then this will also provide +external users with some kind of (probably fairly weak) sender +authenticity. + +On the other hand, the mail server is aware of vanity domains, extension +addresses, and so on, and should let you send mail apparently from an +such an address that you control. If you think the mail server is being +unnecessarily strict about something then I'm willing to discuss your +requirements. + +If I'm hosting your mail domain for you then you get to decide the +appropriate policy. + + +* Mail hosting and custom domains + +I think I have a fairly sane way to set up stratocaster (or some other +server, but strat is the obvious choice) to receive mail for domains +other than =distorted.org.uk=. I can easily arrange to accept mail for +such domains and deliver them locally or to other hosts. Pester me if +this sounds useful to you. + + +* Quick reference + + + +* COMMENT Emacs cruft + +### Local variables: +### mode: org +### End: diff --git a/auth.m4 b/auth.m4 new file mode 100644 index 0000000..d4729c0 --- /dev/null +++ b/auth.m4 @@ -0,0 +1,132 @@ +### -*-m4-*- +### +### Client authentication 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. + +###-------------------------------------------------------------------------- +### Authenticators. + +m4_define(<:CHECK_PASSWD:>, +<:${lookup {$1} lsearch {CONF_sysconf_dir/passwd} \ + {${if crypteq {$2} {$value}}} \ + {false}}:>) + +m4_define(<:ALLOW_PLAINTEXT_AUTH_P:>, +<:or {{match_ip {$sender_host_address}{+localnet}} \ + {and {{def:tls_cipher} {eq{$acl_c_mode}{submission}}}}}:>) + +SECTION(auth)m4_dnl +plain: + driver = plaintext + public_name = PLAIN + server_advertise_condition = ${if ALLOW_PLAINTEXT_AUTH_P} + server_prompts = : + server_condition = CHECK_PASSWD($auth2, $auth3) + server_set_id = $auth2 + +login: + driver = plaintext + public_name = LOGIN + server_advertise_condition = ${if ALLOW_PLAINTEXT_AUTH_P} + server_prompts = <; Username: ; Password: + server_condition = CHECK_PASSWD($auth1, $auth2) + server_set_id = $auth1 + +DIVERT(null) +###-------------------------------------------------------------------------- +### Verification of sender address. + +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 + +SECTION(acl, mail-hooks)m4_dnl + ## Check that a submitted message's sender address is allowable. + require acl = mail_check_auth + +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 = +localnet + !authenticated = * + set acl_c_user = $sender_ident + + ## User must be authenticated. + deny message = Sender not authenticated + !hosts = +localnet + !authenticated = * + + ## Make sure that the local part is one that the authenticated sender + ## is allowed to claim. + deny message = Sender address forbidden to calling user + !condition = ${LOOKUP_DOMAIN($sender_address_domain, + {${if and {{match_local_part \ + {$acl_c_user} \ + {+dom_users}} \ + {match_local_part \ + {$sender_address_local_part} \ + {+dom_locals}}}}}, + {${if and {{match_local_part \ + {$sender_address_local_part} \ + {+user_extaddr}} \ + {or {{eq {$sender_address_domain} \ + {}} \ + {match_domain \ + {$sender_address_domain} \ + {+public}}}}}}})} + + ## All done. + accept + +DIVERT(null) +###-------------------------------------------------------------------------- +### Dealing with `AUTH' parameters and relaying. + +SECTION(global, acl)m4_dnl +acl_smtp_mailauth = mailauth +SECTION(acl, misc)m4_dnl +## Check the `AUTH=...' parameter to a `MAIL' command. +mailauth: + ## If the client has authenticated using TLS then we're OK. The + ## sender was presumably checked upstream, and we can believe that + ## the name has been transmitted honestly. + accept condition = ${if def:tls_peerdn} + + ## If this is submission, and the client has authenticated, then we + ## check that the name matches the user. + accept condition = ${if eq {$authenticated_sender} \ + {$authenticated_id@CONF_master_domain}} + + ## Otherwise we can't tell who really sent it. + deny message = Authenticated user not authoritative for claimed sender. + +DIVERT(null) +###----- That's all, folks -------------------------------------------------- diff --git a/base.m4 b/base.m4 new file mode 100644 index 0000000..887a132 --- /dev/null +++ b/base.m4 @@ -0,0 +1,289 @@ +### -*-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 +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 ; :: + +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} }}\ + ${if def:sender_helo_name \ + {(helo=$sender_helo_name)\n\t}}}}\ + by $primary_hostname \ + ${if def:received_protocol \ + {with $received_protocol \ + ${if def:tls_cipher {(cipher=$tls_cipher)\n\t}}}}\ + (Exim $version_number)\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 + +SECTION(global, bounce)m4_dnl +delay_warning = 1h : 24h : 2d + +DIVERT(null) +###-------------------------------------------------------------------------- +### Access control lists. + +SECTION(global, acl-after) +SECTION(global, acl)m4_dnl +acl_smtp_helo = helo +SECTION(acl, misc)m4_dnl +helo: + require message = The other one has bells on + verify = helo + + accept + +SECTION(global, acl)m4_dnl +acl_smtp_mail = mail +SECTION(acl, mail)m4_dnl +mail: + + ## Always allow the empty sender, so that we can receive bounces. + accept senders = : + + ## 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 + + ## If this is directly from a client then hack on it for a while. + warn condition = ${if eq{$acl_c_mode}{submission}} + control = submission + +SECTION(acl, mail-tail)m4_dnl + ## And we're done. + accept + +SECTION(global, acl)m4_dnl +acl_smtp_connect = connect +SECTION(acl, connect)m4_dnl +connect: +SECTION(acl, connect-tail)m4_dnl + warn acl = check_submission + accept + +check_submission: + ## See whether this message needs hacking on. + accept !hosts = +localnet + !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. + deny message = Relaying not permitted + !hosts = CONF_relay_clients + !authenticated = * + !domains = +known + + ## Ensure that the recipient is routable. + require message = Invalid recipient \ + ($recipient_verify_failure; $acl_verify_message) + verify = recipient + +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)m4_dnl +expn_vrfy: + accept hosts = +trusted + deny message = Suck it and see + +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:>) + +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. Try to do TLS, and don't worry +## too much if it's not very secure: the alternative is sending in plaintext +## anyway. +smtp: + driver = smtp + tls_require_ciphers = CONF_acceptable_ciphers + tls_dh_min_bits = 1020 + tls_tempfail_tryclear = true + +## 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.cert + 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 = 3070 + 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 + USER_DELIVERY + +## Transports for user filters. +mailbox: + driver = appendfile + USER_DELIVERY + +maildir: + driver = appendfile + maildir_format = true + USER_DELIVERY + +pipe: + driver = pipe + return_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 -------------------------------------------------- diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..5901edc --- /dev/null +++ b/config.m4 @@ -0,0 +1,80 @@ +### -*-m4-*- +### +### Basic configuration 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. + +## Master domain name. +DEFCONF(master_domain, distorted.org.uk) + +## The smarthost for satellite hosts. +DEFCONF(smarthost, mail.distorted.org.uk) + +## The user who runs verification filters. +DEFCONF(filter_user, Debian-exim) + +## Where the spam filter is. +DEFCONF(spamd_address, 172.29.199.179) +DEFCONF(spamd_port, 783) + +## Default spam limit for incoming mail (multiplied by ten). +DEFCONF(spam_max, 50) + +## Which interfaces to listen on. Exim checks for the literal string `::0' +## when setting things up: don't use `::', or we'll be tripped up by Linux's +## demented non-`IPV6_V6ONLY' behaviour. +DEFCONF(interfaces, m4_ifelse(MODE, satellite, 127.0.0.1 ; ::1, + 0.0.0.0 ; ::0)) + +## Submission port number. (This is sometimes tweaked for testing.) +DEFCONF(submission_port, 587) + +## Locations of other configuration files. +DEFCONF(sysconf_dir, /etc/mail) +DEFCONF(userconf_dir, $home/.mail) +DEFCONF(alias_file, /etc/aliases) +DEFCONF(ca_dir, /etc/ca) + +## User address suffix handling. +DEFCONF(user_suffix_list, -* : +*) +DEFCONF(user_extaddr_regexp, $acl_c_user([-+@]|\$)) +DEFCONF(user_extaddr_fixup, ${sg {$local_part_suffix}{^[-+]}{}}) + +## Other hosts allowed to relay mail through us. +DEFCONF(relay_clients, +trusted) + +## TLS-related settings. We're assuming GNUTLS here, rather than OpenSSL. +## For local connections we are very strict. For random clients, we try +## fairly hard to encourage any kind of crypto on the grounds that probably +## nobody can verify our certificate anyway. +DEFCONF(good_ciphers, NONE<::>m4_dnl +:+VERS-TLS1.2:+VERS-TLS1.1<::>m4_dnl +:+DHE-RSA:+DHE-DSS<::>m4_dnl +:+AES-256-CBC:+AES-128-CBC<::>m4_dnl +:+SHA256<::>m4_dnl +:+SIGN-RSA-SHA512:+SIGN-RSA-SHA384:+SIGN-RSA-SHA256:+SIGN-DSA-SHA256<::>m4_dnl +:+CTYPE-X.509<::>m4_dnl +:+COMP-NULL<::>m4_dnl +) +DEFCONF(acceptable_ciphers, NORMAL<::>m4_dnl +:-MD5<::>m4_dnl +) + +###----- That's all, folks -------------------------------------------------- diff --git a/defs.m4 b/defs.m4 new file mode 100644 index 0000000..f9b3333 --- /dev/null +++ b/defs.m4 @@ -0,0 +1,106 @@ +m4_divert(-1)m4_dnl ### -*-m4-*- +### +### Basic definitions 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. + +m4_changequote(<:, :>) +m4_changecom(<:#:#:>) + +###-------------------------------------------------------------------------- +### Output file preamble and postamble. + +m4_divert(0)m4_dnl +### -*-conf-*- GENERATED FROM /etc/mail/m4/*.m4: DO NOT EDIT! +### +### Exim configuration for distorted.org.uk. + +m4_divert(1000)m4_dnl +### GENERATED FROM exim4.conf.m4: DO NOT EDIT! +m4_divert(-1) + +###-------------------------------------------------------------------------- +### Useful macros. + +## ONEOF(arg, ...) +## +## Output the first of its arguments that is non-empty. +m4_define(<:ONEOF:>, <:m4_ifelse(<:$#:>, <:1:>, <:$1:>, + <:$1:>, <::>, <:ONEOF(m4_shift($@)):>, <:$1:>):>) + +## DEFCONF(CONF, DEFAULT) +## +## Define config variable CONF, assigning it the DEFAULT value if not +## overridden by `SETCONF'. +m4_define(<:DEFCONF:>, +<:m4_ifdef(<:CONF_$1:>, <::>, +<:m4_define(<:CONF_$1:>, <:$2:>):>):>) + +## SETCONF(CONF, VALUE) +## +## Set config variable VALUE. +m4_define(<:SETCONF:>, <:m4_define(<:CONF_$1:>, <:$2:>):>) + +## FOREACH(what, list) +## +## The LIST is a comma-separated list of things, like an m4 argument list. +## For each item in the list, expand WHAT as if it's the body of a macro with +## the list item as its arguments. In other words, the list item itself can +## be a list of comma-separated items, which are available as $1, $2, ..., +## within WHAT. +m4_define(<:_FOREACH:>, <:m4_dnl +m4_ifelse(<:$#:>, <:1:>, <:_foreach_func($1):>, + <:_foreach_func($1)<::>_FOREACH(m4_shift($@)):>):>) +m4_define(<:FOREACH:>, <:m4_dnl +m4_pushdef(<:_foreach_func:>, <:$1:>)m4_dnl +_FOREACH($2)<::>m4_dnl +m4_popdef(<:_foreach_func:>):>) + +m4_define(<:DIVERT:>, <:m4_dnl +m4_divert(m4_indir(<:_div:$1:>))m4_dnl +:>) + +m4_define(<:SECTION:>, <:m4_dnl +DIVERT(<:$1:>)m4_dnl +m4_ifdef(<:_done:$1:>, <::>, <:m4_dnl +###-------------------------------------------------------------------------- +m4_ifdef(<:_head:$1:>, <:m4_indir(<:_head:$1:>):>, <:begin $1:>) + +m4_define(<:_done:$1:>, <:1:>):>)m4_dnl +m4_ifelse(<:$2:>, <::>, <::>, <:m4_dnl +DIVERT(<:$1/$2:>)m4_dnl +m4_ifdef(<:_done:$1/$2:>, <::>, <:m4_dnl +m4_ifdef(<:_head:$1/$2:>, <:<:##:> m4_indir(<:_head:$1/$2:>) +:>)m4_define(<:_done:$1/$2:>):>):>):>) + +m4_define(<:LOOKUP_DOMAIN:>, + <:if exists{CONF_sysconf_dir/domains.conf} \ + {${lookup {$1}partial0-lsearch{CONF_sysconf_dir/domains.conf} \ + m4_ifelse(<:$2$3:>, <::>, <::>, + <:$2:>, <::>, <:{$value}$3:>, + <:$2$3:>)}} \ + $3:>) + +m4_define(<:KV:>, <:${extract {$1}{$value}$2}:>) +m4_define(<:DOMKV:>, <:${extract {$1}{$domain_data}$2}:>) + +m4_divert(999)m4_dnl +###----- That's all, folks -------------------------------------------------- +m4_divert(-1) diff --git a/divmap.m4 b/divmap.m4 new file mode 100644 index 0000000..c7ed1d7 --- /dev/null +++ b/divmap.m4 @@ -0,0 +1,128 @@ +### -*-m4-*- +### +### Diversion map 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. + +###-------------------------------------------------------------------------- +### Numbering machinery. + +## Rather than maintain the diversion numbers by hand, this chunk of Emacs +## Lisp will recompute them when they've been edited. All you have to do is +## declare the diversions in the correct order. +(save-excursion + (save-match-data + (goto-char (point-min)) + (let ((next-div 0) div) + (while (re-search-forward (concat "\\(DEFDIVERSION(\\)" ;1 + "\\([^,]+\\)" ;2 = diversion + "\\(,\\s-*\\)" ;3 + "\\([0-9]+\\)" ;4 = number + "\\([,)]\\)") ;5 + nil t) + (setq div (match-string 2)) + (if (not (string-match-p "/" div)) + (setq next-div (* 100 (/ (+ next-div 100) 100)))) + (replace-match (concat "\\1\\2\\3" + (int-to-string next-div) + "\\5")) + (setq next-div (+ next-div 2)))))) + +m4_define(<:_div:null:>, <:-1:>) + +m4_define(<:DEFDIVERSION:>, <:m4_dnl + m4_define(<:_div:$1:>, <:$2:>)m4_dnl + m4_ifelse(<:$3:>, <::>, <::>, <:m4_dnl + m4_define(<:_head:$1:>, <:$3:>):>):>) + +###-------------------------------------------------------------------------- +### The diversion map. + +## header 0 + +DEFDIVERSION(global, 100, ### Global settings.) +DEFDIVERSION(global/logging, 102, Logging.) + +DEFDIVERSION(global/lists, 104) + +DEFDIVERSION(global/misc, 106, Miscellaneous.) +DEFDIVERSION(global/param, 108, Exim parameters.) +DEFDIVERSION(global/priv, 110, Privilege controls.) +DEFDIVERSION(global/frozen, 112, Frozen messages.) +DEFDIVERSION(global/lookups, 114, Data lookups.) +DEFDIVERSION(global/msgid, 116, Message ids.) +DEFDIVERSION(global/perl, 118, Embedded Perl startup.) +DEFDIVERSION(global/daemon, 120, Daemon.) +DEFDIVERSION(global/resource, 122, Resource control.) +DEFDIVERSION(global/policy, 124, Policy controls.) +DEFDIVERSION(global/callout, 126, Callout cache.) +DEFDIVERSION(global/tls, 128, TLS.) +DEFDIVERSION(global/users, 130, Local user handling.) +DEFDIVERSION(global/incoming, 132, + All incoming messages (SMTP and non-SMTP).) +DEFDIVERSION(global/non-smtp, 134, Non-SMTP incoming messages.) +DEFDIVERSION(global/smtp, 136, Incoming SMTP messages.) +DEFDIVERSION(global/process, 138, Processing messages.) +DEFDIVERSION(global/filter, 140, System filter.) +DEFDIVERSION(global/routing, 142, Routing and delivery.) +DEFDIVERSION(global/bounce, 144, Bounce and warning messages.) +DEFDIVERSION(global/acl, 146, Access control lists.) +DEFDIVERSION(global/acl-after, 148) + +DEFDIVERSION(acl, 200) +DEFDIVERSION(acl/connect, 202) +DEFDIVERSION(acl/connect-hooks, 204) +DEFDIVERSION(acl/connect-tail, 206) +DEFDIVERSION(acl/mail, 208) +DEFDIVERSION(acl/mail-hooks, 210) +DEFDIVERSION(acl/mail-tail, 212) +DEFDIVERSION(acl/rcpt, 214) +DEFDIVERSION(acl/rcpt-hooks, 216) +DEFDIVERSION(acl/rcpt-tail, 218) +DEFDIVERSION(acl/data, 220) +DEFDIVERSION(acl/data-spam, 222) +DEFDIVERSION(acl/data-tail, 224) +DEFDIVERSION(acl/misc, 226) + +DEFDIVERSION(auth, 300, begin authenticators) + +DEFDIVERSION(routers, 400) +DEFDIVERSION(routers/route, 402) +DEFDIVERSION(routers/remote, 404) +DEFDIVERSION(routers/virtual, 406) +DEFDIVERSION(routers/real, 408) +DEFDIVERSION(routers/alias, 410) +DEFDIVERSION(routers/allspam, 412) +DEFDIVERSION(routers/dispatch, 414) +DEFDIVERSION(routers/forward, 416) +DEFDIVERSION(routers/deliver, 418) + +DEFDIVERSION(transports, 500) + +DEFDIVERSION(retry, 600) +DEFDIVERSION(retry/misc, 602) +DEFDIVERSION(retry/default, 604) + +DEFDIVERSION(rewrite, 700) + +## warning trailer 1000 + +DIVERT(null) +###----- That's all, folks -------------------------------------------------- diff --git a/exchange.m4 b/exchange.m4 new file mode 100644 index 0000000..08fe4a0 --- /dev/null +++ b/exchange.m4 @@ -0,0 +1,92 @@ +### -*-m4-*- +### +### Transmission to remote hosts 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. + +###-------------------------------------------------------------------------- +### Listen for incoming connections. + +SECTION(global, tls)m4_dnl +tls_certificate = CONF_sysconf_dir/server.cert +tls_privatekey = CONF_sysconf_dir/server.key +tls_advertise_hosts = * +tls_dhparam = CONF_ca_dir/dh-param.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) +###-------------------------------------------------------------------------- +### Check source addresses for apparently local senders. + +SECTION(acl, mail-hooks)m4_dnl + ## Check that a submitted message's sender address is allowable. + require acl = mail_client_addr + + ## 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 = * + +SECTION(acl, misc)m4_dnl +mail_client_addr: + + ## If this is a message submission then that's handled elsewhere. + accept condition = ${if eq{$acl_c_mode}{submission}} + + ## Make sure that the sender matches the client address. + require message = Client host invalid for sender domain + hosts = ${LOOKUP_DOMAIN($sender_address_domain, + {KV(hosts, {$value}{+allnets})}, + {${if match_domain {$sender_address_domain} \ + {+public} \ + {+allnets}{! +allnets}}})} + + ## OK. + accept + +DIVERT(null) +###-------------------------------------------------------------------------- +### The obvious trivial router. + +SECTION(routers, remote)m4_dnl +## Send mail on to a host in our own network. We must apply extra security. +local: + driver = dnslookup + domains = ! +known : *.CONF_master_domain + self = fail + transport = smtp_local + no_more + +## Send mail on to unknown hosts. +remote: + driver = dnslookup + domains = ! +known + self = fail + transport = smtp + no_more + +DIVERT(null) +###----- That's all, folks -------------------------------------------------- diff --git a/lists.m4 b/lists.m4 new file mode 100644 index 0000000..85d48c2 --- /dev/null +++ b/lists.m4 @@ -0,0 +1,57 @@ +### -*-m4-*- +### +### Lists of addresses and suchlike 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. + +SECTION(global, lists)m4_dnl +## Definitions for known networks. +hostlist localnet = <; \ + 127.0.0.0/8 ; ::1 +hostlist border = <; \ + 62.49.204.144/28 ; 2001:470:1f09:1b98::/64 ; \ + 212.13.198.64/28 ; 2001:ba8:0:1d9::/64 +hostlist trusted = <; \ + +localnet ; +border ; \ + 172.29.199.0/24 ; 2001:ba8:1d9::/49 ; 2001:470:9740::/49 +hostlist allnets = <; \ + +localnet ; +border ; \ + 172.29.198.0/23 ; 2001:ba8:1d9::/48 ; 2001:470:9740::/48 + +## Domains we're authoritative for. +domainlist thishost = @ : @[] : \ + ${map {${extract {${extract {1}{.}{$primary_hostname}}} \ + { telecaster=tele \ + stratocaster=strat }}} \ + {$item.$qualify_domain}} +domainlist public = +thishost : distorted.org.uk +domainlist known = +public : \ + ${if exists{CONF_sysconf_dir/domains.conf} \ + {partial0-lsearch; CONF_sysconf_dir/domains.conf} \ + {}} + +## Some magic lists used because `match_local_parts' and friends don't expand +## their right-hand sides. +localpartlist dom_users = ${expand:KV(users, {$value}{*})} +localpartlist dom_locals = ${expand:KV(locals, {$value}{+user_extaddr})} +localpartlist user_extaddr = ^CONF_user_extaddr_regexp + +DIVERT(null) +###----- That's all, folks -------------------------------------------------- diff --git a/local.m4 b/local.m4 new file mode 100644 index 0000000..78b3e64 --- /dev/null +++ b/local.m4 @@ -0,0 +1,113 @@ +### -*-m4-*- +### +### Local senders and recipients 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. + +m4_define(<:FILTER_LOCALUSER:>, + <:FILTER_BASE + check_local_user + ignore_enotdir + sieve_useraddress = $local_part + sieve_subaddress = CONF_user_extaddr_fixup + sieve_vacation_directory = CONF_userconf_dir/vacation + condition = ${if exists {<:$1:>}} + file = <:$1:>:>) + +###-------------------------------------------------------------------------- +### Be flexible about originator addresses, as long as they probably work. + +SECTION(global, priv)m4_dnl +local_from_suffix = CONF_user_suffix_list + +SECTION(global, process)m4_dnl +## Restrict users to plausible envelope sender addresses. This is +## surprisingly fiddly. What I actually want to say is that the local part +## must match ^$sender_ident(\$|-) and the domain part must match an +## appropriate domain; but writing a conjunction is rather tricky. And so we +## must burn some addresslist variables. +addresslist wrong_local = ! +user_extaddr +addresslist wrong_domain = ! *@+public +addresslist wrong_address = +wrong_local : +wrong_domain +untrusted_set_sender = : \ + ${LOOKUP_DOMAIN($sender_address_domain, + {${if and {{match_local_part {$sender_ident} {+dom_users}} \ + {match_local_part {$sender_address_local_part} \ + {+dom_locals}}} \ + {*}}})} : \ + ! +wrong_address + +###-------------------------------------------------------------------------- +### Forwarding and redirection for incoming mail. + +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 + data = ${lookup {$local_part} lsearch {CONF_alias_file}} + +SECTION(routers, real)m4_dnl +## A special hack to get mail to a user who has a forward file. Only for +## special effects. +real: + driver = accept + check_local_user + local_part_prefix = real- + transport = deliver + condition = ${if match_ip {$sender_host_address} \ + {<; ; 127.0.0.1 ; ::1}} + +SECTION(routers, forward)m4_dnl +## Handle user forward files. Each user is granted an arbitrary number of +## additional mailboxes named USER-SUFFIX. Such addresses are handled by a +## filter file `~/.mail/forward.suffix' in the USER's home directory. The +## filter may reject the incoming message (which is reported as an SMTP +## rejection if possible). Mail sent directly to the user is handled through +## `~/.mail/forward', or `~/.forward', or if neither of those exists, by +## writing the message to `/var/mail/USER'. +filter_verify: + FILTER_LOCALUSER(CONF_userconf_dir/forward.suffix) + FILTER_VERIFY + local_part_suffix = CONF_user_suffix_list +filter_suffix: + FILTER_LOCALUSER(CONF_userconf_dir/forward.suffix) + local_part_suffix = CONF_user_suffix_list + FILTER_TRANSPORTS +filter: + FILTER_LOCALUSER(CONF_userconf_dir/forward) + FILTER_TRANSPORTS +dot_forward: + FILTER_LOCALUSER($home/.forward) + FILTER_TRANSPORTS + +SECTION(routers, deliver)m4_dnl +## Deliver mail to a user, in the absence of any other instructions. +deliver: + driver = accept + check_local_user + transport = deliver + cannot_route_message = Unknown local part + +DIVERT(null) +###----- That's all, folks -------------------------------------------------- diff --git a/satellite.m4 b/satellite.m4 new file mode 100644 index 0000000..088dded --- /dev/null +++ b/satellite.m4 @@ -0,0 +1,39 @@ +### -*-m4-*- +### +### Satellite hosts 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. + +SECTION(acl, connect-hooks)m4_dnl + ## Check the caller. + deny message = This is not a public mail server + hosts = ! +allnets + +SECTION(routers, remote)m4_dnl +satellite: + driver = manualroute + transport = smtp_local + domains = ! +thishost + host_find_failed = defer + same_domain_copy_routing = true + route_list = {$spam_score_int}{$acl_m_spam_limit} \ + {true}{false}} + + ## Insert headers from the spam check now that we've decided to + ## accept the message. + warn + ## Convert the limit (currently 10x fixed point) into a + ## decimal for presentation. + set acl_m_spam_limit_presentation = \ + ${sg{$acl_m_spam_limit}{\N(\d)$\N}{.\$1}} + + ## Convert the report into something less obnoxious. Plain + ## old SpamAssassin has an `X-Spam-Status' header which + ## lists the matched rules and provides some other basic + ## information. Try to extract something similar from the + ## report. + ## + ## This is rather fiddly. + + ## Firstly, escape angle brackets, because we'll be using + ## them for our own purposes. + set acl_m_spam_tests = ${sg{$spam_report}{([!<>])}{!\$1}} + + ## Trim off the blurb paragraph and the preview. The rest + ## should be fairly well behaved. Wrap double angle- + ## brackets around the remainder; these can't appear in the + ## body because we escaped them all earlier. + set acl_m_spam_tests = \ + ${sg{$acl_m_spam_tests} \ + {\N^(?s).*\n Content analysis details:(.*)$\N} \ + {<<\$1>>}} + + ## Extract the information about the matching rules and + ## their scores. Leave `<<...>>' around everything else. + set acl_m_spam_tests = \ + ${sg{$acl_m_spam_tests} \ + {\N(?s)\n\s*([\d.]+)\s+([-\w]+)\s\N} \ + {>>\$2:\$1,<<}} + + ## Strip everything still in `<<...>>' pairs, including any + ## escaped characters inside. + set acl_m_spam_tests = \ + ${sg{$acl_m_spam_tests}{\N(?s)<<([^!>]+|!.)*>>\N}{}} + + ## Trim off a trailing comma. + set acl_m_spam_tests = ${sg{$acl_m_spam_tests}{,\s*\$}{}} + + ## Undo the escaping. + set acl_m_spam_tests = ${sg{$acl_m_spam_tests}{!(.)}{\$1}} + + ## Insert the headers. + add_header = X-SpamAssassin-Score: \ + $spam_score/$acl_m_spam_limit_presentation \ + ($spam_bar) + add_header = X-SpamAssassin-Status: \ + score=$spam_score, \ + limit=$acl_m_spam_limit_presentation, \n\t\ + tests=$acl_m_spam_tests + + + ## We're good. + accept + +DIVERT(null) +###----- That's all, folks -------------------------------------------------- diff --git a/vhost.m4 b/vhost.m4 new file mode 100644 index 0000000..07ca79e --- /dev/null +++ b/vhost.m4 @@ -0,0 +1,69 @@ +### -*-m4-*- +### +### Virtual host support 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. + +m4_define(<:VHOST:>, + <:domains = partial0-lsearch; CONF_sysconf_dir/domains.conf + condition = DOMKV($1):>) +m4_define(<:VHOST_FILTER:>, + <:FILTER_BASE + VHOST(<:$1:>) + $2 = ${expand:DOMKV($1, {$value} fail)}:>) +m4_define(<:VHOST_USER:>, + <:user = DOMKV(owner, {$value}{CONF_filter_user}) + errors_to = DOMKV(errors_to, {$value} fail):>) + +###-------------------------------------------------------------------------- +### Routers and transports for virtual hosting. + +SECTION(routers, route)m4_dnl +## If we're a front-end for some other domain, or we have special information +## about the domain, then pass stuff along as instructed. +route: + driver = manualroute + self = fail + VHOST(route) + route_data = DOMKV(route, {$value} fail) + same_domain_copy_routing = true + transport = smtp + no_more + +SECTION(routers, virtual)m4_dnl +## Remap recipients according to the virtual host's instructions. This must +## be done in two passes, so that we can identify the correct user's spam +## threshold during address verification. +virtual_verify_data: + VHOST_FILTER(redirect, data) + FILTER_VERIFY +virtual_verify_file: + VHOST_FILTER(filter, file) + FILTER_VERIFY + +virtual_filter_data: + VHOST_FILTER(redirect, data) + VHOST_USER +virtual_filter_file: + VHOST_FILTER(filter, file) + VHOST_USER + +DIVERT(null) +###----- That's all, folks --------------------------------------------------