From fe4ccf2c3eb4d2a87f813649a532090560a7a1b3 Mon Sep 17 00:00:00 2001 From: Matthew Saunders Brown Date: Thu, 5 Dec 2024 08:33:33 -0800 Subject: [PATCH] drop Ubuntu support, install works for Debian 12 only --- etc/exim4/exim4-jammy.conf | 1239 ----------------- etc/exim4/{exim4-bookworm.conf => exim4.conf} | 0 install-mm3.sh | 103 ++ install.sh | 73 +- 4 files changed, 126 insertions(+), 1289 deletions(-) delete mode 100644 etc/exim4/exim4-jammy.conf rename etc/exim4/{exim4-bookworm.conf => exim4.conf} (100%) create mode 100755 install-mm3.sh diff --git a/etc/exim4/exim4-jammy.conf b/etc/exim4/exim4-jammy.conf deleted file mode 100644 index 5799946..0000000 --- a/etc/exim4/exim4-jammy.conf +++ /dev/null @@ -1,1239 +0,0 @@ -# $Id: exim.conf 228 2005-07-27 17:06:36Z root $ -###################################################################### -# Runtime configuration file for Exim # -###################################################################### - -disable_ipv6 = true -keep_environment = -add_environment = PATH=/usr/sbin:/usr/bin:/sbin:/bin -smtp_enforce_sync = false -bounce_message_file = /etc/exim4/bounce_message_text - -smtp_accept_max = 50 -smtp_accept_max_per_host = 10 -smtp_accept_queue_per_connection = 50 - -.include /etc/exim4/exim_local.conf - -tls_advertise_hosts = * -tls_certificate = ${if exists{/etc/ssl/letsencrypt/${sg{${tls_sni}}{^smtp\.}{mail.}}.pem}{/etc/ssl/letsencrypt/${sg{${tls_sni}}{^smtp\.}{mail.}}.pem}} - -tls_on_connect_ports = 465 -daemon_smtp_ports = 25 : 465 : 587 - -# This is a default configuration file which will operate correctly in -# uncomplicated installations. Please see the manual for a complete list -# of all the runtime configuration options that can be included in a -# configuration file. There are many more than are mentioned here. The -# manual is in the file doc/spec.txt in the Exim distribution as a plain -# ASCII file. Other formats (PostScript, Texinfo, HTML, PDF) are available -# from the Exim ftp sites. The manual is also online at the Exim web sites. - - -# This file is divided into several parts, all but the first of which are -# headed by a line starting with the word "begin". Only those parts that -# are required need to be present. Blank lines, and lines starting with # -# are ignored. - - -########### IMPORTANT ########## IMPORTANT ########### IMPORTANT ########### -# # -# Whenever you change Exim's configuration file, you *must* remember to # -# HUP the Exim daemon, because it will not pick up the new configuration # -# until you do. However, any other Exim processes that are started, for # -# example, a process started by an MUA in order to send a message, will # -# see the new configuration as soon as it is in place. # -# # -# You do not need to HUP the daemon for changes in auxiliary files that # -# are referenced from this file. They are read every time they are used. # -# # -# It is usually a good idea to test a new configuration for syntactic # -# correctness before installing it (for example, by running the command # -# "exim -C /config/file.new -bV"). # -# # -########### IMPORTANT ########## IMPORTANT ########### IMPORTANT ########### - - - -###################################################################### -# MAIN CONFIGURATION SETTINGS # -###################################################################### - -# Specify your host's canonical name here. This should normally be the fully -# qualified "official" name of your host. If this option is not set, the -# uname() function is called to obtain the name. In many cases this does -# the right thing and you need not set anything explicitly. - -queue_only_load = 3 -log_selector = +all - -# The next three settings create two lists of domains and one list of hosts. -# These lists are referred to later in this configuration using the syntax -# +local_domains, +relay_to_domains, and +relay_from_hosts, respectively. They -# are all colon-separated lists: - -domainlist local_domains = ${lookup mysql{SELECT domain FROM vm_domains WHERE domain='${quote_mysql:$domain}' AND status = '1'}} -domainlist relay_to_domains = /etc/exim4/relay_domains -hostlist relay_from_hosts = 127.0.0.1 -hostlist skip_greylisting_hosts = /etc/exim4/skip_greylisting_hosts - -# overide default sender checks -untrusted_set_sender = * -local_from_check = false -local_sender_retain = true - -# trusted users, needed by spamcheck -trusted_users = mail - -# local whitelist check macro: -WHITELISTED = ${lookup mysql{\ - SELECT prefid FROM sa_userpref \ - WHERE (username = '${quote_mysql:$local_part@$domain}' \ - OR username = '\$GLOBAL' \ - OR username = CONCAT('%','${quote_mysql:$domain}')) \ - AND preference = 'whitelist_from' \ - AND (value = '${quote_mysql:$sender_address}' \ - OR value = CONCAT('\*@','${quote_mysql:$sender_address_domain}')) \ - }{true}{false}} - -# greylist options -# these need to be valid as xxx in mysql's DATE_ADD(..,INTERVAL xxx) -# not valid, for example, are plurals: "2 HOUR" instead of "2 HOURS" -GREYLIST_INITIAL_DELAY = 4 MINUTE -GREYLIST_INITIAL_LIFETIME = 12 HOUR -GREYLIST_WHITE_LIFETIME = 36 DAY -GREYLIST_BOUNCE_LIFETIME = 0 HOUR - -# you can change the table names -GREYLIST_TABLE=vm_greylisting -GREYLIST_RESENDERS_TABLE=vm_greylisting_resenders - -# database macros -GREYLIST_TEST = SELECT CASE \ - WHEN now() > block_expires THEN "accepted" \ - ELSE "deferred" \ - END AS result, id \ - FROM GREYLIST_TABLE \ - WHERE (now() < record_expires) \ - AND (sender = '${quote_mysql:$sender_address}' \ - OR (type='MANUAL' \ - AND ( sender IS NULL \ - OR sender = '${quote_mysql:*@$sender_address_domain}' \ - ) \ - ) \ - ) \ - AND (recipient = '${quote_mysql:$local_part@$domain}' \ - OR (type = 'MANUAL' \ - AND ( recipient IS NULL \ - OR recipient = '${quote_mysql:$local_part@*}' \ - OR recipient = '${quote_mysql:*@$domain}' \ - ) \ - ) \ - ) \ - AND (relay_hostname = '${quote_mysql:$acl_m_sender}' \ - OR (type='MANUAL' \ - AND relay_hostname IS NULL) \ - ) \ - ORDER BY result DESC LIMIT 1 - -GREYLIST_ADD = INSERT INTO GREYLIST_TABLE \ - (relay_hostname, relay_ip, sender, recipient, block_expires, \ - record_expires, create_time, type) \ - VALUES ( '${quote_mysql:$acl_m_sender}', \ - '${quote_mysql:$sender_host_address}', \ - '${quote_mysql:$sender_address}', \ - '${quote_mysql:$local_part@$domain}', \ - DATE_ADD(now(), INTERVAL GREYLIST_INITIAL_DELAY), \ - DATE_ADD(now(), INTERVAL GREYLIST_INITIAL_LIFETIME), \ - now(), \ - 'AUTO' \ -) - -GREYLIST_DEFER_HIT = UPDATE GREYLIST_TABLE \ - SET blockcount=blockcount+1 \ - WHERE id = $acl_m9 - -GREYLIST_OK_COUNT = UPDATE GREYLIST_TABLE \ - SET passcount=passcount+1 \ - WHERE id = $acl_m9 - -GREYLIST_OK_NEWTIME = UPDATE GREYLIST_TABLE \ - SET record_expires = DATE_ADD(now(), INTERVAL GREYLIST_WHITE_LIFETIME) \ - WHERE id = $acl_m9 AND type='AUTO' - -GREYLIST_OK_BOUNCE = UPDATE GREYLIST_TABLE \ - SET record_expires = DATE_ADD(now(), INTERVAL GREYLIST_BOUNCE_LIFETIME) \ - WHERE id = $acl_m9 AND type='AUTO' - -GREYLIST_RESENDERS_UPDATE = REPLACE INTO GREYLIST_RESENDERS_TABLE SET hostname = '${quote_mysql:$acl_m_sender}' - -# No deliveries will ever be run under the uids of these users (a colon- -# separated list). An attempt to do so causes a panic error to be logged, and -# the delivery to be deferred. This is a paranoic safety catch. Note that the -# default setting means you cannot deliver mail addressed to root as if it -# were a normal user. This isn't usually a problem, as most sites have an alias -# for root that redirects such mail to a human administrator. - -never_users = root - - -# The setting below causes Exim to do a reverse DNS lookup on all incoming -# IP calls, in order to get the true host name. If you feel this is too -# expensive, you can specify the networks for which a lookup is done, or -# remove the setting entirely. - -host_lookup = * - - -# The settings below, which are actually the same as the defaults in the -# code, cause Exim to make RFC 1413 (ident) callbacks for all incoming SMTP -# calls. You can limit the hosts to which these calls are made, and/or change -# the timeout that is used. If you set the timeout to zero, all RFC 1413 calls -# are disabled. RFC 1413 calls are cheap and can provide useful information -# for tracing problem messages, but some hosts and firewalls have problems -# with them. This can result in a timeout instead of an immediate refused -# connection, leading to delays on starting up an SMTP session. - -rfc1413_hosts = * -rfc1413_query_timeout = 0s - -# This option unfreezes frozen bounce messages after two days, tries -# once more to deliver them, and ignores any delivery failures. - -ignore_bounce_errors_after = 0s - -# This option cancels (removes) frozen messages that are older than a day. - -timeout_frozen_after = 1d - -# Treat DNS failures (SERVFAIL) as lookup failures. -# This is so that we can later reject sender addresses -# within non-existing domains, or domains for which no -# nameserver exists. -dns_again_means_nonexist = !+local_domains - -# Enable HELO verification in ACLs for all hosts -helo_try_verify_hosts = * - -# Advertise ESMTP "PIPELINING" to all hosts -pipelining_advertise_hosts = * - -acl_smtp_connect = acl_connect -acl_smtp_helo = acl_helo -acl_smtp_mail = acl_mail_from -acl_smtp_dkim = acl_check_dkim -acl_smtp_rcpt = acl_rcpt_to -acl_smtp_data = acl_data - -# set the av scanner to clamav -##av_scanner = clamd:/run/clamav/clamd.ctl - -begin acl - -# this acl returns either deny or accept -# since we use it inside a defer with acl = greylist_acl, -# accepting here makes the condition TRUE thus deferring, -# denying here makes the condition FALSE thus not deferring -# -greylist_acl: - # For regular deliveries, check greylist. - - # check greylist tuple, returning "accepted", "deferred" or "unknown" - # in acl_m8, and the record id in acl_m9 - - warn set acl_m8 = ${lookup mysql{GREYLIST_TEST}{$value}{result=unknown}} - # here acl_m8 = "result=x id=y" - - set acl_m9 = ${extract{id}{$acl_m8}{$value}{-1}} - # now acl_m9 contains the record id (or -1) - - set acl_m8 = ${extract{result}{$acl_m8}{$value}{unknown}} - # now acl_m8 contains unknown/deferred/accepted - - # check if we know a certain triple, add and defer message if not - accept - # if above check returned unknown (no record yet) - condition = ${if eq{$acl_m8}{unknown}{1}} - # then also add a record - condition = ${lookup mysql{GREYLIST_ADD}{yes}{no}} - - # check if the triple is still blocked - accept - # if above check returned deferred then defer - condition = ${if eq{$acl_m8}{deferred}{1}} - # and note it down - condition = ${lookup mysql{GREYLIST_DEFER_HIT}{yes}{yes}} - - # use a warn verb to count records that were hit - warn condition = ${lookup mysql{GREYLIST_OK_COUNT}} - - # use a warn verb to set a new expire time on automatic records, - # but only if the mail was not a bounce, otherwise set to now(). - warn !senders = : postmaster@* - condition = ${lookup mysql{GREYLIST_OK_NEWTIME}} - warn senders = : postmaster@* - condition = ${lookup mysql{GREYLIST_OK_BOUNCE}} - - deny - add_header = :at_start_rfc:X-DNS-Greylist: mail from $sender_address to $local_part@$domain accepted by greylisting - condition = ${lookup mysql{GREYLIST_RESENDERS_UPDATE}} - -# This access control list is used at the start of an incoming -# connection. The tests are run in order until the connection -# is either accepted or denied. -# -acl_connect: - - # In this pass, we do not perform any checks here. - accept - -# This access control list is used for the HELO or EHLO command in -# an incoming SMTP transaction. The tests are run in order until the -# greeting is either accepted or denied. -# -acl_helo: - - # Early rejection of brute-force bots under the name 'ylmf-pc' - deny - condition = ${if eq{$sender_helo_name}{ylmf-pc}{yes}{no}} - - # In this pass, we do not perform any checks here. - accept - -# This access control list is used for the MAIL FROM: command in an -# incoming SMTP transaction. The tests are run in order until the -# sender address is either accepted or denied. -# -acl_mail_from: - - # Accept the command. - accept - -# This access control list is used for every RCPT command in an -# incoming SMTP message. The tests are run in order until the -# recipient address is either accepted or denied. -# -acl_check_dkim: - - ###################################################################### - # DomainKeys - ###################################################################### - - # DKIM fail - accept - dkim_status = fail - logwrite = DKIM test failed: $dkim_verify_reason - add_header = :at_start_rfc:X-DKIM-Status: fail: $dkim_verify_reason - set acl_m_junk = yes - - # DKIM invalid - accept - dkim_status = invalid - logwrite = DKIM test invalid: $dkim_verify_reason - add_header = :at_start_rfc:X-DKIM-Status: invalid: $dkim_verify_reason - set acl_m_junk = yes - - # DKIM none - accept - dkim_status = none - logwrite = DKIM test none - add_header = :at_start_rfc:X-DKIM-Status: none - - # DKIM pass - accept - dkim_status = pass - logwrite = DKIM test passed - add_header = :at_start_rfc:X-DKIM-Status: passed: (address=$sender_address domain=$dkim_cur_signer), signature is good. - - # Accept the message. - accept - -acl_rcpt_to: - - # Accept mail received over local SMTP (i.e. not over TCP/IP). - # We do this by testing for an empty sending host field. - # Also accept mails received from hosts for which we relay mail. - # - # Recipient verification is omitted here, because in many - # cases the clients are dumb MUAs that don't cope well with - # SMTP error responses. - # - accept - hosts = : +relay_from_hosts - - # Get rate limit for user and log current rate. - # Hourly rate limit is extracted from db, multiplied by 10 to get daily rate limit. - # The idea being that the hourly rate limit should be a maximum, peak rate, not a sustained rate. - # The default ratelimit of 100000 is meant to be so high as to never be reached (no limit). - # A second default of 10 is set for lookup failures. This shouldn't happen unless there is a misconfiguration somewhere or a database issue. - # The primary purpose of rate limiting is fighting spam, either by an abusive user or a - # compromised account, not to restrict legitimate users. Set your defaults & limits accordignly. - warn - authenticated = * - set acl_m_ratelimit_hourly = ${lookup mysql{SELECT IFNULL(vm_mboxes.ratelimit, 100000) FROM vm_mboxes WHERE vm_mboxes.mbox="$sender_address_local_part" AND vm_mboxes.domain='$sender_address_domain'}{$value}{10}} - set acl_m_ratelimit_daily = ${eval: (10 * $acl_m_ratelimit_hourly) } - ratelimit = 0 / 1h / per_mail / strict / $authenticated_id - log_message = Sender rate is $sender_rate/$sender_rate_period for $authenticated_id - - # enforce hourly rate limit - deny - authenticated = * - ratelimit = $acl_m_ratelimit_hourly / 1h / per_mail / strict / $authenticated_id - message = Rate Limit of $acl_m_ratelimit_hourly per hour exceeded. Try again later. - log_message = $authenticated_id exceeded rate limit of $acl_m_ratelimit_hourly per hour - - # enforce daily rate limit - deny - authenticated = * - ratelimit = $acl_m_ratelimit_daily / 1d / per_mail / strict / $authenticated_id - message = Rate Limit of $acl_m_ratelimit_daily per day exceeded. Try again later. - log_message = $authenticated_id exceeded rate limit of $acl_m_ratelimit_daily per day - - # authenticated user did not exceed rate limits, accept message now - accept - authenticated = * - - ###################################################################### - # Hello checks - ###################################################################### - - # If the remote host greets with an IP address, then reject the mail. - deny - message = Message was delivered by ratware - log_message = remote host used IP address in HELO/EHLO greeting - condition = ${if isip {$sender_helo_name}{true}{false}} - - # Likewise if the peer greets with one of our own names - deny - message = Message was delivered by ratware - log_message = remote host used our name in HELO/EHLO greeting. - condition = ${if match_domain{$sender_helo_name}\ - {$primary_hostname:+local_domains}\ - {true}{false}} - - deny - message = Message was delivered by ratware - log_message = remote host did not present HELO/EHLO greeting. - condition = ${if def:sender_helo_name {false}{true}} - - # If HELO verification fails, we add a X-HELO-Warning: header in the message. - warn - !verify = helo - message = X-HELO-Warning: Remote host $sender_host_address \ - ${if def:sender_host_name {($sender_host_name) }}\ - incorrectly presented itself as $sender_helo_name - log_message = remote host presented unverifiable HELO/EHLO greeting. - - # if "!verify = helo" & $send_host_name = '' reject??? - - ###################################################################### - # Sender Address Checks - ###################################################################### - - # If we cannot verify the sender address, deny the message. - # - # You may choose to remove the "callout" option. In particular, - # if you are sending outgoing mail through a smarthost, it will not - # give any useful information. - # - # Details regarding the failed callout verification attempt are - # included in the 550 response; to omit these, change - # "sender/callout" to "sender/callout,no_details". - # -# deny -# message = <$sender_address> does not appear to be a \ -# valid sender address. -# !verify = sender/callout - - ###################################################################### - # Recipent Address Checks - ###################################################################### - - # SRS configs taken from: https://ente.limmat.ch/kb/exim/exim_v4_srs.html -## SRS checks are temporarily disabled. srsd is currently broken on Ubuntu 22.04. -## These could be updated to use "run{/usr/bin/srs" instead of readsocket. -## # Ensure only valid SRS prefixed bounce message get accepted -## deny -## senders = : -## domains = +local_domains -## local_parts = ${if match {$local_part} {(?i)\N^SRS[01][=+-]\N} {$local_part}} -## control = caseful_local_part -## condition = ${if match{${readsocket{/run/srsd/srsd.sock}{REVERSE $local_part@$domain}{5s}{\n}}}{^ERROR: .* Invalid hash at .*}} -## message = Invalid reverse path (SRS check failed on $local_part@$domain). -## -## warn -## senders = : -## domains = +local_domains -## local_parts = ${if match {$local_part} {\N^srs[01][=+-]\N} {$local_part}} -## control = caseful_local_part -## condition = ${if match{${readsocket{/run/srsd/srsd.sock}{REVERSE $local_part@$domain}{5s}{ }}}{^SRS: Case insensitive hash match detected. Someone smashed case in the local-part. .*}} -## log_message = SRS hash smashed on the way for $local_part@$domain by case insensitive MTA. -## -## # this is for debugging only. can be safely removed any time -## warn -## senders = : -## domains = +local_domains -## local_parts = ${if match {$local_part} {(?i)\N^SRS[01][=+-]\N} {$local_part}} -## control = caseful_local_part -## condition = ${if !match{${readsocket{/run/srsd/srsd.sock}{REVERSE $local_part@$domain}{5s}{\n}}}{^ERROR: .* Invalid hash at .*}} -## log_message = Incoming SRS bounce to $local_part@$domain - - # Deny if the local part contains @ or % or / or | or !. These are - # rarely found in genuine local parts, but are often tried by people - # looking to circumvent relaying restrictions. - # - # Also deny if the local part starts with a dot. Empty components - # aren't strictly legal in RFC 2822, but Exim allows them because - # this is common. However, actually starting with a dot may cause - # trouble if the local part is used as a file name (e.g. for a - # mailing list). - # - deny - local_parts = ^.*[@%!/|] : ^\\. - - # Drop the connection if the envelope sender is empty, but there is - # more than one recipient address. Legitimate DSNs are never sent - # to more than one address. - # - drop - message = Legitimate bounces are never sent to more than one recipient. - log_message = Legitimate bounces are never sent to more than one recipient (count: $recipients_count). - senders = : postmaster@* - condition = $recipients_count - - # Reject the recipient address if it is not in a domain for - # which we are handling mail. - # - deny - message = relay not permitted - !domains = +local_domains : +relay_to_domains - - # Reject the recipient if it is not a valid mailbox. - # If the mailbox is not on our system (e.g. if we are a - # backup MX for the recipient domain), then perform a - # callout verification; but if the destination server is - # not responding, accept the recipient anyway. - # - deny - message = unknown user - domains = +local_domains - !domains = +relay_to_domains - !verify = recipient/callout=no_cache,10s,defer_ok - - # skip any further checks if the address is whitelisted - accept - condition = WHITELISTED - logwrite = From: $sender_address To: $local_part@$domain is whitelisted in sa_userpref - add_header = :at_start_rfc:X-Whitelist-Flag: YES - set acl_m_whitelist = yes - - ###################################################################### - # DNS checks - ###################################################################### - # - # The results of these checks are cached, so multiple recipients - # does not translate into multiple DNS lookups. - # - - # check whitelists - # a match will: - # add X-DNS-Whitelist header - # skip the rest of the checks (DNS blacklist/greylist, SPF) - -## as of Aug 2019 swl.spamhaus.org appears to be offline pending redesign -## list.dnswl.org does not work with large nameresolvers (over 100k queries / 24 hours) -## accept -## domains = +local_domains -## dnslists = swl.spamhaus.org : list.dnswl.org&0.0.0.2 -## logwrite = $sender_host_address is whitelisted in $dnslist_domain ${if def:dnslist_text {($dnslist_text)}}, adding X-DNS-Whitelist header -## add_header = :at_start_rfc:X-DNS-Whitelist: $sender_host_address is listed in $dnslist_domain ${if def:dnslist_text {($dnslist_text)}} - - # Check SPF. Failures are marked as Junk and accepted - this skips further checks (e.g. DNSBL) and filters messages to the Junk folder - accept - !senders = : - condition = ${run{/usr/bin/spfquery --scope mfrom \ - --identity ${quote:$sender_address} \ - --ip-address ${quote:$sender_host_address}} \ - {no}{${if eq {$runrc}{1}{yes}{no}}}} - add_header = Received-SPF: fail - set acl_m_junk = yes - #log_message = SPF check failed. - - warn - condition = ${if eq {$runrc}{0}{yes}{no}} - add_header = Received-SPF: pass - #log_message = SPF check: pass - - warn - condition = ${if eq {$runrc}{2}{yes}{no}} - add_header = Received-SPF: softfail - set acl_m_junk = yes - #log_message = SPF check: softfail - - warn - condition = ${if eq {$runrc}{3}{yes}{no}} - add_header = Received-SPF: neutral - #log_message = SPF check: neutral - - warn - condition = ${if eq {$runrc}{4}{yes}{no}} - add_header = Received-SPF: permerror - #log_message = SPF check: permerror - - warn - condition = ${if eq {$runrc}{5}{yes}{no}} - add_header = Received-SPF: temperror - log_message = Temporary DNS error while checking SPF record. - - warn - condition = ${if eq {$runrc}{6}{yes}{no}} - add_header = Received-SPF: none - #log_message = SPF check: none - - warn - condition = ${if >{$runrc}{6}{yes}{no}} - log_message = Unexpected error in SPF check. spfquery returned $runrc - - # check DNSBL(s) and if found add header for filtering to Junk - accept - !condition = ${if eq {$header_X-Whitelist-Flag:}{YES}} - dnslists = zen.spamhaus.org!&127.255.255.0 - logwrite = Warning: $sender_host_address is listed in DNSBL $dnslist_domain ${if def:dnslist_text {($dnslist_text)}} - add_header = :at_start_rfc:X-DNS-Blacklist: $sender_host_address is listed in $dnslist_domain ${if def:dnslist_text {($dnslist_text)}} - set acl_m_junk = yes - - # greylisting - # if $sender_host_name is set use that. run command strips of leftmost subdomain if this is a third or higher level domain - warn - condition = ${if def:sender_host_name} - set acl_m_sender = ${run{/bin/bash /etc/exim4/return-resender.sh $sender_host_name}{$value}{$sender_host_name}} - - # if $sender_host_name is not set use $sender_helo_name. if sender_helo_name not set email was already rejected (deny) earlier in this acl - warn - condition = ${if !def:sender_host_name} - set acl_m_sender = $sender_helo_name - - # bypass greylisting if sender listed in skip_greylisting_hosts - accept - hosts = +skip_greylisting_hosts - log_message = skipping greylisting due to match in skip_greylisting_hosts - - # bypass greylisting if the sending host is a known resender - accept - condition = ${lookup mysql{SELECT id FROM GREYLIST_RESENDERS_TABLE WHERE hostname = '${quote_mysql:$acl_m_sender}'}{yes}{no}} - condition = ${lookup mysql{UPDATE GREYLIST_RESENDERS_TABLE SET count=count+1, timestamp = NOW() WHERE hostname = '${quote_mysql:$acl_m_sender}'}{yes}{yes}} - add_header = :at_start_rfc:X-DNS-Greylist: known resender - logwrite = skipping greylisting for $acl_m_sender due to match in GREYLIST_RESENDERS_TABLE - - # run greylisting acl - defer - !senders = : postmaster@* - !hosts = +skip_greylisting_hosts - acl = greylist_acl - message = greylisted - try again later - - # Otherwise, the recipient address is OK. - # - accept - -# This access control list is used for message data received via -# SMTP. The tests are run in order until the recipient address -# is either accepted or denied. -# -acl_data: - - # Add Message-ID if missing in messages received from our own hosts. - warn - condition = ${if !def:h_Message-ID: {1}} - hosts = : +relay_from_hosts - message = Message-ID: - - # add domain keys status header - # - #warn - # message = DomainKey-Status: $dk_status - # !condition = ${if eq{$dk_status}{}{1}{0}} - - # Accept mail received over local SMTP (i.e. not over TCP/IP). - # We do this by testing for an empty sending host field. - # Also accept mails received from hosts for which we relay mail. - # - accept - hosts = : +relay_from_hosts - - # Accept if the message arrived over an authenticated connection, from - # any host. - # - accept - authenticated = * - - # Enforce a message-size limit - # - #deny - # message = Message size $message_size is larger than limit of \ - # MESSAGE_SIZE_LIMIT - # condition = ${if >{$message_size}{MESSAGE_SIZE_LIMIT}{true}{false}} - - # Check if the address list header is syntactically correct. - # Note that some specialized MTAs, such as certain mailing list - # servers, do not automatically generate a Message-ID for bounces. - # Thus, we add the check for a non-empty sender. - # (email feedback reports from aol fail this check) - accept - message = X-RFC2822-Error: Your message does not conform to RFC2822 standard - log_message = message header failed RFC2822 syntax check - !hosts = +relay_from_hosts - !senders = : postmaster@* - !verify = header_syntax - add_header = :at_start_rfc:X-RFC2822-Error: Your message does not conform to RFC2822 standard - set acl_m_junk = yes - - # Warn unless there is a verifiable sender address in at least - # one of the "Sender:", "Reply-To:", or "From:" header lines. - warn - !verify = header_sender - log_message = No valid sender in message header - add_header = :at_start_rfc:X-Sender-Verify-Failed: No valid sender in message header - - # Deny if the message contains a virus. Before enabling this check, you - # must install a virus scanner and set the av_scanner option above. - # - #accept - # malware = */defer_ok - # log_message = This message contains a virus ($malware_name). - # add_header = :at_start_rfc:X-Virus-Warning: This message contains a virus ($malware_name). - # set acl_m_junk = yes - - # Accept the message. - # - accept - - -###################################################################### -# ROUTERS CONFIGURATION # -# Specifies how addresses are handled # -###################################################################### -# THE ORDER IN WHICH THE ROUTERS ARE DEFINED IS IMPORTANT! # -# An address is passed to each router in turn until it is accepted. # -###################################################################### - -begin routers - -autowhitelist_filter: - driver = redirect - domains = ! +local_domains - ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 - condition = ${lookup mysql{SELECT vm_mboxes.id FROM vm_mboxes WHERE vm_mboxes.mbox="${quote_mysql:$sender_address_local_part}" AND vm_mboxes.domain="${quote_mysql:$sender_address_domain}" }} - check_local_user = false - user = Debian-exim - file = /etc/exim4/autowhitelist.filter - no_verify - unseen - allow_filter = true - -srs_bounce: - senders = : - driver = redirect - domains = +local_domains - allow_fail - allow_defer - local_part_prefix = srs0+ : srs0- : srs0= : srs1+ : srs1- : srs1= - caseful_local_part - address_data = ${run{/usr/bin/srs --secretfile=/etc/exim4/srsd.secret --hashlength=24 --reverse --address=$local_part_prefix$local_part@$domain}{$value}{:defer: SRS failure}} - ## srsd is broken on ubuntu 22.04. using above "run" command instead -# address_data = ${readsocket{/run/srsd/srsd.sock}{REVERSE $local_part_prefix$local_part@$domain}{5s}{ }{:defer: SRS daemon failure}} - data = ${sg {$address_data} {^SRS: Case insensitive hash match detected. Someone smashed case in the local-part\. .* ([^ ]+)@([^ ]+)\$} {\N$1@$2\N} } - headers_add = X-SRS: Decoded valid SRS return address to ${quote_local_part:${local_part:$address_data}}@${domain:$address_data} by $primary_hostname - -srs_forward: - driver = redirect - senders = ! : ! *@+local_domains - domains = ! +local_domains : ! +relay_to_domains - condition = ${lookup mysql{SELECT vm_domains.id FROM vm_domains WHERE vm_domains.domain='${quote_mysql:$original_domain}' AND vm_domains.status = '1'}} - address_data = ${run{/usr/bin/srs --secretfile=/etc/exim4/srsd.secret --hashlength=24 --forward --address=$sender_address_local_part@$sender_address_domain --alias=$original_domain}{$value}{:defer: SRS failure}} - ## srsd is broken on ubuntu 22.04. using above "run" command instead -# address_data = ${readsocket{/run/srsd/srsd.sock}\ -# {FORWARD $sender_address_local_part@$sender_address_domain $original_domain\n}\ -# {5s}{\n}{:defer: SRS daemon failure}} - errors_to = ${quote_local_part:${local_part:$address_data}}@${domain:$address_data} - data = ${quote_local_part:$local_part}@$domain - headers_add = X-SRS-Forward: from $sender_address to $original_local_part@$original_domain forwarded to $local_part@$domain by $primary_hostname - repeat_use = false - allow_defer - no_verify - -# This router routes addresses that are not in local domains by doing a DNS -# lookup on the domain name. Any domain that resolves to 0.0.0.0 or to a -# loopback interface address (127.0.0.0/8) is treated as if it had no DNS -# entry. Note that 0.0.0.0 is the same as 0.0.0.0/32, which is commonly treated -# as the local host inside the network stack. It is not 0.0.0.0/0, the default -# route. If the DNS lookup fails, no further routers are tried because of -# the no_more setting, and consequently the address is unrouteable. - -dnslookup: - driver = dnslookup - self = pass - transport = remote_smtp - ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 - no_more - -# The remaining routers handle addresses in the local domain(s). - -junk_filter: - driver = accept - domains = +local_domains - condition = ${if eq{$acl_m_junk}{yes}} - condition = ${if !eq{$acl_m_whitelist}{yes}} - condition = ${if !eq {$received_protocol}{spam-scanned}} - local_part_suffix = +* - local_part_suffix_optional = true - local_parts = ${lookup mysql{SELECT vm_mboxes.mbox FROM vm_mboxes WHERE vm_mboxes.mbox='${quote_mysql:$local_part}' AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_mboxes.filter > '0'}} - headers_add = X-Junk-Flag: YES - transport = junk_delivery - -spam_filter: - driver = accept - domains = +local_domains - condition = ${if eq {$received_protocol}{spam-scanned}} - condition = ${if eq {$header_X-Spam-Flag:}{YES}} -## condition = ${if !eq {$header_X-Whitelist-Flag:}{YES}} - local_part_suffix = +* - local_part_suffix_optional = true - local_parts = ${lookup mysql{SELECT vm_mboxes.mbox FROM vm_mboxes WHERE vm_mboxes.mbox='${quote_mysql:$local_part}' AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_mboxes.filter = '2'}} - transport = junk_delivery - -mailman3_router: - driver = accept - domains = +local_domains - condition = ${if !eq {$received_protocol}{spam-scanned}} - require_files = /var/lib/mailman3/lists/${local_part}.${domain} - local_part_suffix_optional - local_part_suffix = \ - -bounces : -bounces+* : \ - -confirm : -confirm+* : \ - -join : -leave : \ - -owner : -request : \ - -subscribe : -unsubscribe - transport = mailman3_transport - -virtual_alias: - driver = redirect - domains = +local_domains - local_part_suffix = +* - local_part_suffix_optional = true - condition = ${if !eq {$received_protocol}{spam-scanned}} - data = ${lookup mysql{SELECT CONCAT(vm_aliases.mbox,'@','${quote_mysql:$domain}') FROM vm_mboxes, vm_aliases WHERE vm_mboxes.mbox=vm_aliases.mbox AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_aliases.alias='${quote_mysql:$local_part}' AND vm_aliases.domain='${quote_mysql:$domain}'}} - -virtual_vacation: - driver = accept - domains = +local_domains - # currently configured to *not* autorespond to + aliases - #local_part_suffix = +* - #local_part_suffix_optional = true - local_parts = ${lookup mysql{SELECT vm_mboxes.mbox FROM vm_mboxes, vm_autoresponders WHERE vm_mboxes.mbox='${quote_mysql:$local_part}' AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_autoresponders.mbox='${quote_mysql:$local_part}' AND vm_autoresponders.domain='${quote_mysql:$domain}' AND vm_autoresponders.mode='Vacation' AND vm_autoresponders.status='1'}} - # add options for start & end date fields to above query - # do not reply to errors or lists or spam-scanned messages, require vacation message in db - condition = ${if !match {$h_precedence:} {(?i)junk|bulk|list}} - condition = ${if !eq{$acl_m_junk}{yes}} - condition = ${if !eq {$received_protocol}{spam-scanned}} - condition = ${if !eq {$sender_address} {}} - no_expn - # do not reply to errors and bounces or lists - senders = " ! ^.*-request@.*:\ - ! ^owner-.*@.*:\ - ! ^postmaster@.*:\ - ! ^listmaster@.*:\ - ! ^mailer-daemon@.*\ - ! ^root@.*" - transport = vacation_transport - unseen - no_verify - -virtual_autoresponder: - driver = accept - domains = +local_domains - # currently configured to *not* autorespond to + aliases - #local_part_suffix = +* - #local_part_suffix_optional = true - local_parts = ${lookup mysql{SELECT vm_mboxes.mbox FROM vm_mboxes, vm_autoresponders WHERE vm_mboxes.mbox='${quote_mysql:$local_part}' AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_autoresponders.mbox='${quote_mysql:$local_part}' AND vm_autoresponders.domain='${quote_mysql:$domain}' AND vm_autoresponders.mode='Autoresponder' AND vm_autoresponders.status='1'}} - # add options for start & end date fields to above query - # do not reply to errors or lists or spam-scanned messages, require autoresponder message in db - condition = ${if !match {$h_precedence:} {(?i)junk|bulk|list}} - condition = ${if !eq{$acl_m_junk}{yes}} - condition = ${if !eq {$received_protocol}{spam-scanned}} - condition = ${if !eq {$sender_address} {}} - no_expn - # do not reply to errors and bounces or lists - senders = " ! ^.*-request@.*:\ - ! ^owner-.*@.*:\ - ! ^postmaster@.*:\ - ! ^listmaster@.*:\ - ! ^mailer-daemon@.*\ - ! ^root@.*" - transport = autoresponder_transport - unseen - no_verify - -virtual_forward_and_drop: - driver = redirect - domains = +local_domains - condition = ${if !eq {$received_protocol}{spam-scanned}} - local_part_suffix = +* - local_part_suffix_optional = true - data = ${lookup mysql{SELECT vm_forwards.forward_to FROM vm_mboxes, vm_forwards WHERE vm_mboxes.mbox='${quote_mysql:$local_part}' AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_forwards.mbox='${quote_mysql:$local_part}' AND vm_forwards.domain='${quote_mysql:$domain}' AND vm_forwards.save_local='0'}} - -virtual_forward_and_keep: - driver = redirect - domains = +local_domains - condition = ${if !eq {$received_protocol}{spam-scanned}} - local_part_suffix = +* - local_part_suffix_optional = true - data = ${lookup mysql{SELECT CONCAT('${quote_mysql:$local_part}@${quote_mysql:$domain}\n', vm_forwards.forward_to) FROM vm_mboxes, vm_forwards WHERE vm_mboxes.mbox='${quote_mysql:$local_part}' AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_forwards.mbox='${quote_mysql:$local_part}' AND vm_forwards.domain='${quote_mysql:$domain}' AND vm_forwards.save_local='1'}} - -spamcheck_router: - driver = accept - # uncomment next line to bypass spamcheck when testing address routing with "exim -bt user@example.com" - #address_test = false - domains = +local_domains - condition = ${if !eq {$received_protocol}{spam-scanned}} - condition = ${if !eq {$sender_address_domain}{$domain}} - condition = ${if !eq{$acl_m_whitelist}{yes}} - condition = ${if < {$message_size}{512k}} - local_part_suffix = +* - local_part_suffix_optional = true - local_parts = ${lookup mysql{SELECT vm_mboxes.mbox FROM vm_mboxes WHERE vm_mboxes.mbox='${quote_mysql:$local_part}' AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_mboxes.filter = '2'}} - headers_remove = X-Spam-Checker-Version:X-Spam-Flag:X-Spam-Level:X-Spam-Status:X-Spam-Score:X-Spam-Report - transport = spamcheck - -# add mailman3 spamcheck? - -user_filter: - driver = redirect - domains = +local_domains - local_part_suffix = +* - local_part_suffix_optional = true - data = ${lookup mysql{SELECT vm_filters.filter FROM vm_mboxes, vm_filters WHERE vm_mboxes.mbox='${quote_mysql:$local_part}' AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_filters.mbox='${quote_mysql:$local_part}' AND vm_filters.domain='${quote_mysql:$domain}'}} - user = vmail - no_verify - no_expn - check_ancestor - allow_filter - file_transport = address_file - pipe_transport = address_pipe - reply_transport = address_reply - directory_transport = user_filter_maildir_delivery - allow_fail - -lmtp_localuser: - driver = accept - domains = +local_domains - local_part_suffix = +* - local_part_suffix_optional = true - condition = ${lookup mysql{SELECT vm_mboxes.id FROM vm_mboxes WHERE vm_mboxes.mbox='${quote_mysql:$local_part}' AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0'}} - transport = dovecot_lmtp - cannot_route_message = Unknown user - -# Support for catchall aliases. It is *not* recommended to use this. -virtual_alias_catchall: - driver = redirect - domains = +local_domains -## condition = ${if !eq {$received_protocol}{spam-scanned}} - data = ${lookup mysql{SELECT CONCAT(vm_aliases.mbox,'@','${quote_mysql:$domain}') FROM vm_mboxes, vm_aliases WHERE vm_mboxes.mbox=vm_aliases.mbox AND vm_mboxes.domain='${quote_mysql:$domain}' AND vm_mboxes.status > '0' AND vm_aliases.alias='catchall' AND vm_aliases.domain='${quote_mysql:$domain}'}} - -# This router handles aliasing using a linearly searched alias file with the -# name SYSTEM_ALIASES_FILE. When this configuration is installed automatically, -# the name gets inserted into this file from whatever is set in Exim's -# build-time configuration. The default path is the traditional /etc/aliases. -# If you install this configuration by hand, you need to specify the correct -# path in the "data" setting below. -# -##### NB You must ensure that the alias file exists. It used to be the case -##### NB that every Unix had that file, because it was the Sendmail default. -##### NB These days, there are systems that don't have it. Your aliases -##### NB file should at least contain an alias for "postmaster". -# -# If any of your aliases expand to pipes or files, you will need to set -# up a user and a group for these deliveries to run under. You can do -# this by uncommenting the "user" option below (changing the user name -# as appropriate) and adding a "group" option if necessary. Alternatively, you -# can specify "user" on the transports that are used. Note that the transports -# listed below are the same as are used for .forward files; you might want -# to set up different ones for pipe and file deliveries from aliases. - -# System Aliases, and User Forwards below, are only enabled for primary_hostname & qualify_domain. -# primary_hostname is the server hostname (FQDN) -# qualify_domain can be set in exim_local.conf and defaults to the server domain name, -# which is the primary_hostname without the local part - what "hostname -d" returns. -# These routers do not get invoked for any other virtual email domains configured on the server. - -system_aliases: - driver = redirect - domains = $primary_hostname:$qualify_domain:$qualify_recipient - allow_fail - allow_defer - data = ${lookup{$local_part}lsearch{/etc/aliases}} -# user = exim -# file_transport = address_file -# pipe_transport = address_pipe - - -# This router handles forwarding using traditional .forward files in users' -# home directories. If you want it also to allow mail filtering when a forward -# file starts with the string "# Exim filter", uncomment the "allow_filter" -# option. - -# The no_verify setting means that this router is skipped when Exim is -# verifying addresses. Similarly, no_expn means that this router is skipped if -# Exim is processing an EXPN command. - -# The check_ancestor option means that if the forward file generates an -# address that is an ancestor of the current one, the current one gets -# passed on instead. This covers the case where A is aliased to B and B -# has a .forward file pointing to A. - -# The three transports specified at the end are those that are used when -# forwarding generates a direct delivery to a file, or to a pipe, or sets -# up an auto-reply, respectively. - -userforward: - driver = redirect - check_local_user - domains = $primary_hostname:$qualify_domain:$qualify_recipient - file = $home/.forward - no_verify - no_expn - check_ancestor - allow_filter - file_transport = address_file - pipe_transport = address_pipe - reply_transport = address_reply - - -# This router matches local user mailboxes. - -#localuser: -# driver = accept -# check_local_user -# transport = local_delivery - - - -###################################################################### -# TRANSPORTS CONFIGURATION # -###################################################################### -# ORDER DOES NOT MATTER # -# Only one appropriate transport is called for each delivery. # -###################################################################### - -# A transport is used only when referenced from a router that successfully -# handles an address. - -begin transports - -# This transport is used for delivering messages over SMTP connections. - -remote_smtp: - driver = smtp - # run{/bin/echo part is required to de-taint the domain - dkim_domain = ${run{/bin/echo ${lc:${domain:$h_from:}}}{$value}} - dkim_canon = relaxed - dkim_selector = ${if exists{/etc/ssl/dkim/${dkim_domain}.selector}{${readfile{/etc/ssl/dkim/${dkim_domain}.selector}{}}}{0}} - dkim_private_key = ${if exists{/etc/ssl/dkim/${dkim_domain}.pem}{/etc/ssl/dkim/${dkim_domain}.pem}{0}} - -# This transport is used for local delivery to user mailboxes in traditional -# BSD mailbox format. By default it will be run under the uid and gid of the -# local user, and requires the sticky bit to be set on the /var/mail directory. -# Some systems use the alternative approach of running mail deliveries under a -# particular group instead of using the sticky bit. The commented options below -# show how this can be done. - -#local_delivery: -# driver = appendfile -## file = /var/vmail/$local_part_data -# maildir_format = true -# directory = /home/$local_part_data/Maildir -# create_directory = true -# directory_mode = 770 -# delivery_date_add -# envelope_to_add -# return_path_add -# user = $local_part -# group = $local_part -# mode = 0660 - -dovecot_lmtp: - driver = lmtp - socket = /run/dovecot/lmtp - #return_path_add - #maximum number of deliveries per batch, default 1 - #batch_max = 200 - #allow suffixes/prefixes (default unset) - #rcpt_include_affixes - -## for vacation mail -vacation_transport: - driver = autoreply - log = /var/vmail/${domain_data}/${local_part_data}/vacation_log - once = /var/vmail/${domain_data}/${local_part_data}/vacation_once_db - return_path = ${local_part}@${domain} - to = ${sender_address} - from = ${local_part}@${domain} - subject = ${lookup mysql{SELECT vm_autoresponders.subject FROM vm_autoresponders WHERE vm_autoresponders.mbox='${quote_mysql:$local_part}' AND vm_autoresponders.domain='${quote_mysql:$domain}' AND vm_autoresponders.status='1' AND vm_autoresponders.mode='Vacation'}{$value}{"Auto Reply"}} - text = ${lookup mysql{SELECT vm_autoresponders.body FROM vm_autoresponders WHERE vm_autoresponders.mbox='${quote_mysql:$local_part}' AND vm_autoresponders.domain='${quote_mysql:$domain}' AND vm_autoresponders.status='1' AND vm_autoresponders.mode='Vacation'}{$value}fail} - user = vmail - -## for autoresponder -autoresponder_transport: - driver = autoreply - log = /var/vmail/${domain_data}/${local_part_data}/autoresponder_log - return_path = ${local_part}@${domain} - to = ${sender_address} - from = ${local_part}@${domain} - subject = ${lookup mysql{SELECT vm_autoresponders.subject FROM vm_autoresponders WHERE vm_autoresponders.mbox='${quote_mysql:$local_part}' AND vm_autoresponders.domain='${quote_mysql:$domain}' AND vm_autoresponders.status='1' AND vm_autoresponders.mode='Autoresponder'}{$value}{"Auto Reply"}} - text = ${lookup mysql{SELECT vm_autoresponders.body FROM vm_autoresponders WHERE vm_autoresponders.mbox='${quote_mysql:$local_part}' AND vm_autoresponders.domain='${quote_mysql:$domain}' AND vm_autoresponders.status='1' AND vm_autoresponders.mode='Autoresponder'}{$value}fail} - user = vmail - -#maildir_delivery: -# driver = appendfile -# maildir_format = true -# directory = /var/vmail/${domain_data}/${local_part_data}/Maildir -# create_directory = true -# directory_mode = 750 -# user = vmail - -user_filter_maildir_delivery: - driver = appendfile - maildir_format = true - user = vmail - -junk_delivery: - driver = appendfile - maildir_format = true - directory = /var/vmail/${domain_data}/${local_part_data}/Maildir/.Junk - create_directory = true - directory_mode = 750 - user = vmail - -# SpamAssassin -spamcheck: - driver = pipe - command = /usr/sbin/exim -oMr spam-scanned -bS - use_bsmtp = true - transport_filter = /usr/bin/spamc -f -u $local_part_data@$domain_data - home_directory = "/tmp" - current_directory = "/tmp" - # must use a privileged user to set $received_protocol on the way back in! - user = mail - group = mail - log_output = true - return_fail_output = true - return_path_add = false - message_prefix = - message_suffix = - -mailman3_transport: - driver = smtp - protocol = lmtp - allow_localhost - hosts = localhost - #hosts_override - port = 8024 - rcpt_include_affixes = true - -#mailman_transport: -# driver = pipe -# command = MAILMAN_WRAP \ -# '${if def:local_part_suffix \ -# {${sg{$local_part_suffix}{-(\\w+)(\\+.*)?}{\$1}}} \ -# {post}}' \ -# $local_part -# current_directory = MAILMAN_HOME -# home_directory = MAILMAN_HOME -# user = MAILMAN_UID -# group = MAILMAN_GID - - -# clamav -#clamav_scan -# driver = pipe -# command = /usr/bin/clamdscan -# user = clamav -# user_bsmtp = true - -# This transport is used for handling pipe deliveries generated by alias or -# .forward files. If the pipe generates any standard output, it is returned -# to the sender of the message as a delivery error. Set return_fail_output -# instead of return_output if you want this to happen only when the pipe fails -# to complete normally. You can set different transports for aliases and -# forwards if you want to - see the references to address_pipe in the routers -# section above. - -address_pipe: - driver = pipe - return_output - - -# This transport is used for handling deliveries directly to files that are -# generated by aliasing or forwarding. - -address_file: - driver = appendfile - delivery_date_add - envelope_to_add - return_path_add - - -# This transport is used for handling autoreplies generated by the filtering -# option of the userforward router. - -address_reply: - driver = autoreply - - - -###################################################################### -# RETRY CONFIGURATION # -###################################################################### - -begin retry - -# This single retry rule applies to all domains and all errors. It specifies -# retries every 15 minutes for 2 hours, then every 2 hours until 1 full day -# has passed since the first delivery failed. - -# Domain Error Retries -# ------ ----- ------- - -* * F,2h,15m; F,1d,2h - - -###################################################################### -# REWRITE CONFIGURATION # -###################################################################### - -# There are no rewriting specifications in this default configuration file. - -begin rewrite - - -###################################################################### -# AUTHENTICATION CONFIGURATION # -###################################################################### - -# There are no authenticator specifications in this default configuration file. - -begin authenticators - -dovecot_plain: - driver = dovecot - public_name = PLAIN - server_socket = /run/dovecot/auth-client - server_set_id = $auth1 - -dovecot_login: - driver = dovecot - public_name = LOGIN - server_socket = /run/dovecot/auth-client - server_set_id = $auth1 - -# End of Exim configuration file diff --git a/etc/exim4/exim4-bookworm.conf b/etc/exim4/exim4.conf similarity index 100% rename from etc/exim4/exim4-bookworm.conf rename to etc/exim4/exim4.conf diff --git a/install-mm3.sh b/install-mm3.sh new file mode 100755 index 0000000..ec144d1 --- /dev/null +++ b/install-mm3.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +if [ "${EUID}" -ne 0 ]; then + echo "You must be root to run this installer." + exit +fi + +# check Debian 12 (bookworm) +os_codename=`lsb_release -cs` +if [[ $os_codename != bookworm ]]; then + echo "This installer only runs on Debian 12 (Bookworm), bailing out." + exit 1 +fi + +# Check for vmail-stack & vhost-stack installs +if [ ! -f "/usr/local/bin/vhost.sh" ] || [ ! -f "/usr/local/bin/vmail.sh" ]; then + echo "You must install vhost-stack & vmail-stack first." + exit 1 +fi + +FQDN=`hostname -f` + +# install mailman3-full +apt -y install python3-pymysql python3-mysqldb +echo "mailman3 mailman3/database-type select mysql" | debconf-set-selections +echo "mailman3-web mailman3-web/database-type select mysql" | debconf-set-selections +echo "mailman3-web mailman3-web/superuser-mail string webmaster@$FQDN" | debconf-set-selections +DEBIAN_FRONTEND=noninteractive apt-get -y install mailman3-full +systemctl stop mailman3.service +systemctl stop mailman3-web.service + +# enable apache proxy_uwsgi_module for mailman3 +a2enmod proxy_uwsgi + +# Enable Mailman3 admin on default server hostname +sed -i "s|#Include /etc/mailman3/apache.conf|Include /etc/mailman3/apache.conf|g" /etc/apache2/sites-available/001-default-ssl.conf + +# configure mailman-hyperkitty.cfg +sed -i "s|base_url: http://localhost/mailman3/hyperkitty/|base_url: https://$FQDN/mailman3/hyperkitty/|g" /etc/mailman3/mailman-hyperkitty.cfg +MAILMAN_ARCHIVER_KEY=`grep MAILMAN_ARCHIVER_KEY /etc/mailman3/mailman-web.py | cut -d ' ' -f 3 | xargs` +sed -i "s|SecretArchiverAPIKey|$MAILMAN_ARCHIVER_KEY|g" /etc/mailman3/mailman-hyperkitty.cfg + +# configure mailman-web.py +TIMEZONE=`cat /etc/timezone` +IP=`ip route get 1.1.1.1| head -n 1 | cut -d ' ' -f 7` +sed -i "s|root@localhost|webmaster@$FQDN|g" /etc/mailman3/mailman-web.py +sed -i "s|MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1')|MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1', '$IP')|g" /etc/mailman3/mailman-web.py +sed -i "s|'django_mailman3.lib.auth.fedora',|#'django_mailman3.lib.auth.fedora',|g" /etc/mailman3/mailman-web.py +sed -i 's|# SECURE_PROXY_SSL_HEADER =|SECURE_PROXY_SSL_HEADER =|g' /etc/mailman3/mailman-web.py +sed -i "s|TIME_ZONE =.*|TIME_ZONE = '$TIMEZONE'|g" /etc/mailman3/mailman-web.py +sed -i "s|localhost.local|$FQDN|g" /etc/mailman3/mailman-web.py +sed -i 's|postorius@{}|webmaster@{}|g' /etc/mailman3/mailman-web.py +sed -i 's|root@{}|webmaster@{}|g' /etc/mailman3/mailman-web.py +echo "" >> /etc/mailman3/mailman-web.py +echo "# fix retry must be larger than timeout error" >> /etc/mailman3/mailman-web.py +echo "Q_CLUSTER = {" >> /etc/mailman3/mailman-web.py +echo " 'timeout': 300," >> /etc/mailman3/mailman-web.py +echo " 'retry': 360," >> /etc/mailman3/mailman-web.py +echo " 'save_limit': 100," >> /etc/mailman3/mailman-web.py +echo " 'orm': 'default'," >> /etc/mailman3/mailman-web.py +echo " 'poll': 5," >> /etc/mailman3/mailman-web.py +echo "}" >> /etc/mailman3/mailman-web.py +echo "" >> /etc/mailman3/mailman-web.py +echo "# Default primary key field type to use, required to avoid mysql errors." +echo "DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'" >> /etc/mailman3/mailman-web.py +echo "" >> /etc/mailman3/mailman-web.py +echo "# Make DISPLAY NAME header based on virtualhost" >> /etc/mailman3/mailman-web.py +echo "SITE_ID = 0" >> /etc/mailman3/mailman-web.py +echo "" >> /etc/mailman3/mailman-web.py +echo "# Only show lists that match domain. Currently seems to only work for HyperKitty (archives)" >> /etc/mailman3/mailman-web.py +echo "FILTER_VHOST = True" >> /etc/mailman3/mailman-web.py + +# configure mailman.cfg +sed -i "s|changeme@example.com|webmaster@$FQDN|g" /etc/mailman3/mailman.cfg +sed -i 's|noreply_address: noreply|noreply_address: webmaster|g' /etc/mailman3/mailman.cfg +sed -i 's|#incoming: mailman.mta.exim4.LMTP|incoming: mailman.mta.exim4.LMTP|g' /etc/mailman3/mailman.cfg +sed -i 's|incoming: mailman.mta.postfix.LMTP|#incoming: mailman.mta.postfix.LMTP|g' /etc/mailman3/mailman.cfg +sed -i 's|#configuration: python:mailman.config.exim4|configuration: python:mailman.config.exim4|g' /etc/mailman3/mailman.cfg +sed -i 's|configuration: python:mailman.config.postfix|#configuration: python:mailman.config.postfix|g' /etc/mailman3/mailman.cfg +echo "" >> /etc/mailman3/mailman.cfg +echo "# strip dkim, outgoing will be re-signed" >> /etc/mailman3/mailman.cfg +echo "remove_dkim_headers: yes" >> /etc/mailman3/mailman.cfg +echo "" >> /etc/mailman3/mailman.cfg +echo "[archiver.hyperkitty]" >> /etc/mailman3/mailman.cfg +echo "class: mailman_hyperkitty.Archiver" >> /etc/mailman3/mailman.cfg +echo "enable: yes" >> /etc/mailman3/mailman.cfg +echo "configuration: /etc/mailman3/mailman-hyperkitty.cfg" >> /etc/mailman3/mailman.cfg + +# restart mailman3 services +systemctl start mailman3.service +systemctl start mailman3-web.service +systemctl reload apache2.service + +echo "" +echo "To finish setting up Mailman3:" +echo "letsencrypt-certonly.sh -d $FQDN" +echo "a2ensite 001-default-ssl.conf" +echo "systemctl reload apache2" +echo "mailman-web createsuperuser" +echo "https://$FQDN/mailman3/admin" +echo " Sites -> change example.com to $FQDN" +echo "sed -i 's|SITE_ID = 1|SITE_ID = 0|g' /etc/mailman3/mailman-web.py" +echo "systemctl restart mailman3-web.service" diff --git a/install.sh b/install.sh index 3ff58ae..391142b 100755 --- a/install.sh +++ b/install.sh @@ -5,10 +5,11 @@ if [ "${EUID}" -ne 0 ]; then exit fi -# check for Ubuntu 22.04 (jammy) or Debian 12 (bookworm) -os_codename=`lsb_release -cs` -if [ $os_codename != jammy ] && [ $os_codename != bookworm ]; then - echo "This installer only runs on Ubuntu 22.04 (jammy) or Debian 12 (Bookworm), bailing out." +# check for Debian 12 (bookworm) +os_id=`lsb_release -is` +os_release=`lsb_release -rs` +if [ $os_id != Debian ] || [ $os_release != 12 ]; then + echo "This installer only runs on Debian 12 (Bookworm), bailing out." exit 1 fi @@ -91,38 +92,19 @@ sed -i "s|userpref|sa_userpref|g" /usr/share/doc/spamassassin/sql/userpref_mysql sed -i "s|username varchar(100)|username varchar(255)|g" /usr/share/doc/spamassassin/sql/userpref_mysql.sql sed -i "s|TYPE=MyISAM||g" /usr/share/doc/spamassassin/sql/userpref_mysql.sql mysql vmail < /usr/share/doc/spamassassin/sql/userpref_mysql.sql +sed -i 's|OPTIONS="--create-prefs --max-children 5 --helper-home-dir"|OPTIONS="-x -q -v -u Debian-exim -m 5"|g' /etc/default/spamd cp etc/spamassassin/*.cf /etc/spamassassin/ sed -i "s|user_scores_sql_password password|user_scores_sql_password $VMAILPASS|g" /etc/spamassassin/sql.cf chown root:root /etc/spamassassin/local.cf chmod 644 /etc/spamassassin/local.cf +chown Debian-exim:mail /etc/spamassassin/sql.cf chmod 640 /etc/spamassassin/sql.cf -chown debian-spamd:mail /etc/spamassassin/sql.cf -if [ $os_codename = jammy ]; then - sed -i 's|OPTIONS="--create-prefs --max-children 5 --helper-home-dir"|OPTIONS="-x -q -v -u debian-spamd -g mail -m 5"|g' /etc/default/spamassassin - sed -i 's|CRON=0|CRON=1|g' /etc/default/spamassassin -elif [ $os_codename = bookworm ]; then - sed -i 's|OPTIONS="--create-prefs --max-children 5 --helper-home-dir"|OPTIONS="-x -q -v -u debian-spamd -g mail -m 5"|g' /etc/default/spamd - systemctl enable spamassassin-maintenance.timer - systemctl start spamassassin-maintenance.timer -else - echo "WARNING: Unexpected OS codename. This should never happen due to previous checks." -fi # create local systemd dir, used by srsd & vmail-cron if [[ ! -d /usr/local/lib/systemd/system ]]; then mkdir -p /usr/local/lib/systemd/system fi -# srsd -# bug fixes for libmail-srs-perl. still needed as of v0.31-6 on Ubuntu 22.04 -sed -i 's|/tmp/srsd|/run/srsd/srsd.sock|' /usr/share/perl5/Mail/SRS/Daemon.pm -sed -i '/Until we decide that forward/,+3d' /usr/share/perl5/Mail/SRS/Daemon.pm -cp systemd/srsd.service /usr/local/lib/systemd/system/srsd.service -chmod 644 /usr/local/lib/systemd/system/srsd.service -systemctl daemon-reload -systemctl enable srsd -systemctl start srsd - # exim config fqdn=`hostname -f` maildomain=`hostname -d` @@ -130,13 +112,6 @@ sed -i 's/nocreate/create 640 Debian-exim adm/g' /etc/logrotate.d/exim4-base sed -i 's/size 10M/daily/g' /etc/logrotate.d/exim4-paniclog install --owner=Debian-exim --group=Debian-exim --mode=640 /dev/null /etc/exim4/relay_domains cp etc/exim4/* /etc/exim4/ -if [ $os_codename = jammy ]; then - cp /etc/exim4/exim4-jammy.conf /etc/exim4/exim4.conf -elif [ $os_codename = bookworm ]; then - cp /etc/exim4/exim4-bookworm.conf /etc/exim4/exim4.conf -else - echo "WARNING: Unexpected OS codename. This should never happen due to previous checks." -fi chmod 640 /etc/exim4/exim4.conf chown Debian-exim:Debian-exim /etc/exim4/autowhitelist.filter chmod 640 /etc/exim4/autowhitelist.filter @@ -154,9 +129,17 @@ chown Debian-exim:Debian-exim /etc/exim4/srsd.secret pwgen -N 1 64 > /etc/exim4/srsd.secret sed -i "s|^QUEUERUNNER.*|QUEUERUNNER='separate'|g" /etc/default/exim4 sed -i "s|^QUEUEINTERVAL.*|QUEUEINTERVAL='15m'|g" /etc/default/exim4 -if [[ ! -f /var/log/exim4/rejectlog ]]; then - install --owner=Debian-exim --group=adm --mode=640 /dev/null /var/log/exim4/rejectlog -fi +install --owner=Debian-exim --group=adm --mode=640 /dev/null /var/log/exim4/rejectlog + +# srsd +# bug fixes for libmail-srs-perl. still needed as of v0.31-9 on Debian 12 +sed -i 's|/tmp/srsd|/run/srsd/srsd.sock|' /usr/share/perl5/Mail/SRS/Daemon.pm +sed -i '/Until we decide that forward/,+3d' /usr/share/perl5/Mail/SRS/Daemon.pm +cp systemd/srsd.service /usr/local/lib/systemd/system/srsd.service +chmod 644 /usr/local/lib/systemd/system/srsd.service +systemctl daemon-reload +systemctl enable srsd +systemctl start srsd # dovecot config mkdir /etc/dovecot/sites.d @@ -172,15 +155,8 @@ chmod 750 /usr/local/libexec/vmail-quota-warning.sh chown dovecot:mail /usr/local/libexec/vmail-quota-warning.sh # restart services -if [ $os_codename = jammy ]; then - systemctl enable spamassassin - systemctl restart spamassassin -elif [ $os_codename = bookworm ]; then - systemctl enable spamd - systemctl restart spamd -else - echo "WARNING: Unexpected OS codename. This should never happen due to previous checks." -fi +systemctl enable spamd +systemctl restart spamd systemctl restart exim4 systemctl restart dovecot @@ -220,12 +196,6 @@ fi if [[ -d /etc/fail2ban/ ]]; then chmod 644 etc/fail2ban/*/*.conf cp -a etc/fail2ban/* /etc/fail2ban/ - if [ $os_codename = bookworm ]; then - echo "backend = systemd" >> /etc/fail2ban/jail.d/dovecot.conf - elif [ $os_codename = jammy ]; then - echo "backend = auto" >> /etc/fail2ban/jail.d/dovecot.conf - echo "logpath = /var/log/mail.log tail" >> /etc/fail2ban/jail.d/dovecot.conf - fi else echo echo "fail2ban not installed, skipping fail2ban email configs." @@ -237,3 +207,6 @@ echo echo "System emails are all configured to alias to root@$fqdn," echo "which in turn forwards to webmaster@$maildomain." echo "Adjust /etc/aliases & /root/.forward as desired." +echo +echo "To install the Mailman3 mailing list software run:" +echo "install-mm3.sh"