X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;f=mail-setup;h=46933160539e29a67fbc258a3c5ae0c208268d82;hb=17b4b5f6afb18accc328282d8ce7dcc5513e621d;hp=47f5990258ca184d2c5388f6411ce25507f26701;hpb=f234193be4019bb40b50d2973632ce2469f4c0af;p=distro-setup diff --git a/mail-setup b/mail-setup index 47f5990..4693316 100755 --- a/mail-setup +++ b/mail-setup @@ -3,6 +3,11 @@ # Copyright (C) 2019 Ian Kelling # SPDX-License-Identifier: AGPL-3.0-or-later +# todo: harden dovecot. need to do some research. one way is for it to only listen on a wireguard vpn interface, so only clients that are on the vpn can access it. +# todo: consider hardening cups listening on 0.0.0.0 +# todo: stop/disable local apache, and rpc.mountd, and kdeconnect when not in use. +# todo: check that spamd and unbound only listen locally. + # todo: hosts should only allow external mail that is authed and # destined for backup route. it is a minor issue since traffic is # limited to the wghole network. @@ -10,8 +15,6 @@ # todo: emailing info@amnimal.ninja produces a bounce, user doesn't exist # instead of a simple rejection like it should. -# todo: auto restart of je on checkrestart - # todo: run mailping test after running, or otherwise # clear out terminal alert @@ -133,7 +136,7 @@ fi [[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@" - +# note, this is hardcoded in /etc/exim4/conf.d/main/000_local u=$(id -nu 1000) @@ -276,7 +279,10 @@ fi i() { # install file local tmp tmpdir dest="$1" local base="${dest##*/}" - mkdir -p ${dest%/*} + local dir="${dest%/*}" + if [[ $dir != "$base" ]]; then + mkdir -p ${dest%/*} + fi ir=false # i result tmpdir=$(mktemp -d) cat >$tmpdir/"$base" @@ -300,22 +306,16 @@ soff () { for service; do # ignore services that dont exist if systemctl cat $service &>/dev/null; then - m systemctl stop $service; - m systemctl disable $service + m systemctl disable --now $service fi done } -sre () { +sre() { for service; do m systemctl restart $service m systemctl enable $service; done } -sstart() { - for service; do - m systemctl enable --now $service; - done -} mailhost() { [[ $HOSTNAME == "$MAIL_HOST" ]] } @@ -359,7 +359,7 @@ fi bhost_t=false case $HOSTNAME in $MAIL_HOST) : ;; - kd|frodo|x2|x3|kw|sy) + kd|frodo|x2|x3|kw|sy|bo) bhost_t=true ;; esac @@ -367,6 +367,10 @@ esac # * Install universal packages + +# installs epanicclean iptables-exim ip6tables-exim +/a/bin/ds/install-my-scripts + if [[ $(debian-codename-compat) == bionic ]]; then cat >/etc/apt/preferences.d/spamassassin <<'EOF' Package: spamassassin sa-compile spamc @@ -380,7 +384,7 @@ pi-nostart exim4 exim4-daemon-heavy spamassassin openvpn unbound clamav-daemon w # note: pyzor debian readme says you need to run some initialization command # but its outdated. -pi spf-tools-perl p0f postgrey pyzor razor jq moreutils certbot +pi spf-tools-perl p0f postgrey pyzor razor jq moreutils certbot fail2ban # bad packages that sometimes get automatically installed pu openresolv resolvconf @@ -404,21 +408,6 @@ fi # our nostart pi fails to avoid enabling -# * user forward file -case $HOSTNAME in - $MAIL_HOST) - # afaik, these will get ignored on MAIL_HOST because they are routing to my own - # machine, but rm them is safer - rm -fv $uhome/.forward /root/.forward - ;; - *) - # this can\'t be a symlink and has permission restrictions - # it might work in /etc/aliases, but this seems more proper. - e setting $uhome/.forward to $forward - install -m 644 {-o,-g}$u <(e $forward) $uhome/.forward - ;; -esac - # * Mail clean cronjob i /etc/systemd/system/mailclean.timer <<'EOF' @@ -462,30 +451,50 @@ EOF # * mail vpn config -vpnser=mailvpn.service +# old. +#vpnser=mailvpn.service +# todo: this hangs if it cant resolv the endpoint. we +# want it to just retry in the background. vpnser=wg-quick@wgmail.service case $HOSTNAME in $MAIL_HOST) + rsync -aiSAX --chown=root:root --chmod=g-s /p/c/filesystem/etc/wireguard/ /etc/wireguard bindpaths="/etc/127.0.0.1-resolv:/run/systemd/resolve /etc/basic-nsswitch:/etc/resolved-nsswitch:norbind" ;;& bk) bindpaths="/etc/10.173.8.1-resolv:/etc/127.0.0.1-resolv" ;;& + *) + d=/p/c/machine_specific/$HOSTNAME/filesystem/etc/wireguard/ + if [[ -d $d ]]; then + rsync -aiSAX --chown=root:root --chmod=g-s $d /etc/wireguard + fi + ;; esac -i /etc/systemd/system/wg-quick@wgmail.service.d/override.conf <, Serg G. Brester +# made config file IPv6 capable (see new section Init?family=inet6) + +[INCLUDES] + +after = iptables-blocktype.local + iptables-common.local +# iptables-blocktype.local is obsolete + +[Definition] + +# Option: actionflush +# Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action) +# Values: CMD +# +actionflush = -F f2b- + + +[Init] + +# Option: chain +# Notes specifies the iptables chain to which the Fail2Ban rules should be +# added +# Values: STRING Default: INPUT +chain = INPUT + +# Default name of the chain +# +name = default + +# Option: port +# Notes.: specifies port to monitor +# Values: [ NUM | STRING ] Default: +# +port = ssh + +# Option: protocol +# Notes.: internally used by config reader for interpolations. +# Values: [ tcp | udp | icmp | all ] Default: tcp +# +protocol = tcp + +# Option: blocktype +# Note: This is what the action does with rules. This can be any jump target +# as per the iptables man page (section 8). Common values are DROP +# REJECT, REJECT --reject-with icmp-port-unreachable +# Values: STRING +blocktype = REJECT --reject-with icmp-port-unreachable + +# Option: returntype +# Note: This is the default rule on "actionstart". This should be RETURN +# in all (blocking) actions, except REJECT in allowing actions. +# Values: STRING +returntype = RETURN + +# Option: lockingopt +# Notes.: Option was introduced to iptables to prevent multiple instances from +# running concurrently and causing irratic behavior. -w was introduced +# in iptables 1.4.20, so might be absent on older systems +# See https://github.com/fail2ban/fail2ban/issues/1122 +# Values: STRING +lockingopt = -w + +# Option: iptables +# Notes.: Actual command to be executed, including common to all calls options +# Values: STRING +iptables = /usr/local/bin/iptables-exim + + +[Init?family=inet6] + +# Option: blocktype (ipv6) +# Note: This is what the action does with rules. This can be any jump target +# as per the iptables man page (section 8). Common values are DROP +# REJECT, REJECT --reject-with icmp6-port-unreachable +# Values: STRING +blocktype = REJECT --reject-with icmp6-port-unreachable + +# Option: iptables (ipv6) +# Notes.: Actual command to be executed, including common to all calls options +# Values: STRING +iptables = /usr/local/bin/ip6tables-exim +EOF + +i /etc/fail2ban/jail.d/exim.local <<'EOF' +[exim] +enabled = true +port = 25,587 +filter = exim +banaction = iptables-exim +EOF +if $ir; then + m systemctl restart fail2ban +fi + # * common exim4 config +## old, not using forward files anymore +rm -fv $uhome/.forward /root/.forward + + # Make all system users be aliases. preventative -# measure for things like cron mail for user without alias -awk 'BEGIN { FS = ":" } ; $6 !~ /^\/home/ { print $1 }' /etc/passwd| while read -r user; do +# prevents things like cron mail for user without alias +awk 'BEGIN { FS = ":" } ; $6 !~ /^\/home/ || $7 ~ /\/nologin$/ { print $1 }' /etc/passwd| while read -r user; do if [[ ! $user ]]; then continue fi @@ -810,6 +956,20 @@ awk 'BEGIN { FS = ":" } ; $6 !~ /^\/home/ { print $1 }' /etc/passwd| while read fi done + +awk 'BEGIN { FS = ":" } ; $6 ~ /^\/home/ && $7 !~ /\/nologin$/ { print $1 }' /etc/passwd| while read -r user; do + case $HOSTNAME in + $MAIL_HOST) + sed -i "/^user:/d" /etc/aliases + ;; + *) + if ! grep -q "^$user:" /etc/aliases; then + echo "$user: root" |m tee -a /etc/aliases + fi + ;; + esac +done + if ! grep -q "^ncsoft:" /etc/aliases; then echo "ncsoft: graceq2323@gmail.com" |m tee -a /etc/aliases fi @@ -846,16 +1006,24 @@ if (( ${#files[@]} )); then ${files[@]} /etc/exim4 fi -# by default, only 10 days of logs are kept. increase that. -m sed -ri 's/^(\s*rotate\s).*/\11000/' /etc/logrotate.d/exim4-base - +# By default, only 10 days of logs are kept. increase that. +# And dont compress, I look back at logs too often and +# dont need the annoyance of decompressing them all the time. +m sed -ri '/^\s*compress\s*$/d;s/^(\s*rotate\s).*/\11000/' /etc/logrotate.d/exim4-base +files=(/var/log/exim4/*.gz) +if (( ${#files[@]} )); then + gunzip ${files[@]} +fi -## https://blog.dhampir.no/content/make-exim4-on-debian-respect-forward-and-etcaliases-when-using-a-smarthost -# i only need .forwards, so just doing that one. -cd /etc/exim4/conf.d/router -b=userforward_higher_priority -# replace the router name so it is unique -sed -r s/^\\S+:/$b:/ 600_exim4-config_userforward >175_$b +## disabled. not using .forward files, but this is still interesting +## for reference. +# ## https://blog.dhampir.no/content/make-exim4-on-debian-respect-forward-and-etcaliases-when-using-a-smarthost +# # i only need .forwards, so just doing that one. +# cd /etc/exim4/conf.d/router +# b=userforward_higher_priority +# # replace the router name so it is unique +# sed -r s/^\\S+:/$b:/ 600_exim4-config_userforward >175_$b +rm -fv /etc/exim4/conf.d/router/175_userforward_higher_priority # todo, consider 'separate' in etc/exim4.conf, could it help on busy systems? @@ -884,7 +1052,16 @@ EOF rm -vf /etc/exim4/conf.d/main/000_localmacros # old filename -cat >/etc/exim4/conf.d/main/000_local </etc/exim4/conf.d/main/000_local2 </etc/exim4/conf.d/main/000_local <<'EOF' MAIN_TLS_ENABLE = true # require tls connections for all smarthosts @@ -906,11 +1083,6 @@ MAIN_LOG_SELECTOR = +all # Based on spec, seems like a good idea to be nice. smtp_return_error_details = true -# normally empty, I set this so I can set the envelope address -# when doing mail redelivery to invoke filters. Also allows -# me exiqgrep and stuff. -MAIN_TRUSTED_GROUPS = $u - # default is 10. when exim has been down for a bit, fsf mailserver # will do a big send in one connection, then exim decides to put # the messages in the queue instead of delivering them, to avoid @@ -929,10 +1101,10 @@ DKIM_SELECTOR = li # There could be some circumstance when the # from: isnt our domain, but the envelope sender is # and so still want to sign, but I cant think of any case. -DKIM_DOMAIN = \${lc:\${domain:\$rh_from:}} +DKIM_DOMAIN = ${lc:${domain:$rh_from:}} # The file is based on the outgoing domain-name in the from-header. # sign if key exists -DKIM_PRIVATE_KEY = \${if exists{/etc/exim4/\${dkim_domain}-private.pem} {/etc/exim4/\${dkim_domain}-private.pem}} +DKIM_PRIVATE_KEY = ${if exists{/etc/exim4/${dkim_domain}-private.pem} {/etc/exim4/${dkim_domain}-private.pem}} # most of the ones that gmail seems to use. # Exim has horrible default of signing unincluded @@ -956,6 +1128,19 @@ hostlist iank_trusted = <; \\ 85.119.82.128 ; 2001:ba8:1f1:f09d::2 ; \\ # fsf_mit_net fsf_mit_net_ip6 fsf_net fsf_net_ip6 fsf_office_net 18.4.89.0/24 ; 2603:3005:71a:2e00::/64 ; 209.51.188.0/24 ; 2001:470:142::/48 ; 74.94.156.208/28 + + +# this is the default delay_warning_condition, plus matching on local_domains. +# If I have some problem with my local system that causes delayed delivery, +# I dont want to send warnings out to non-local domains. +delay_warning_condition = ${if or {\ + { !eq{$h_list-id:$h_list-post:$h_list-subscribe:}{} }\ + { match{$h_precedence:}{(?i)bulk|list|junk} }\ + { match{$h_auto-submitted:}{(?i)auto-generated|auto-replied} }\ + { match_domain{$domain}{+local_domains} }\ + } {no}{yes}} + + EOF rm -fv /etc/exim4/rcpt_local_acl # old path @@ -1015,14 +1200,14 @@ warn EOF -i /etc/exim4/conf.d/router/900_exim4-config_local_user < 'OC_User_IMAP','arguments' => array('127.0.0.1', 143, null),),); +#\$CONFIG['user_backends'] = array(array('class' => 'OC_User_IMAP','arguments' => array('127.0.0.1', 143, null),),); # based on installer check @@ -2118,7 +2301,7 @@ if (( ${#files[@]} )); then fi -# ** auth +# ** exim: auth case $HOSTNAME in bk|je) @@ -2154,7 +2337,7 @@ server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} EOF fi -# ** main daemon use non-default config file +# ** exim: main daemon use non-default config file case $HOSTNAME in bk|$MAIL_HOST) # to see the default comments in /etc/default/exim4: @@ -2165,8 +2348,11 @@ QUEUERUNNER='combined' QUEUEINTERVAL='30m' COMMONOPTIONS='-C /etc/exim4/my.conf' UPEX4OPTS='-o /etc/exim4/my.conf' -#E4BCD_PANICLOG_NOISE='malware acl condition: clamd /var/run/clamav/clamd\.ctl : unable to connect to UNIX socket' +#E4BCD_PANICLOG_NOISE='exim user lost privilege for using -C option' EOF + chown Debian-exim:Debian-exim /usr/sbin/exim4 + # needs guid set in order to become Debian-exim + chmod g+s,u+s /usr/sbin/exim4 i /etc/exim4/trusted_configs <<'EOF' /etc/exim4/my.conf EOF @@ -2180,7 +2366,51 @@ EOF ;; esac +# ** exim non-root + case $HOSTNAME in + bk|je|li) + # no reason to expect it to ever be there. + rm -fv /etc/systemd/system/exim4.service.d/nonroot.conf + ;; + *) + i /etc/systemd/system/exim4.service.d/nonroot.conf <<'EOF' +[Service] +# see 56.2 Root privilege in exim spec +AmbientCapabilities=CAP_NET_BIND_SERVICE +# https://www.redhat.com/sysadmin/mastering-systemd +# things that seem good and reasonabl.e +PrivateTmp=yes +ProtectHome=yes +# note, in t10 systemd, if one of these is an sshfs mountpoint, +# this whole setting doesnt work. tried it with a newer systemd 250 though +# an nspawn, and it worked there. +InaccessiblePaths=d m media mnt nocow o p q +NoNewPrivileges=yes +ProtectSystem=yes + +# when we get newer systemd +#ProtectDevices=yes +EOF + i /etc/exim4/conf.d/main/000_local-noroot <<'EOF' +# see 56.2 Root privilege in exim spec +deliver_drop_privilege = true +EOF + files=( + 300_exim4-config_real_local + 600_exim4-config_userforward + 700_exim4-config_procmail + 800_exim4-config_maildrop + mmm_mail4root + ) + for f in ${files[@]}; do + echo "# iank: removed due to running nonroot"|i /etc/exim4/conf.d/router/$f + done + ;; +esac + +case $HOSTNAME in + # ** $MAIL_HOST|bk|je) $MAIL_HOST|bk|je) @@ -2197,7 +2427,6 @@ EOF CHECK_RCPT_VERIFY_SENDER = true # default config comment says: If you enable this, you might reject legitimate mail, # but eggs has had this a long time, so that seems unlikely. -CHECK_DATA_VERIFY_HEADER_SYNTAX = true CHECK_RCPT_SPF = true CHECK_RCPT_REVERSE_DNS = true CHECK_MAIL_HELO_ISSUED = true @@ -2327,6 +2556,7 @@ ignore_target_hosts = ${HOSTNAME}wg.b8.nz # note changes here also require change in passwd.client route_list = * eximbackup.b8.nz same_domain_copy_routing = yes +errors_to = alerts@iankelling.org no_more EOF @@ -2340,9 +2570,8 @@ backup_remote: .endif hosts_require_auth = * hosts_try_auth = * - return_path = alerts@iankelling.org envelope_to_add - # manual return path because we dont want it to be the envelope sender + # manual return path because we want it to be the envelope sender # we got not the one we are using in this smtp transport headers_add = "Return-path: $sender_address" .ifdef REMOTE_SMTP_SMARTHOST_HOSTS_AVOID_TLS @@ -2380,10 +2609,10 @@ EOF # this avoids some error. i cant remember what. todo: # test it out and document why/if its needed. - i /etc/exim4/host_local_deny_exceptions <<'EOF' -mail.fsf.org -*.posteo.de -EOF + # i /etc/exim4/host_local_deny_exceptions <<'EOF' + # mail.fsf.org + # *.posteo.de + # EOF # cron email from smarthost hosts will automatically be to # USER@FQDN. I redirect that to alerts@, on the smarthosts, but in @@ -2448,13 +2677,13 @@ EOF /a/exe/cedit nn /etc/hosts <<'EOF' || [[ $? == 1 ]] # note: i put nn.b8.nz into bind for good measure -10.173.8.2 nn.b8.nz mail.iankelling.org +10.173.8.2 nn.b8.nz mx.iankelling.org EOF # note: systemd-resolved will consult /etc/hosts, dnsmasq wont. this assumes # weve configured this file in dnsmasq if we are using it. /a/exe/cedit mail /etc/dnsmasq-servers.conf <<'EOF' || [[ $? == 1 ]] -server=/mail.iankelling.org/127.0.1.1 +server=/mx.iankelling.org/127.0.1.1 EOF # I used to use debconf-set-selections + dpkg-reconfigure, # which then updates this file @@ -2466,10 +2695,12 @@ EOF # The debconf questions output is additional documentation that is not # easily accessible, but super long, along with the initial default comment in this # file, so I've saved that into ./mail-notes.conf. + # + # # TODO: remove mx.iankelling.org once systems get updated mail-setup from jan 2022 cat >>/etc/exim4/update-exim4.conf.conf <>/etc/exim4/update-exim4.conf.conf <>/etc/exim4/update-exim4.conf.conf <>/etc/exim4/update-exim4.conf.conf <>/etc/exim4/conf.d/main/000_local-nn <<'EOF' + # spool_directory = /var/spool/myexim4 + # EOF cat >>/etc/myexim4/update-exim4.conf.conf <<'EOF' dc_eximconfig_configtype='smarthost' dc_smarthost='nn.b8.nz' @@ -2733,6 +2985,8 @@ if $reload; then m systemctl daemon-reload fi +m systemctl --now enable epanicclean.timer + case $HOSTNAME in je) /a/exe/web-conf apache2 je.b8.nz @@ -2752,25 +3006,31 @@ case $HOSTNAME in ln -sf 127.0.0.1-resolv/stub-resolv.conf /etc/resolv.conf ;;& $MAIL_HOST|bk) - sstart mailnn mailnnroute - # If these have changes, id rather manually restart it, id rather - # not restart and cause temporary errors - if $reload; then - sre $vpnser - else - sstart $vpnser - fi + m systemctl --now enable mailnn mailnnroute ;;& $MAIL_HOST) + # we use dns to start wg if $reload; then sre unbound else - sstart unbound + m systemctl --now enable unbound fi ;;& $MAIL_HOST|bk) + # If these have changes, id rather manually restart it, id rather + # not restart and cause temporary errors + if $reload; then + sre $vpnser + else + m systemctl --now enable $vpnser + fi if ! systemctl is-active clamav-daemon >/dev/null; then - sstart clamav-daemon + m systemctl --now enable clamav-daemon + out=$(rsync -aiSAX --chown=root:root --chmod=g-s /a/bin/ds/filesystem/etc/systemd/system/epanicclean.{timer,service} /etc/systemd/system) + if [[ $out ]]; then + reload=true + fi + # note, this will cause paniclog entries because it takes like 45 # seconds for clamav to start, i use ./epanic-clean to remove # them. @@ -2782,13 +3042,13 @@ case $HOSTNAME in # need to wait a bit before restarting exim, else I # get a paniclog entry like: spam acl condition: all spamd servers failed sleep 3 - sstart mailclean.timer + m systemctl --now enable mailclean.timer ;;& $MAIL_HOST) # < 2.1 (eg: in t9), uses a different data format which required manual # migration. dont start if we are running an old version. if dpkg --compare-versions $(dpkg -s radicale | awk '$1 == "Version:" { print $2 }') ge 2.1; then - sstart radicale + m systemctl --now enable radicale fi ;;& esac @@ -2806,6 +3066,16 @@ esac sre exim4 +case $HOSTNAME in + $MAIL_HOST) + m systemctl --now enable mailbindwatchdog + ;; + *) + soff mailbindwatchdog + ;; +esac + + case $HOSTNAME in bk) sre exim4in ;; esac @@ -2831,6 +3101,8 @@ MAILTO=alerts@iankelling.org #5-59/5 * * * * root mailtest-check |& log-once -1 mailtest-check #0 * * * * root mailtest-check slow |& log-once -1 mailtest-slow */5 * * * * root timeout 290 mailtest-check slow |& log-once -12 mailtest-check +# if a bounce happened yesterday, dont let it slip through the cracks +8 1 * * * root awk '$5 == "**"' /var/log/exim4/mainlog.1 EOF m sudo rsync -ahhi --chown=root:root --chmod=0755 \ /b/ds/mailtest-check /b/ds/check-remote-mailqs /usr/local/bin/ @@ -2840,7 +3112,7 @@ EOF test_to="testignore@expertpathologyreview.com, testignore@je.b8.nz, testignore@amnimal.ninja, jtuttle@gnu.org" cat >>/etc/cron.d/mailtest </usr/local/bin/send-test-forward <<'EOF' #!/bin/bash -exiqgrep -o 260 -i -r '^(testignore@(iankelling\.org|zroe\.org|expertpathologyreview\.com|amnimal\.ninja|je\.b8\.nz)|jtuttle@gnu\.org)$' | xargs /sbin/exim -Mrm >/dev/null +olds=( +$(/sbin/exiqgrep -o 260 -i -r '^(testignore@(iankelling\.org|zroe\.org|expertpathologyreview\.com|amnimal\.ninja|je\.b8\.nz)|jtuttle@gnu\.org)$') +) +if (( ${#olds[@]} )); then + /sbin/exim -Mrm "${olds[@]}" >/dev/null +fi EOF for test_from in ${test_froms[@]}; do cat >>/usr/local/bin/send-test-forward <