# Copyright (C) 2019 Ian Kelling
# SPDX-License-Identifier: AGPL-3.0-or-later
-# todo: sandbox / harden exim:
-# 1. stop it from running as root. how?
-# https://www.exim.org/exim-html-current/doc/html/spec_html/ch-security_considerations.html
-# * avoid using .forward files, remove that router
-# * set deliver_drop_privilege
-# * set user to run as Debian-exim in systemd
-# * set port to something like 2500, and forward 25 to 2500 with iptables. same for 587.
-# https://superuser.com/questions/710253/allow-non-root-process-to-bind-to-port-80-and-443/1334552#1334552
-# * consider whether other routers like postmaster need modification / removal.
-# 2. restrict its filesystem access from within systemd
-
# 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: 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
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
# * Install universal packages
-# installs epanicclean
+# installs epanicclean iptables-exim ip6tables-exim
/a/bin/ds/install-my-scripts
if [[ $(debian-codename-compat) == bionic ]]; then
# 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
# 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'
;;
esac
-i /etc/systemd/system/wg-quick@wgmail.service.d/override.conf <<EOF
+case $HOSTNAME in
+ li) : ;;
+ *)
+ i /etc/systemd/system/wg-quick@wgmail.service.d/override.conf <<EOF
[Unit]
Requires=mailnn.service
After=network.target mailnn.service
JoinsNamespaceOf=mailnn.service
BindsTo=mailnn.service
+StartLimitIntervalSec=0
[Service]
PrivateNetwork=true
# i dont think we need any of these, but it doesnt hurt to stay consistent
BindPaths=$bindpaths
+
+Restart=on-failure
+RestartSec=20
EOF
+ ;;
+esac
+
# https://selivan.github.io/2017/12/30/systemd-serice-always-restart.html
i /etc/systemd/system/mailvpn.service <<EOF
BindPaths=$bindpaths
Restart=always
# time to sleep before restarting a service
-RestartSec=1
+RestartSec=20
[Install]
WantedBy=multi-user.target
ExecStart=/usr/bin/flock -w 20 /tmp/newns.flock /a/bin/newns/newns -n 10.173.8 start mail
ExecStop=/usr/bin/flock -w 20 /tmp/newns.flock /a/bin/newns/newns stop mail
Restart=always
-RestartSec=10
+RestartSec=20
[Install]
ExecStart=/usr/local/bin/mailbindwatchdog $vpnser ${nn_progs[@]} unbound.service radicale.service
Restart=always
# time to sleep before restarting a service
-RestartSec=1
+RestartSec=10
[Install]
WantedBy=multi-user.target
BindPaths=$bindpaths
Restart=always
-RestartSec=1
+RestartSec=20
EOF
# sooo, there are a few ways to get traffic from the mail network
BindPaths=$bindpaths
Restart=always
-RestartSec=1
+RestartSec=20
EOF
done
;;
--deploy-hook /a/bin/ds/le-exim-deploy -d $wghost
fi
+# * fail2ban
+
+# todo: test that these configs actually work, eg run
+# s iptables-exim -S
+# and see someone is banned.
+
+sed 's/^ *before *= *iptables-common.conf/before = iptables-common-exim.conf/' \
+ /etc/fail2ban/action.d/iptables-multiport.conf| i /etc/fail2ban/action.d/iptables-exim.conf
+i /etc/fail2ban/action.d/iptables-common-exim.conf <<'EOF'
+# iank: same as iptables-common, except iptables is iptables-exim, ip6tables is ip6tables-exim
+
+# Fail2Ban configuration file
+#
+# Author: Daniel Black
+#
+# This is a included configuration file and includes the definitions for the iptables
+# used in all iptables based actions by default.
+#
+# The user can override the defaults in iptables-common.local
+#
+# Modified: Alexander Koeppe <format_c@online.de>, Serg G. Brester <serg.brester@sebres.de>
+# 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 = <iptables> -F f2b-<name>
+
+
+[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 <lockingopt>
+
+
+[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 <lockingopt>
+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
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
m sed -ri 's/^(\s*rotate\s).*/\11000/' /etc/logrotate.d/exim4-base
-## 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
# todo, consider 'separate' in etc/exim4.conf, could it help on busy systems?
BindPaths=$bindpaths
Restart=always
# time to sleep before restarting a service
-RestartSec=1000
+RestartSec=20
[Install]
# for openvpn
break
fi
done
- for f in /p/c/subdir_files/sieve/*sieve /a/c/subdir_files/sieve/*sieve; do
- m sudo -u $u /a/exe/lnf -T $f $uhome/sieve/${f##*/}
+ for f in /p/c/subdir_files/sieve/*sieve /a/bin/ds/subdir_files/sieve/*sieve; do
+ m sudo -u $u /a/exe/lnf -v -T $f $uhome/sieve/${f##*/}
done
# https://wiki.dovecot.org/SSL/DovecotConfiguration
ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl_protocols = TLSv1.2
ssl_prefer_server_ciphers = no
-ssl_dh_parameters_length = 2048
protocol lmtp {
#per https://wiki2.dovecot.org/Pigeonhole/Sieve/Configuration
#### begin dl roundcube
# note, im r2e subbed to https://github.com/roundcube/roundcubemail/releases.atom
- v=1.4.11; f=roundcubemail-$v-complete.tar.gz
+ v=1.4.13; f=roundcubemail-$v-complete.tar.gz
cd /a/opt
if [[ -e $f ]]; then
timestamp=$(stat -c %Y $f)
m chown -R www-data.www-data $rctmpdir /m/rc
m chmod 750 $rctmpdir
# Ensure the log file monitored by fail2ban exists, or else fail2ban can't start.
- # todo: setup fail2ban
# todo: check for other mailinabox things
m sudo -u www-data touch $rclogdir/errors.log
m phpenmod -v php mcrypt imap
# dpkg says this is required
m a2enmod proxy_fcgi setenvif
- fpm=$(dpkg-query -s php-fpm | sed -nr 's/^Depends:.* (php[^ ]*-fpm)( .*|$)/\1/p') # eg: php7.3-fpm
+ fpm=$(dpkg-query -s php-fpm | sed -nr 's/^Depends:.* (php[^ ]*-fpm)( .*|$)/\1/p') # eg: php7.4-fpm
phpver=$(dpkg-query -s php-fpm | sed -nr 's/^Depends:.* php([^ ]*)-fpm( .*|$)/\1/p')
m a2enconf $fpm
# 3 useless guides on php fpm fcgi debian 10 later, i figure out from reading
fi
-# ** auth
+# ** exim: auth
case $HOSTNAME in
bk|je)
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:
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
;;
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
+ # Note: there are other routers that would also fail due to not running as root,
+ # but afaik, the main router will catch all mail. If not, we will see
+ # something in the queue.
+ rm -fv /etc/exim4/conf.d/router/600_exim4-config_userforward
+ ;;
+esac
+
case $HOSTNAME in
+
# ** $MAIL_HOST|bk|je)
$MAIL_HOST|bk|je)
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
# 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
.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
# 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
# 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 <<EOF
# man page: is used to build the local_domains list, together with "localhost"
# this is duplicated in a later router.
-dc_other_hostnames='iankelling.org;zroe.org;r2e.iankelling.org;!je.b8.nz;!bk.b8.nz;*.b8.nz;b8.nz'
+dc_other_hostnames='iankelling.org;zroe.org;r2e.iankelling.org;mx.iankelling.org;!je.b8.nz;!bk.b8.nz;*.b8.nz;b8.nz'
EOF
[Service]
Restart=always
# time to sleep before restarting a service
-RestartSec=1
+RestartSec=20
EOF
i /etc/default/exim4in <<'EOF'
echo | /a/exe/cedit nn /etc/hosts || [[ $? == 1 ]]
echo | /a/exe/cedit mail /etc/dnsmasq-servers.conf || [[ $? == 1 ]]
+
if $bhost_t; then
+ install -d /bu
+ install -d -g $u -o $u -m 771 /bu/md
i /etc/exim4/conf.d/transport/30_backup_maildir <<EOF
# modified debian maildir transport
backup_maildir:
transport = backup_maildir
EOF
+ # Bind to wghole to receive mailbackup.
wgholeip=$(sed -rn 's/^ *Address *= *([^/]+).*/\1/p' /etc/wireguard/wghole.conf)
cat >>/etc/exim4/update-exim4.conf.conf <<EOF
dc_other_hostnames='eximbackup.b8.nz'
dc_local_interfaces='127.0.0.1;::1;$wgholeip'
+EOF
+
+ # wghole & thus exim will fail to start without internet connectivity.
+ i /etc/systemd/system/exim4.service.d/backup.conf <<'EOF'
+[Unit]
+StartLimitIntervalSec=0
+[Service]
+Restart=always
+RestartSec=20
EOF
+
else
cat >>/etc/exim4/update-exim4.conf.conf <<EOF
# Note: If theres like a temporary problem where mail gets sent to
# instead of a permanent 5xx.
dc_local_interfaces='127.0.0.1;::1'
EOF
+ rm -fv /etc/systemd/system/exim4.service.d/backup.conf
fi
cat >>/etc/exim4/update-exim4.conf.conf <<EOF
dc_eximconfig_configtype='smarthost'
$MAIL_HOST|bk)
# config for the non-nn exim
m rsync -ra --delete /etc/exim4/ /etc/myexim4
+ # If we ever wanted to have a separate spool,
+ # we could do it like this.
+ # cat >>/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'
case $HOSTNAME in
$MAIL_HOST)
m systemctl --now enable mailbindwatchdog
- ;;
+ ;;
*)
soff mailbindwatchdog
;;
test_to="testignore@expertpathologyreview.com, testignore@je.b8.nz, testignore@amnimal.ninja, jtuttle@gnu.org"
cat >>/etc/cron.d/mailtest <<EOF
-2 * * * * $u check-remote-mailqs |& log-once check-remote-mailqs
+2 * * * * root check-remote-mailqs |& log-once check-remote-mailqs
EOF
;;&
bk)