# Copyright (C) 2019 Ian Kelling
# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# todo: look into changing nsswitch.com to make programs prefer using systemd-resolved
+# but not over the network.
+
+# background: I want to run exim in a network namespace so it can send
+# and receive through a vpn. This is needed so it can do ipv6, because
+# outside the namespace if we dont have ipv6, to send ipv6 through the
+# vpn, we have to send all our ipv6 through the vpn. I did this for a
+# long time, it was fine, but it causes various pains, like increased
+# latency, increased recaptcha because my ip is from a data center, just
+# various issues I dont want on all the time. The problem with the
+# namespace is that all kinds of programs want to invoke exim, but they
+# wont be in the namespace. I could replace exim with a wrapper that
+# jumps into the namespace, i tried that, it works fine. One remaining
+# problem was that I would have needed to hook into exim upgrades to
+# move exim and replace it with my wrapper script. Also, my script to
+# join the namespace is not super reliable because it uses a pgrep.
+# Instead, I should have created a systemd service for a process that
+# will never die and just writes its pid somewhere convenient.
+# That implementation
+# is below here:
+#
+# sudoers:
+# user ALL=(ALL) /usr/sbin/exim4
+#
+# move exim4 to eximian, use this script for exim4:
+#
+# #!/bin/bash
+# if ip a show veth1-mail &>/dev/null; then
+# /usr/sbin/eximian "$@"
+# exit
+# fi
+# dosudo=false
+# if [[ $USER && $USER != root ]]; then
+# dosudo=true
+# fi
+# pid=$(pgrep -f "/usr/sbin/openvpn .* --config /etc/openvpn/.*mail.conf")
+# if $dosudo; then
+# sudo nsenter -t $pid -n -m sudo -u $USER /usr/sbin/eximian "$@"
+# else
+# nsenter -t $pid -n -m /usr/sbin/eximian "$@"
+# fi
+# ## end script
+#
+# an alternate solution: there is a small setguid program for
+# network namespaces in my bookmarks.
+#
+# However, the solution I went with is: have 2 exim
+# configs. A nonstandard location for the daemon that runs
+# in the namespace. For all other invocations, it uses
+# the default config location, which is altered to be
+# in a smarthost config which sends mail to the deaemon.
+#
+# I have a bash function, enn to invoke exim like the daemon is running.
+# and mailbash to just enter its network namespace.
+
if [ -z "$BASH_VERSION" ]; then echo "error: shell is not bash" >&2; exit 1; fi
pre="${0##*/}:"
# our nostart pi fails to avoid enabling
systemctl disable openvpn
-# trisquel 8 = openvpn, debian stretch = openvpn-client
-vpn_ser=openvpn-client
-if [[ ! -e /lib/systemd/system/openvpn-client@.service ]]; then
- vpn_ser=openvpn
-fi
-
uhome=$(eval echo ~$u)
### * user forward file
EOF
# * spamassassin
+## also has a bit of exim config
cat >/etc/sysctl.d/80-iank-mail.conf <<'EOF'
# see exim spec
EOF
case $HOSTNAME in
+ $MAIL_HOST)
+
+ f=/etc/nn-resolv/stub-resolv.conf
+ l="nameserver 8.8.8.8"
+ if ! grep -Fxq "$l" /etc/nn-resolv/stub-resolv.conf &>/dev/null; then
+ mkdir -p ${f%/*}
+ echo "$l" >$f
+ chattr +i $f
+ fi
+
+ reload=false
+ for unit in exim4 spamassassin; do
+ f=/etc/systemd/system/$unit.service.d/nn.conf
+ if [[ ! -s $f || $(stat -c%s $f) != 244 ]]; then
+ reload=true
+ echo creating $f
+ mkdir -p ${f%/*}
+ cat >$f <<'EOF'
+[Unit]
+After=network.target
+Requires=openvpn-client-mail@mail.service
+After=openvpn-client-mail@mail.service
+JoinsNamespaceOf=openvpn-client-mail@mail.service
+
+[Service]
+PrivateNetwork=true
+BindPaths=/etc/nn-resolv:/run/systemd/resolve:norbind
+EOF
+ fi
+ done
+ if $reload; then
+ m systemctl daemon-reload
+ fi
+;;
+
bk|$MAIL_HOST)
m systemctl stop spamassassin
m systemctl disable spamassassin
fi
-rsync -aiSAX --chown=root:root --chmod=g-s /a/bin/ds/mail-cert-cron /usr/local/bin
+
+m rsync -aiSAX --chown=root:root --chmod=g-s /a/bin/ds/mail-cert-cron /usr/local/bin
cat >/etc/systemd/system/mailcert.service <<'EOF'
# * common exim4 config
source /a/bin/bash_unpublished/source-state
-
if [[ ! $MAIL_HOST ]]; then
err "\$MAIL_HOST not set"
fi
+
m gpasswd -a iank adm #needed for reading logs
# replace the router name so it is unique
sed -r s/^\\S+:/$b:/ 600_exim4-config_userforward >175_$b
+#### begin setup alternate config for daemon
+update-exim4defaults -f --commonoptions '-C /etc/exim4/my.conf'
+l="UPEX4OPTS='-o /etc/exim4/my.conf'"
+if ! grep -Fxq "$l" /etc/default/exim4; then
+ sed -i '/^ *UPEX4OPTS=/d' /etc/default/exim4
+ echo "$l" >> /etc/default/exim4
+fi
+cat >/etc/exim4/trusted_configs <<'EOF'
+/etc/exim4/my.conf
+EOF
+#### end setup alternate config for daemon
+
+# alerts is basically the postmaster address
+sed -i --follow-symlinks -f - /etc/aliases <<EOF
+\$a root: alerts@mail.iankelling.org
+/^root:/d
+EOF
rm -vf /etc/exim4/conf.d/main/000_localmacros # old filename
cat >/etc/exim4/conf.d/main/000_local <<EOF
;;
esac
-# todo: for mail submission, test imap based authentication for bk.
-# eg: can we send as other ppl?
# see sender validation in /a/opt/mailinabox/setup/mail-users.sh
cat >/etc/exim4/conf.d/router/900_exim4-config_local_user <<'EOF'
### router/900_exim4-config_local_user
-----END DH PARAMETERS-----
EOF
cat >/etc/dovecot/local.conf <<EOF
-!include /etc/dovecot/local.conf.ext
# https://ssl-config.mozilla.org
ssl = required
ssl_cert = </etc/exim4/exim.crt
#per https://wiki2.dovecot.org/Pigeonhole/Sieve/Configuration
# default is just \$mail_plugins
mail_plugins = \$mail_plugins sieve
-# This downcases the localpart. default is case sensitive.
-# case sensitive local part will miss out on valid email when some person or system
-# mistakenly capitalizes things.
- auth_username_format = %Lu
-}
-
-# make 147 only listen on localhost, plan to use for nextcloud.
-# copied from mailinabox
-service imap-login {
- inet_listener imap {
- address = 127.0.0.1
- }
-}
-# https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_dovecot_authenticator.html
-service auth {
- unix_listener auth-client {
- user = Debian-exim
- group = Debian-exim
- }
}
EOF
EOF
fi
- cat >/etc/dovecot/local.conf.ext <<'EOF'
-passdb {
- driver = sql
- args = /etc/dovecot/dovecot-sql.conf.ext
-}
-userdb {
- driver = sql
- args = /etc/dovecot/dovecot-sql.conf.ext
-}
-
-EOF
-
- cat >/etc/dovecot/dovecot-sql.conf.ext <<'EOF'
-# from mailinabox
-driver = sqlite
-connect = /m/rc/users.sqlite
-default_pass_scheme = SHA512-CRYPT
-password_query = SELECT email as user, password FROM users WHERE email='%u';
-user_query = SELECT email AS user, "mail" as uid, "mail" as gid, "/m/md/%d/%n" as home FROM users WHERE email='%u';
-iterate_query = SELECT email AS user FROM users;
-EOF
- chmod 0600 /etc/dovecot/dovecot-sql.conf.ext # per Dovecot instructions
-
- # db needs to be in a www-data writable directory
- db=/m/rc/users.sqlite
- if [[ ! -s $db ]]; then
- sqlite3 $db <<'EOF'
-CREATE TABLE users (
-id INTEGER PRIMARY KEY AUTOINCREMENT,
-email TEXT NOT NULL UNIQUE,
-password TEXT NOT NULL,
-extra,
-privileges TEXT NOT NULL DEFAULT '');
-EOF
- fi
- # example of adding a user:
- # hash: doveadm pw -s SHA512-CRYPT -p passhere
- # sqlite3 /m/rc/users.sqlite <<'EOF'
- #insert into users (email, password) values ('testignore@bk.b8.nz', 'hash');
- #EOF
-
-
-
case $HOSTNAME in
$MAIL_HOST)
if [[ -e $f ]]; then
mv $f $f-iank-disabled
fi
+
cat >>/etc/dovecot/local.conf <<EOF
+!include /etc/dovecot/local.conf.ext
# for debugging info, uncomment these.
# logs go to syslog and to /var/log/mail.log
auth_verbose=yes
mail_debug=yes
+
+protocol lmtp {
+# This downcases the localpart. default is case sensitive.
+# case sensitive local part will miss out on valid email when some person or system
+# mistakenly capitalizes things.
+ auth_username_format = %Lu
+}
+
+# make 147 only listen on localhost, plan to use for nextcloud.
+# copied from mailinabox
+service imap-login {
+ inet_listener imap {
+ address = 127.0.0.1
+ }
+}
+# https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_dovecot_authenticator.html
+service auth {
+ unix_listener auth-client {
+ user = Debian-exim
+ group = Debian-exim
+ }
+}
+
+
plugin {
sieve_before = /etc/dovecot/sieve-spam.sieve
# from mailinabox
EOF
sievec /etc/dovecot/sieve-spam.sieve
+
+ cat >/etc/dovecot/local.conf.ext <<'EOF'
+passdb {
+ driver = sql
+ args = /etc/dovecot/dovecot-sql.conf.ext
+}
+userdb {
+ driver = sql
+ args = /etc/dovecot/dovecot-sql.conf.ext
+}
+
+EOF
+
+ cat >/etc/dovecot/dovecot-sql.conf.ext <<'EOF'
+# from mailinabox
+driver = sqlite
+connect = /m/rc/users.sqlite
+default_pass_scheme = SHA512-CRYPT
+password_query = SELECT email as user, password FROM users WHERE email='%u';
+user_query = SELECT email AS user, "mail" as uid, "mail" as gid, "/m/md/%d/%n" as home FROM users WHERE email='%u';
+iterate_query = SELECT email AS user FROM users;
+EOF
+ chmod 0600 /etc/dovecot/dovecot-sql.conf.ext # per Dovecot instructions
+
+ # db needs to be in a www-data writable directory
+ db=/m/rc/users.sqlite
+ if [[ ! -s $db ]]; then
+ mkdir -p /m/rc
+ sqlite3 $db <<'EOF'
+CREATE TABLE users (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+email TEXT NOT NULL UNIQUE,
+password TEXT NOT NULL,
+extra,
+privileges TEXT NOT NULL DEFAULT '');
+EOF
+ fi
+ # example of adding a user:
+ # hash: doveadm pw -s SHA512-CRYPT -p passhere
+ # sqlite3 /m/rc/users.sqlite <<'EOF'
+ #insert into users (email, password) values ('testignore@bk.b8.nz', 'hash');
+ #EOF
+
+
;;
esac
####### end dovecot-setup ########
# * nextcloud setup
-# https://docs.nextcloud.com/server/19/admin_manual/installation/source_installation.html
-# curl from the web installer requirement, but i switched to cli
-pi php-curl php-fileinfo php-bz2
-web-conf - apache2 expertpathologyreview.com <<'EOF'
+nextcloud-setup() {
+ # https://docs.nextcloud.com/server/19/admin_manual/installation/source_installation.html
+ # curl from the web installer requirement, but i switched to cli
+ pi php-curl php-fileinfo php-bz2
+ /a/exe/web-conf - apache2 expertpathologyreview.com <<'EOF'
Alias /nextcloud "/var/www/nextcloud/"
<Directory /var/www/nextcloud/>
Require all granted
</Directory>
EOF
-cd /var/www
-wget https://download.nextcloud.com/server/releases/latest.zip
-unzip -q latest.zip
-rm latest.zip
-chown -R www-data.www-data nextcloud
-cd /var/www/nextcloud
-sudo -u www-data php occ maintenance:install --database sqlite --admin-user iank --admin-pass swarm.numbered.alienist
-cd /var/www/nextcloud/config
-# https://docs.nextcloud.com/server/19/admin_manual/installation/source_installation.html
-cat >/etc/php/$phpver/fpm/pool.d/localwww.conf <<'EOF'
+ cd /var/www
+ wget https://download.nextcloud.com/server/releases/latest.zip
+ unzip -q latest.zip
+ rm latest.zip
+ chown -R www-data.www-data nextcloud
+ cd /var/www/nextcloud
+ sudo -u www-data php occ maintenance:install --database sqlite --admin-user iank --admin-pass swarm.numbered.alienist
+ cd /var/www/nextcloud/config
+ # https://docs.nextcloud.com/server/19/admin_manual/installation/source_installation.html
+ cat >/etc/php/$phpver/fpm/pool.d/localwww.conf <<'EOF'
[www]
clear_env = no
EOF
-cat config.php - >tmp.php <<'EOF'
+ cat config.php - >tmp.php <<'EOF'
$CONFIG['overwrite.cli.url'] = 'https://expertpathologyreview.com/nextcloud';
$CONFIG['htaccess.RewriteBase'] = '/nextcloud';
$CONFIG['trusted_domains'] = array (
var_export($CONFIG);
fwrite(STDOUT, ";\n");
EOF
-php tmp.php >config.php 2>/dev/null
-rm tmp.php
-sudo -u www-data php /var/www/nextcloud/occ maintenance:update:htaccess
-
+ php tmp.php >config.php 2>/dev/null
+ rm tmp.php
+ sudo -u www-data php /var/www/nextcloud/occ maintenance:update:htaccess
+}
# * roundcube setup
}
-# * if MAIL_HOST
+# * exim host conditional config
+
+
case $HOSTNAME in
+ # ** $MAIL_HOST|bk)
$MAIL_HOST|bk)
dovecot-setup
m systemctl enable dovecot
m systemctl start mailclean.timer
;;&
+ # ** $MAIL_HOST)
$MAIL_HOST)
-
- # * exim
-
sudo rsync -ahhi --chown=root:Debian-exim --chmod=0640 \
/p/c/filesystem/etc/exim4/passwd /p/c/filesystem/etc/exim4/*.pem /etc/exim4/
-
# mail.iankelling.org so local imap clients can connect with tls and
# when they happen to not be local.
sed -ri -f - /etc/hosts <<'EOF'
m chmod 755 $f
# make all system users be aliases
- for u in $(awk 'BEGIN { FS = ":" } ; $6 !~ /^\/home/ { print $1 }' /etc/passwd); do
- if ! grep -q "^$u:" aliases; then
+ for user in $(awk 'BEGIN { FS = ":" } ; $6 !~ /^\/home/ { print $1 }' /etc/passwd); do
+ if ! grep -q "^$user:" /etc/aliases; then
echo "$u: root" |tee -a /etc/aliases
fi
done
- # alerts is basically the postmaster address
- sed -i --follow-symlinks -f - /etc/aliases <<EOF
-\$a root: alerts@mail.iankelling.org
-/^root:/d
-EOF
+ # https://selivan.github.io/2017/12/30/systemd-serice-always-restart.html
+ f=/etc/systemd/system/openvpn-client-mail@.service
+ if [[ ! -s $f || $(stat -c%s $f) != 1709 ]]; then
+ cat >$f <<'EOF'
+[Unit]
+Description=OpenVPN tunnel for %I
+After=syslog.target network-online.target
+Wants=network-online.target
+Documentation=man:openvpn(8)
+Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
+Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO
+Requires=iptables.service
+# needed to continually restatr
+StartLimitIntervalSec=0
- # https://selivan.github.io/2017/12/30/systemd-serice-always-restart.html
- d=/etc/systemd/system/$vpn_ser@mail.service.d
- m mkdir -p $d
- cat >$d/override.conf <<'EOF'
[Service]
+Type=notify
+RuntimeDirectory=openvpn-client
+RuntimeDirectoryMode=0710
+WorkingDirectory=/etc/openvpn/client
+ExecStart=/usr/sbin/openvpn --suppress-timestamps --nobind --config /etc/openvpn/client/%i.conf
+#CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE
+LimitNPROC=10
+# DeviceAllow=/dev/null rw
+# DeviceAllow=/dev/net/tun rw
+ExecStartPre=/a/bin/newns/newns -n 10.173.8 start %i
+ExecStopPost=/a/bin/newns/newns stop %i
+PrivateNetwork=true
+# in the network namespace, we cant connect to systemd-resolved on 127.0.0.53,
+# because of
+# https://unix.stackexchange.com/questions/445782/how-to-allow-systemd-resolved-to-listen-to-an-interface-other-than-loopback
+# there is a workaround there, but i dont think its really worth it,
+# the mail server is fine with a static dns anyways.
+# This thread is also interesting,
+# https://github.com/slingamn/namespaced-openvpn/issues/7
+# todo: the iptables rule at the bottom could be useful to prevent
+# dns from leaking in my network namespaced vpn.
+# I also like the idea of patching systemd-resolved so it
+# will listen on other interfaces, but its not worth my time.
+BindPaths=/etc/nn-resolv:/run/systemd/resolve:norbind
+
Restart=always
# time to sleep before restarting a service
RestartSec=1
-[Unit]
-StartLimitIntervalSec=0
+
+[Install]
+WantedBy=multi-user.target
EOF
- if ! systemctl cat $vpn_ser@mail.service|grep -xF StartLimitInterval=0 &>/dev/null; then
- # needed for the above config to go into effect
- m systemctl daemon-reexec
+ m systemctl daemon-reload
+ m systemctl restart openvpn-client-mail@mail
fi
- m systemctl restart $vpn_ser@mail
- m systemctl enable $vpn_ser@mail
+ m systemctl start openvpn-client-mail@mail
+ m systemctl enable openvpn-client-mail@mail
+
+ /a/exe/cedit nn.b8.nz /etc/hosts <<'EOF' || [[ $? == 1 ]]
+# note: i put this into bind for good measure
+10.173.8.2 nn.b8.nz
+# this is just here to avoid mainlog errors, however, it doesnt seem to work
+# todo: look into it more. nsswitch.conf? cached result? i dunno
+# list matching forced to fail: failed to find host name for 10.173.8.1
+10.173.8.1 defaultnn.b8.nz
+EOF
+
+ rsync -ra --delete /etc/exim4/ /etc/myexim4
+ cat >>/etc/myexim4/update-exim4.conf.conf <<'EOF'
+dc_eximconfig_configtype='smarthost'
+dc_smarthost='nn.b8.nz'
+EOF
+ update-exim4.conf -d /etc/myexim4
+
;;
- # * bk
+ # ** bk
## we use this host to monitor MAIL_HOST and host a mail server for someone
bk)
EOF
roundcube-setup
+ nextcloud-setup
;;
- # * not MAIL_HOST and not bk
+ # ** not MAIL_HOST and not bk
*)
+ for unit in exim4 spamassassin; do
+ f=/etc/systemd/system/$unit.service.d/nn.conf
+ rm -fv $f
+ done
+
# remove mail. uses 2 lines to properly remove whitespace
sed -ri -f - /etc/hosts <<'EOF'
s#^(127\.0\.1\.1 .*) +mail\.iankelling\.org$#\1#
m systemctl disable mailclean.timer &>/dev/null ||:
m systemctl stop mailclean.timer &>/dev/null ||:
- m systemctl disable $vpn_ser@mail
- m systemctl stop $vpn_ser@mail
+ m systemctl disable openvpn-client-mail@mail
+ m systemctl stop openvpn-client-mail@mail
rm -fv /etc/exim4/conf.d/main/000_localmacros # old filename
- cat >>/etc/exim4/update-exim4.conf.conf <<EOF
+
+ cat >>/etc/exim4/update-exim4.conf.conf <<'EOF'
dc_eximconfig_configtype='smarthost'
dc_smarthost='$smarthost'
-# The manpage incorrectly states this will do header rewriting, but
-# that only happens if we have dc_hide_mailname is set.
-dc_readhost='iankelling.org'
EOF
hostname -f >/etc/mailname
- # This ends up at alerts mailbox on MAIL_HOST
- sed -i --follow-symlinks -f - /etc/aliases <<EOF
-\$a root: root@mail.iankelling.org
-/^root:/d
-EOF
cat >>/etc/exim4/update-exim4.conf.conf <<EOF
+# The manpage incorrectly states this will do header rewriting, but
+# that only happens if we have dc_hide_mailname is set.
+dc_readhost='iankelling.org'
# Only used in case of bounces.
dc_localdelivery='maildir_home'
EOF
;;
esac
+
+
# * spool dir setup
# ** bind mount setup
if ! grep -Fx "/nocow/exim4 /var/spool/exim4 none bind 0 0" /etc/fstab; then
echo "/nocow/exim4 /var/spool/exim4 none bind 0 0" >>/etc/fstab
fi
+ reload=false
f=/etc/systemd/system/exim4.service.d/override.conf
- if [[ ! -s $f ]]; then
- # without this, we get these kind of errors in paniclog on shutdown:
- # 2020-03-12 07:25:31.965 [32678] 1jCLxz-0008V4-V9 Failed to create spool file /var/spool/exim4//input//1jCLxz-0008V4-V9-D: Permission denied
- mkdir -p /etc/systemd/system/exim4.service.d
+ if [[ ! -s $f || $(stat -c%s $f) != 220 ]]; then
+ reload=true
+ mkdir -p ${f%/*}
cat >$f <<'EOF'
[Unit]
+# without this on exim, we get these kind of errors in paniclog on shutdown:
+# Failed to create spool file /var/spool/exim4//input//1jCLxz-0008V4-V9-D: Permission denied
After=local-fs.target
+After=network.target
EOF
m systemctl daemon-reload
fi
# * misc
-sudo -u $u mkdir -p /home/$u/.cache
+m sudo -u $u mkdir -p /home/$u/.cache
set -- /m/mucache /home/$u/.cache/mu /m/.mu /home/$u/.mu
while (($#)); do
target=$1