script_dir=$(dirname $(readlink -f "$BASH_SOURCE"))
-# todo: finish figuring out fai / distro-setup
-# initial fstab / subvol setup.
+# note q is owned by root:1000
+# note p/m is owned 1000:1000 and chmod 700
+mountpoints=(/a)
+private_mountpoints=(/q /m)
+rsync_mountpoint=/q
conf_only=false
dry_run=false # mostly for testing
# btrbk -l debug -v dryrun
EOF
-# note q is owned by root:1000
-# note p is owned 1000:1000 and chmod 700
-mountpoints=(/a)
-qmnt=/q
-if awk '{print $2}' /etc/fstab | grep -xF $qmnt &>/dev/null; then
- mountpoints+=($qmnt)
-fi
+for mp in ${private_mountpoints[@]}; do # private mountpoints
+ if awk '{print $2}' /etc/fstab | grep -xF $mp &>/dev/null; then
+ mountpoints+=($mp)
+ fi
+done
# if our mountpoints are from stale snapshots,
# it doesn't make sense to do a backup.
m btrbk $progress_arg $resume_arg run
fi
-# if we have /p, rsync to targets without /p
-if mountpoint /p >/dev/null; then
+# if we have it, sync to systems which don't
+if mountpoint $rsync_mountpoint >/dev/null; then
for tg in ${targets[@]}; do
case $tg in
tp|li|lk)
# limitations under the License.
# usage: $0 SUBVOL_MOUNTPOINT...
-# if latest subvols $@ are not mounted, exit 1, print message, and touch /nocow/btrfs-stale/$subvol
+#
+# In git, this is not not executable because it's meant to be installed
+# using ./install-my-scripts
+#
+# If latest subvols $@ are not mounted, exit 1, print message, and touch
+# /nocow/btrfs-stale/$subvol
+#
+# Either SUBVOL_MOUNTPOINT is a snapshot of the latest, or
+# the latest snapshot is snapshot of SUBVOL_MOUNTPOINT.
[[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
for d; do
vol=${d##*/}
cd /mnt/root/btrbk
- snaps=($vol.20*)
+ snaps=($vol.20*) # Assumes we are in the 21st century.
if [[ ! $snaps ]]; then
# no snapshots yet
continue
done | sort -r | head -n 1 | awk '{print $2}'
)
if [[ ! $last_snap ]]; then
- echo "$d stale"
+ # should not happen.
+ echo "$0: error: could not find latest snapshot for $d among ${snaps[@]}"
ret=1
continue
fi
stale=true
- if btrfs sub show $d 2>/dev/null | sed '0,/^\t*Snapshot(s):/d;s/^\s*//' | \
+ # check that $d has $last_snap as a snapshot,
+ # or else $d is a snapshot of $last_snap. In the second
+ # case, we use a uuid comparison, which if I remember from the
+ # docs, is a bit more robust, perhaps to renames.
+ if btrfs sub show $d 2>/dev/null | sed '0,/^\s*Snapshot(s):/d;s/^\s*//' | \
grep -xF btrbk/$last_snap &>/dev/null; then
stale=false
else
fi
done
exit $ret
-
ser restart systemd-udev-trigger
fi
-# work desktop doesnt need gpg stuff, but it doesnt hurt
-s dd of=/etc/profile.d/environment.sh <<'EOF'
-# IAN: EDIT THIS FROM /a/bin/distro-setup/distro-begin
-export ACME_TINY_WRAPPER_CERT_DIR=/p/c/machine_specific/$HOSTNAME/webservercerts
-
-if [ -f $HOME/path_add-function ]; then
- . $HOME/path_add-function
- path_add /usr/sbin /usr/local/sbin /sbin
- path_add /a/exe /a/opt/bin $HOME/.cabal/bin
-
- if [ -r /etc/alternatives/java_sdk ]; then
- export JAVA_HOME=/etc/alternatives/java_sdk
- path_add /etc/alternatives/java_sdk
- fi
-fi
-
-export EDITOR="emacsclient"
-# this makes emacsclient file/-c start a server instance if none is running,
-# instead of some alternate editor logic
-export ALTERNATE_EDITOR=""
-
-# ubuntu starts gpg agent automatically with /etc/X11/Xsession.d/90gpg-agent.
-# fedora doesn't, which left me to figure this out, and google was no help.
-# fedora documentation is often quite bad :(
-# This is mostly copied from that file.
-# Main difference is that we eval the result of starting gpg-agent,
-# while that file executes it through xsession specific var.
-# Also make sourcing the pidfile make more sense.
-# End result should be the same afaik.
-# for gpg-agent to work when calling gpg from the command line,
-# we need an environment variable that is setup via the eval.
-# which is why we do this upon login, so it can propogate
-# It is also written to the file $HOME/.gnupg/gpg-agent-info-$(hostname)
-# I'm not aware if that is ever used, but just fyi.
-# I also added the bit about xmessaging the stderr,
-# because I'd like to know if the command fails
-if [ -f /etc/fedora-release ]; then
- : ${GNUPGHOME=$HOME/.gnupg}
-
- GPGAGENT=/usr/bin/gpg-agent
- PID_FILE="$GNUPGHOME/gpg-agent-info-$(hostname)"
-
- if ! $GPGAGENT 2>/dev/null; then
- temp="$(mktemp)"
- eval "$($GPGAGENT --homedir /p/do-not-delete --daemon --sh --write-env-file=$PID_FILE 2>$temp)"
- temperr="$(<"$temp")"
- [ -n "$temperr" ] && xmessage "gpg-agent stderr: $temperr"
- elif [ -r "$PID_FILE" ]; then
- . "$PID_FILE"
- export GPG_AGENT_INFO
- fi
-fi
-
-# ubuntu has 002, debian has 022.
-# from what I've read, benefit of 002 makes shared groups read/write.
-# Security concern is where some unixes put everyone in a same group,
-# so if you copy files there with exact perms, that is probably not
-# what you want. I don't use a system like that, and I don't really care
-# either way, but I'd prefer
-# being able to sync file perms with ubuntu systems at work,
-# and it's easier to change the debian one.
-
-umask 002
-EOF
-
if isdeb; then
# I\'ve had problems with postfix on debian:
distro=$(distro-name)
pending_reboot=false
+sed="sed --follow-symlinks"
# template
case $distro in
# mutagen for pithos
simple_packages+=(
apache2
+ apache2-doc
+ apt-doc
+ aptitude-doc-en
+ bash-doc
+ binutils-doc
bwm-ng
chromium
+ cpio-doc
cron
debconf-doc
duplicity
fdupes
feh
filelight
+ gawk-doc
gcc-doc
gdb
+ gdb-doc
+ git-doc
gitk
+ glibc-doc
goaccess
gnome-screenshot
i3lock
+ iproute2-doc
jq
linux-doc
locate
+ make-doc
manpages
manpages-dev
meld
offlineimap
p7zip
paprefs
+ parted-doc
pavucontrol
pdfgrep
+ perl-doc
pianobar
pidgin
+ python3-doc
python3-mutagen
reportbug
+ sqlite3-doc
squashfs-tools
swh-plugins
+ tar-doc
tcpdump
transmission-remote-gtk
vlc
+ whois
)
+ spa $(apt-cache search ruby[.0-9]+-doc| awk '{print $1}')
;;
esac
-
########### begin section including li ################
#$src/phab-setup
pi-nostart mumble-server
- s sed -ri "s/^ *(serverpassword=).*/\1$(< /a/bin/bash_unpublished/mumble_pass)/" /etc/mumble-server.ini
+ s $sed -ri "s/^ *(serverpassword=).*/\1$(< /a/bin/bash_unpublished/mumble_pass)/" /etc/mumble-server.ini
sgo mumble-server
vpn-server-setup -d
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=/sbin/iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 25 -j DNAT --to-destination 10.8.0.4:25
-ExecStop=/sbin/iptables -t nat -D PREROUTING -i eth0 -p tcp -m tcp --dport 25 -j DNAT --to-destination 10.8.0.4:25
+ExecStart=/a/bin/distro-setup/vpn-mail-forward start
+ExecStop=/a/bin/distro-setup/vpn-mail-forward stop
[Install]
WantedBy=openvpn.service
ser enable vpnmail.service
acme-tiny-wrapper mail.iankelling.org
sgo openvpn
- tu /etc/hosts <<<"mail.iankelling.org 10.8.0.4"
+ tu /etc/hosts <<<"10.8.0.4 mail.iankelling.org"
echo "$0: $(date): ending now)"
fi
ser enable mailroute
if [[ $HOSTNAME == treetowl ]]; then
- # note, this will need to be changed when the mail host changes
+ # note, this will need to be changed when the mail/contacts host changes
sgo openvpn-client@mail
+ /a/bin/distro-setup/radicale-setup.sh
fi
## android studio setup
# on dekstop, top right, actions, device id
# after adding, notification will appear on desktop to confirm
#
- # add folder to sync phone, notification will appear on desktop
- # to set folder location.
+ # syncing folder. from phone to desktop: select desktop in the
+ # folder on phone's sync options, notification will appear in
+ # desktop's web ui within a minute. For the reverse, the
+ # notification will appear in android's notifications, you have to
+ # swipe down and tap it to add the folder. It won't appear in the
+ # syncthing ui, which would be intuitive, but don't wait for it
+ # there.
#
# On phone, set settings to run syncthing all the time, and
# show no notification.
ser disable transmission-daemon
sgo transmission-daemon-nn
;;
- # todo: others unknown
-esac
+ # todo: others unknown
+ esac
fi
# adapted from /var/lib/dpkg/info/transmission-daemon.postinst
p = '/etc/transmission-daemon/settings.json'
puts JSON.parse(File.read(p))["rpc-password"]
EOF
-)
+ )
for f in /home/*; do
d=$f/.config/transmission-remote-gtk
bridge-utils dnsmasq qemu bind-tools
# otherwise we get error about accessing kvm module.
# seems like there might be a better way, but google was a bit vague.
- s sed -ri --follow-symlinks '/^ *user *=/d' /etc/libvirt/qemu.conf
+ s $sed -ri '/^ *user *=/d' /etc/libvirt/qemu.conf
echo 'user = "root"' | s tee -a /etc/libvirt/qemu.conf
# https://bbs.archlinux.org/viewtopic.php?id=206206
# # this should prolly go in the wiki
# other distros unknown
esac
-case $distro in
- debian)
- # if [[ `debian-archive` == testing ]]; then
- # # has no unstable dependencies
- # pi bitcoind/unstable
- # fi
- s cp /a/opt/bitcoin/contrib/init/bitcoind.service /etc/systemd/system
- ser daemon-reload
-
- dir=/nocow/.bitcoin
- s mkdir -p $dir
- s chown -R bitcoin:bitcoin $dir
- dir=/etc/bitcoin
- s mkdir -p $dir
- s chown -R root:bitcoin $dir
- s chmod 750 $dir
- f=$dir/bitcoin.conf
-
- # pruning decreases the bitcoin dir to 2 gb, keeps
- # just the recent blocks. can't do a few things like
- # import a wallet dump.
- # pruning works, but people had to do
- # some manual stuff in joinmarket. I dun need the
- # disk space, so not bothering yet, maybe in a year or so.
- # https://github.com/JoinMarket-Org/joinmarket/issues/431
- #https://bitcoin.org/en/release/v0.12.0#wallet-pruning
- #prune=550
-
- s dd of=$f <<EOF
-rpcbind=127.0.0.1
+if [[ $HOSTNAME == treetowl ]]; then
+ case $distro in
+ debian)
+ if [[ `debian-archive` == testing ]]; then
+ # has no unstable dependencies
+ pi bitcoind/unstable
+ src=/a/opt/bitcoin/contrib/init/bitcoind.service
+ s cp $src /etc/systemd/system
+ p=/etc/bitcoin/bitcoin
+ dst=/etc/systemd/system/bitcoinjm.service
+ # jm for joinmarket
+ $sed -r "/^\s*ExecStart/s,${p}.conf,${p}jm.conf," $src \
+ >/etc/systemd/system/bitcoinjm.service
+
+ d=jm; jm=d # being clever for succinctness
+ for s in d jm; do
+ s $sed -ri "/^\s*\[Unit\]/a Conflicts=bitcoin${!s}.service" \
+ /etc/systemd/system/bitcoin${s}.service
+ done
+
+ ser daemon-reload
+
+ dir=/nocow/.bitcoin
+ s mkdir -p $dir
+ s chown -R bitcoin:bitcoin $dir
+ dir=/etc/bitcoin
+ s mkdir -p $dir
+ s chown -R root:bitcoin $dir
+ s chmod 750 $dir
+
+ # pruning decreases the bitcoin dir to 2 gb, keeps
+ # just the recent blocks. can\'t do a few things like
+ # import a wallet dump.
+ # pruning works, but people had to do
+ # some manual stuff in joinmarket. I dun need the
+ # disk space, so not bothering yet, maybe in a year or so.
+ # https://github.com/JoinMarket-Org/joinmarket/issues/431
+ #https://bitcoin.org/en/release/v0.12.0#wallet-pruning
+ #prune=550
+
+ f=$dir/bitcoin.conf
+ s dd of=$f <<EOF
server=1
rpcpassword=$(openssl rand -base64 32)
rpcuser=$(openssl rand -base64 32)
+EOF
+
+ f2=$dir/bitcoinjm.conf
+ s cp $f $f2
+ s tee -a $f2 >/dev/null <<EOF
# Joinmarket
walletnotify=curl -sI --connect-timeout 1 http://localhost:62602/walletnotify?%s
alertnotify=curl -sI --connect-timeout 1 http://localhost:62602/alertnotify?%s
+wallet=joinmarket.dat
EOF
- ;;
- # other distros unknown
-esac
-if [[ $HOSTNAME == treetowl ]]; then
+ # dunno about sharing a wallet between multiple instances
+ # manually did, wallet.dat symlinked in /nocow/.bitcoin
+ sgo bitcoind
+ fi
+ ;;
+ # other distros unknown
+ esac
pi libsodium-dev python-pip
cd /a/opt/joinmarket
# using develop branch, as it seems to be mostly bug fixes,
# note: python3 does not work.
# has seg fault error due to some bug, but it still works
pip install -r requirements.txt || [[ $? == 139 ]]
- # we need bitcoin.conf in the data dir according to
- # https://github.com/JoinMarket-Org/joinmarket/wiki/Running-JoinMarket-with-Bitcoin-Core-full-node
- # following the example .service script, I don\'t have it there,
- # and I generate it, so lets just symlink it.
- sudo -u bitcoin ln -sf /etc/bitcoin/bitcoin.conf /nocow/.bitcoin
-
- # one time, manually did python wallet-tool.py generate.
- # The "wallet" is just a key which deterministically generates addresses.
- # One time: move the wallet, then link to it.
- # ln -s /p/joinmarket/wallet.json wallets
- #
- # see wallet addresses via:
- # python wallet-tool.py wallet.json
- # send to the first 3 mixing depth 0 addresses.
- # depths are like "identities", to separate out association with
- # each other. the big hash in that output is the depth/branch id,
- # ignore it afaik.
- #
- # after sending btc to wallet from a 3rd party service, check that
- # at least 20% of utxo of each transaction was sent to you,
- # btc listtransactions 10 0 true
- # btc getrawtransaction TXID 1
- #
- # to view status, do
- # python wallet-tool.py wallet.json history
- #
- # to help make other people,
- # python yield-generator-basic.py wallet.json
+ # note, the target must exist ahead of time, or bitcoin
+ # just overwrites the link, and it's not happy with an empty file,
+ # so we have to create the wallet, then move and link it.
+ s lnf -T /q/bitcoin/wallet.dat /nocow/.bitcoin/wallet.dat
+ s lnf -T /q/bitcoin/joinmarket.dat /nocow/.bitcoin/joinmarket.dat
+ # not technically needed, but seems cleaner not to have
+ # symlinks be root owned unlike everything else
+ s chown -h bitcoin:bitcoin /nocow/.bitcoin/*
for var in rpcuser rpcpassword; do
u="$(s sed -rn "s/^$var=(.*)/\1/p" /etc/bitcoin/bitcoin.conf)"
done
sed -ri "s/^\s*(blockchain_source\s*=).*/\1 bitcoin-rpc/" joinmarket.cfg
- # dunno about sharing a wallet between multiple instances
- # manually did, wallet.dat symlinked in /nocow/.bitcoin
- #sgo bitcoind
fi
# run "control userpasswords2", turn on automatic login.
# note: when changing devices, I just undefine, the create the vm again.
- if [[ -e /a/images/win10.qcow2 ]]; then
+ if [[ -e /nocow/user/vms/win10.qcow2 ]]; then
s virt-install --noautoconsole --graphics spice,listen=0.0.0.0 \
--disk=/a/images/win10.qcow2,bus=virtio --vcpus 2 -r 4096 -w bridge=br0 \
-n win10 --import --os-variant $variant --cpu host-model-only
s virsh destroy win10
fi
- if [[ -e /a/images/win7.qcow2 ]]; then
+ if [[ -e /nocow/user/vms/win7.qcow2 ]]; then
# this one hasn\'t had the virtio fix done yet.
s virt-install --noautoconsole --graphics spice,listen=0.0.0.0 \
--disk=/a/images/win7.qcow2 --vcpus 2 -r 4096 -w bridge=br0 \
# https://www.namecheap.com/support/knowledgebase/article.aspx/583/11/how-do-i-configure-ddclient
ip=`curl -s4 echoip.com`
-curl -sS "https://dynamicdns.park-your-domain.com/update?host=@&domain=$HOME_DOMAIN&password=$(cat /p/dynamic-ip-pass)&ip=$ip" > /dev/null | /a/exe/log-once dynamic-ip
+curl -sS "https://dynamicdns.park-your-domain.com/update?host=@&domain=$HOME_DOMAIN&password=$(cat /p/dynamic-ip-pass)&ip=$ip" > /dev/null
# an alternative, putting my ip on some known server,
# allows ssh to home if I can access that server:
# 12 is to effectively disable the middle click button
xinput --set-button-map "$device_id" 1 12 3 10 11 6 7
fi
- /a/bin/bash_unpublished/gpg
+ . /a/bin/bash_unpublished/duplicity-gpg-agent-setup
;;
frodo*)
;;
x="$(readlink -f "$BASH_SOURCE")"; cd ${x%/*}
-echo install mount-latest-subvol check-subvol-stale /usr/local/bin
-install mount-latest-subvol check-subvol-stale /usr/local/bin
+e() { echo "$*"; "$@"; }
+
+# scripts that would interfere with unmounting /a, put them elsewhere
+e install mail-cert-cron mount-latest-subvol check-subvol-stale /usr/local/bin
set -eE -o pipefail
trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
+[[ $EUID == 0 ]] || exec sudo "$BASH_SOURCE" "$@"
+
source /a/bin/bash_unpublished/source-semi-priv
if [[ $HOSTNAME == $MAIL_HOST ]]; then
local_mx=mail.iankelling.org
- rsync_common="s rsync -ogt --chown=root:Debian-exim --chmod=640 root@li:/p/c/machine_specific/li/webservercerts/$local_mx-"
+ rsync_common="rsync -ogt --chown=root:Debian-exim --chmod=640 root@li:/p/c/machine_specific/li/webservercerts/$local_mx-"
${rsync_common}chained.pem /etc/exim4/exim.crt
${rsync_common}domain.key /etc/exim4/exim.key
fi
# exim is replying to. I don't know why.
#iptables -t mangle -A OUTPUT -m owner --uid-owner Debian-exim -j MARK --set-mark 0x1
+# match source or dest port. when we send to 25, it picks a random high port as
+# the source.
-e iptables -t mangle $iptables_op OUTPUT -m tcp -p tcp -m multiport --sports 25 -j MARK --set-mark 0x1
+for port in 25 993; do # smtp and imap with ssl.
+e iptables -t mangle $iptables_op OUTPUT -m tcp -p tcp -m multiport --ports $port -j MARK --set-mark 0x1
+done
e iptables -t nat $iptables_op POSTROUTING -o tun0 -m mark --mark 0x1 -j SNAT --to-source 10.8.0.4
e ip rule $ip_op fwmark 1 table 1
# note, this rule does not persist when the tun interface is deleted
# See the License for the specific language governing permissions and
# limitations under the License.
+set -eE -o pipefail
+trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
+
+####### begin perstent password instructions ######
+# # exim passwords:
+# # for hosts which have all private files I just use the same user
+# # for other hosts, each one get's their own password.
+# # for generating secure pass, and storing for server too:
+# # user=USUALLY_SAME_AS_HOSTNAME
+# user=li
+# f=$(mktemp)
+# apg -m 50 -x 70 -n 1 -a 1 -M CLN >$f
+# s sed -i "/^$user:/d" /p/c/filesystem/etc/exim4/passwd
+# echo "$user:$(mkpasswd -m sha-512 -s <$f)" >>/p/c/filesystem/etc/exim4/passwd
+# echo "mail.iankelling.org:$user:$(<$f)" >> /p/c/machine_specific/$user/filesystem/etc/mailpass
+# # then run this script, or part of it which uses /etc/mailpass
+
+# # dovecot password, i just need 1 as I'm the only user
+# mkdir /p/c/filesystem/etc/dovecot
+# echo "ian:$(doveadm pw -s ssha256)::::::" >/p/c/filesystem/etc/dovecot/users
+# conflink
+
+
+
+# # for ad-hoc testing of some random new host sending mail:
+# user=li # client host username & hostname
+# f=$(mktemp)
+# apg -m 50 -x 70 -n 1 -a 1 -M CLN >$f
+# s sed -i "/^$user:/d" /etc/exim4/passwd
+# echo "$user:$(mkpasswd -m sha-512 -s <$f)" | s tee -a /etc/exim4/passwd
+# echo "mail.iankelling.org:$user:$(<$f)" | ssh root@$user dd of=/etc/exim4/passwd.client
+####### end perstent password instructions ######
+
+
+####### begin persistent dkim/dns instructions #########
+# # Remove 1 level of comments in this section, set the domain var
+# # for the domain you are setting up, then run this and copy dns settings
+# # into dns.
+# domain=iankelling.org
+# c /p/c/filesystem/etc/exim4
+# # this has several bugs addressed in comments, but it was helpful
+# # https://debian-administration.org/article/718/DKIM-signing_outgoing_mail_with_exim4
+
+# openssl genrsa -out $domain-private.pem 2048 -outform PEM
+# openssl rsa -in $domain-private.pem -out $domain.pem -pubout -outform PEM
+# # selector is needed for having multiple keys for one domain.
+# # I dun do that, so just use a static one: li
+# echo "txt record name: li._domainkey.$domain"
+# # Debadmin page does not have v=, fastmail does, and this
+# # says it's recommended in 3.6.1, default is DKIM1 anyways.
+# # https://www.ietf.org/rfc/rfc6376.txt
+# # Join and print all but first and last line.
+# # last line: swap hold & pattern, remove newlines, print.
+# # lines 2+: append to hold space
+# echo "txt record contents:"
+# echo "v=DKIM1; k=rsa; p=$(sed -n '${x;s/\n//gp};2,$H' $domain.pem)"
+# chmod 644 $domain.pem
+# chmod 640 $domain-private.pem
+# # in conflink, we chown these to group debian
+# conflink
+# # selector was also put into /etc/exim4/conf.d/main/000_localmacros,
+# # via the mail-setup scripts
+
+# # 2017-02 dmarc policies:
+# # yahoo: p=reject, hotmail: p=none, gmail: p=none, fastmail none for legacy reasons
+# # gmail will be changing to p=reject, which is expected to cause problems
+# # with a few old mailing lists, copying theirs for now.
+# echo "dmarc dns, name: _dmarc value: v=DMARC1; p=none; rua=mailto:mailauth-reports@$domain"
+
+# # 2017-02 spf policies:
+# # google ~all, hotmail -all, yahoo: ?all, fastmail ?all
+# # i include fastmail's settings, per their instructions,
+# # and follow their policy. In mail in a box, or similar instructions,
+# # I've seen recommended to not use a restrictive policy.
+# echo "spf dns: name is empty, value: v=spf1 a include:spf.messagingengine.com ?all"
+
+# # to check if dns has updated, you do
+# host -a mesmtp._domainkey.$domain
+
+# # mx records,
+# # setting it to iankelling.org would work the same, but this
+# # is more flexible, I could change where mail.iankelling.org pointed.
+# cat <<'EOF'
+# mx records, 2 records each, for * and empty domain
+# pri 10 mail.iankelling.org
+# pri 20 in1-smtp.messagingengine.com
+# pri 30 in2-smtp.messagingengine.com
+# EOF
+####### end persistent dkim instructions #########
+
# misc exim notes:
# useful exim docs:
#exim4 -bt ian@localhost
-set -eE -o pipefail
-trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
-
type=$1
postfix() { [[ $type == postfix ]]; }
exim() { [[ $type == exim4 ]]; }
local_mx=mail.iankelling.org
-if [[ $HOSTNAME == $MAIL_HOST ]]; then
- host=mail.messagingengine.com
- relayhost="[$host]:587" # postfix
- smarthost="$host::587" # exim
-else
- host=$local_mx
- relayhost="[$host]:25" # postfix
- smarthost="$host::25" # exim
-fi
+
+host=$local_mx
+relayhost="[$host]:25" # postfix
+smarthost="$host::25" # exim
+
+
+# this was for when I used the exim config type
+# "mail sent by smarthost; received via SMTP or fetchmail"
+# if [[ $HOSTNAME == $MAIL_HOST ]]; then
+# host=mail.messagingengine.com
+# relayhost="[$host]:587" # postfix
+# smarthost="$host::587" # exim
+# fi
forward=ian@$local_mx
s service postfix reload
else # exim. has debian specific stuff for now
- # debconf settings will not work if packages are already installed,
- # such as on vps images.
- pu exim4-daemon-light exim4-daemon-heavy exim4-config exim4-base exim4
+
# wording of question from dpkg-reconfigure exim4-config
# 1. internet site; mail is sent and received directly using SMTP
# 4. local delivery only; not on a network
# 5. no configuration at this time
#
- # only the one receiving host needs option 2, the rest can do option
- # 1, but that host might change, so we pick option 2 and later don't
- # set it up to receive anything, because it has nothing in it's
- # receiving password file and we make that mandatory. Also, only
- # receiving host needs dc_other_hostnames and beyond, but no harm.
-
- # note, another related setting is /etc/mailname, which
- # is set to be $HOSTNAME.lan on stretch. this may need to be
- # setup on other distros.
-
- # setting local_interfaces to empty listens on all interfaces.
- # default is 127.0.0.1 ; ::1, so only listen to lo interface.
+ # Note, I have used option 2 in the past for receiving mail
+ # from lan hosts, sending external mail via another smtp server.
+ #
+ # Note, other than configtype, we could set all the options in
+ # both types of configs without harm, they would either be
+ # ignored or be disabled by other settings, but the default
+ # local_interfaces definitely makes things more secure.
+
+ # most of these settings get translated into settings
+ # in /etc/exim4/update-exim4.conf.conf
+ # mailname setting sets /etc/mailname
+
s debconf-set-selections <<EOF
-exim4-config exim4/dc_eximconfig_configtype select mail sent by smarthost; received via SMTP or fetchmail
-exim4-config exim4/dc_smarthost string $smarthost
exim4-config exim4/use_split_config boolean true
-exim4-config exim4/dc_other_hostnames string mail.iankelling.org
+EOF
+
+ source /a/bin/bash_unpublished/source-semi-priv
+ exim_main_dir=/etc/exim4/conf.d/main
+ s mkdir -p exim_main_dir
+ if [[ $HOSTNAME == $MAIL_HOST ]]; then
+ # afaik, these will get ignored, routing to my own machine, but rm
+ # them to make me feel better.
+ s rm -f ~/.forward /root/.forward
+
+ s debconf-set-selections <<EOF
+# Mail Server configuration
+# -------------------------
+
+# Please select the mail server configuration type that best meets your needs.
+
+# Systems with dynamic IP addresses, including dialup systems, should generally be
+# configured to send outgoing mail to another machine, called a 'smarthost' for
+# delivery because many receiving systems on the Internet block incoming mail from
+# dynamic IP addresses as spam protection.
+
+# A system with a dynamic IP address can receive its own mail, or local delivery can be
+# disabled entirely (except mail for root and postmaster).
+
+# 1. internet site; mail is sent and received directly using SMTP
+# 2. mail sent by smarthost; received via SMTP or fetchmail
+# 3. mail sent by smarthost; no local mail
+# 4. local delivery only; not on a network
+# 5. no configuration at this time
+
+# General type of mail configuration: 1
+exim4-config exim4/dc_eximconfig_configtype select internet site; mail is sent and received directly using SMTP
+
+
+
+# The 'mail name' is the domain name used to 'qualify' mail addresses without a domain
+# name.
+
+# This name will also be used by other programs. It should be the single, fully
+# qualified domain name (FQDN).
+
+# Thus, if a mail address on the local host is foo@example.org, the correct value for
+# this option would be example.org.
+
+# This name won't appear on From: lines of outgoing messages if rewriting is enabled.
+
+# System mail name:
+exim4-config exim4/mailname string li.iankelling.org
+
+
+
+
+# Please enter a semicolon-separated list of recipient domains for which this machine
+# should consider itself the final destination. These domains are commonly called
+# 'local domains'. The local hostname (treetowl.lan) and 'localhost' are always added
+# to the list given here.
+
+# By default all local domains will be treated identically. If both a.example and
+# b.example are local domains, acc@a.example and acc@b.example will be delivered to the
+# same final destination. If different domain names should be treated differently, it
+# is necessary to edit the config files afterwards.
+
+# Other destinations for which mail is accepted:
+# iank.bid is for testing
+# mail.iankelling.org is for machines i own
+exim4-config exim4/dc_other_hostnames string mail.iankelling.org;iank.bid;iankelling.org;zroe.org
+
+
+
+
+# Please enter a semicolon-separated list of IP addresses. The Exim SMTP listener
+# daemon will listen on all IP addresses listed here.
+
+# An empty value will cause Exim to listen for connections on all available network
+# interfaces.
+
+# If this system only receives mail directly from local services (and not from other
+# hosts), it is suggested to prohibit external connections to the local Exim daemon.
+# Such services include e-mail programs (MUAs) which talk to localhost only as well as
+# fetchmail. External connections are impossible when 127.0.0.1 is entered here, as
+# this will disable listening on public network interfaces.
+
+# IP-addresses to listen on for incoming SMTP connections:
exim4-config exim4/dc_local_interfaces string
+
+
+
+
+# Mail for the 'postmaster', 'root', and other system accounts needs to be redirected
+# to the user account of the actual system administrator.
+
+# If this value is left empty, such mail will be saved in /var/mail/mail, which is not
+# recommended.
+
+# Note that postmaster's mail should be read on the system to which it is directed,
+# rather than being forwarded elsewhere, so (at least one of) the users listed here
+# should not redirect their mail off this machine. A 'real-' prefix can be used to
+# force local delivery.
+
+# Multiple user names need to be separated by spaces.
+
+# Root and postmaster mail recipient:
exim4-config exim4/dc_postmaster string ian
+
+
+
+# Exim is able to store locally delivered email in different formats. The most commonly
+# used ones are mbox and Maildir. mbox uses a single file for the complete mail folder
+# stored in /var/mail/. With Maildir format every single message is stored in a
+# separate file in ~/Maildir/.
+
+# Please note that most mail tools in Debian expect the local delivery method to be
+# mbox in their default.
+
+# 1. mbox format in /var/mail/ 2. Maildir format in home directory
+
+# Delivery method for local mail: 2
exim4-config exim4/dc_localdelivery select Maildir format in home directory
EOF
+ # MAIN_HARDCODE_PRIMARY_HOSTNAME might mess up the
+ # smarthost config type, not sure. all other settings
+ # would be unused in that config type.
+ s dd of=$exim_main_dir/000_localmacros 2>/dev/null <<'EOF'
+MAIN_TLS_ENABLE = true
+
+DKIM_CANON = relaxed
+DKIM_SELECTOR = li
+
+# from comments in
+# https://debian-administration.org/article/718/DKIM-signing_outgoing_mail_with_exim4
+
+# The file is based on the outgoing domain-name in the from-header.
+DKIM_DOMAIN = ${lc:${domain:$h_from:}}
+# sign if key exists
+DKIM_PRIVATE_KEY= ${if exists{/etc/exim4/${dkim_domain}-private.pem} {/etc/exim4/${dkim_domain}-private.pem}}
+
+
+# failing message on mail-tester.com:
+# We check if there is a server (A Record) behind your hostname treetowl.
+# You may want to publish a DNS record (A type) for the hostname treetowl or use a different hostname in your mail software
+# https://serverfault.com/questions/46545/how-do-i-change-exim4s-primary-hostname-on-a-debian-box
+# and this one seemed appropriate from grepping config
+MAIN_HARDCODE_PRIMARY_HOSTNAME = li.iankelling.org
+
+# normally empty, I set this so I can set the envelope address
+# when doing mail redelivery to invoke filters
+MAIN_TRUSTED_GROUPS = ian
+
+# disabled, didn't finished configuring
+#LOCAL_DELIVERY = dovecot_lmtp
+
+CHECK_RCPT_LOCAL_ACL_FILE = /etc/exim4/rcpt_local_acl
+EOF
+
+
+ s dd of=/etc/systemd/system/offlineimapsync.timer <<'EOF'
+[Unit]
+Description=Run offlineimap-sync once every 5 mins
+
+[Timer]
+OnCalendar=*:0/5
+
+[Install]
+WantedBy=timers.target
+EOF
+
+ s dd of=/etc/systemd/system/offlineimapsync.service <<'EOF'
+[Unit]
+Description=Offlineimap sync
+After=multi-user.target
+
+[Service]
+Type=oneshot
+ExecStart=/a/bin/log-quiet/sysd-mail-once offlineimap-sync /a/bin/distro-setup/offlineimap-sync
+EOF
+ s systemctl daemon-reload
+ s systemctl enable offlineimapsync.timer
+ s systemctl start offlineimapsync.timer
+
+ else # $HOSTNAME != $MAIL_HOST
+ s systemctl disable offlineimapsync.timer &>/dev/null ||:
+ s systemctl stop offlineimapsync.timer &>/dev/null ||:
+ #
+ #
+ # would only exist because I wrote it i the previous condition,
+ # it's not part of exim
+ s rm -f $exim_main_dir/000_localmacros
+ s debconf-set-selections <<EOF
+exim4-config exim4/dc_eximconfig_configtype select mail sent by smarthost; no local mail
+exim4-config exim4/dc_smarthost string $smarthost
+EOF
+ fi
+
+ # if we already have it installed, need to reconfigure, without being prompted
+ if ! dpkg -s exim4-config &>/dev/null; then
+ s dpkg-reconfigure -u -pcritical exim4-config
+ fi
# light version does not have sasl auth support.
- pi exim4-daemon-heavy
+ pi exim4-daemon-heavy spamassassin
+
+ ##### begin spamassassin config
+ ser enable spamassassin
+ # per readme.debian
+ s sed -i '/^\s*CRON\s*=/d' /etc/default/spamassassin
+ s tee -a /etc/default/spamassassin <<<CRON=1
+ # just noticed this in the config file, seems like a good idea.
+ s sed -i '/^\s*NICE\s*=/d' /etc/default/spamassassin
+ s tee -a /etc/default/spamassassin <<<'NICE="--nicelevel 15"'
+
+ s dd of=/etc/systemd/system/spamddnsfix.service <<'EOF'
+[Unit]
+Description=spamd dns bug fix cronjob
+
+[Service]
+Type=oneshot
+ExecStart=/a/bin/distro-setup/spamd-dns-fix
+EOF
+ s dd of=/etc/systemd/system/spamddnsfix.timer <<'EOF'
+[Unit]
+Description=run spamd bug fix script every 10 minutes
+
+[Timer]
+# the script looks back 9 minutes into the journal,
+# it takes a second to run,
+# so lets run every 9 minutes and 10 seconds.
+OnActiveSec=550
+[Install]
+WantedBy=timers.target
+EOF
+ ser daemon-reload
+ sgo spamddnsfix.timer
+ #
+ ##### end spamassassin config
+
+ gitslink # needed to install the execstart files below
s dd of=/etc/systemd/system/mailcert.service <<'EOF'
[Unit]
Description=Mail cert rsync
[Service]
Type=oneshot
-ExecStart=/a/bin/log-quiet/sysd-mail-once mailcert /a/bin/distro-setup/mail-cert-cron
+ExecStart=/usr/local/bin/sysd-mail-once mailcert /usr/local/bin/mail-cert-cron
EOF
s dd of=/etc/systemd/system/mailcert.timer <<'EOF'
[Install]
WantedBy=timers.target
EOF
+ ser daemon-reload
ser start mailcert
sgo mailcert.timer
+
+ # based on a little google and package search, just the dovecot
+ # packages we need instead of dovecot-common.
+ #
+ # dovecot-lmtpd is for exim to deliver to dovecot instead of maildir
+ # directly. The reason to do this is to use dovecot's sieve, which
+ # has extensions that allow it to be almost equivalent to exim's
+ # filter capabilities, some ways probably better, some worse, and
+ # sieve has the benefit of being supported in postfix and
+ # proprietary/weird environments, so there is more examples on the
+ # internet. I was torn about whether to do this or not, meh.
+ pi dovecot-core dovecot-imapd dovecot-sieve dovecot-lmtpd
+
+ # if we changed 90-sieve.conf and removed the active part of the
+ # sieve option, we wouldn't need this, but I'd rather not modify a
+ # default config if not needed. This won't work as a symlink in /a/c
+ # unfortunately.
+ lnf -T sieve/main.sieve ~/.dovecot.sieve
+
+ # we have a few config files which installing exim/dovecot overwrites,
+ # and might as well have this before reading /etc/mailpass,
+ # which this sets up too.
+ conflink
+
+ # begin setup passwd.client
f=/etc/exim4/passwd.client
s rm -f $f
s touch $f
s chmod 640 $f
s chown root:Debian-exim $f
- # generating secure pass, and storing for server too:
- # user=USUALLY_SAME_AS_HOSTNAME
- # f=$(mktemp)
- # apg -m 50 -x 70 -n 1 -a 1 -M CLN >$f
- # echo "$user:$(mkpasswd -m sha-512 -s <$f)" >>/p/c/filesystem/etc/exim4/passwd
- # echo "mail.iankelling.org:$user:$(<$f)" >> /p/c/machine_specific/$user/filesystem/etc/mailpass
- #
- # for ad-hoc testing of some random new host:
- # host=testhost # client host username & hostname
- # f=$(mktemp)
- # apg -m 50 -x 70 -n 1 -a 1 -M CLN >$f
- # s sed "/^host:/d" /etc/exim4/passwd
- # echo "$host:$(mkpasswd -m sha-512 -s <$f)" | s tee -a /etc/exim4/passwd
- # echo "mail.iankelling.org:$host:$(<$f)" | ssh root@$host dd of=/etc/exim4/passwd.client
+ # note: this will go away
s cat /etc/mailpass| while read -r domain port pass; do
# reference: exim4_passwd_client(5)
- printf "%s:%s" "$domain" "$pass" | s tee -a $f >/dev/null
+ printf "%s:%s\n" "$domain" "$pass" | s tee -a $f >/dev/null
done
+ # end setup passwd.client
# 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.
sed -r s/^\\S+:/$b:/ 600_exim4-config_$a | s dd of=$tmp 2>/dev/null
if ! diff -q $tmp $of &>/dev/null; then
s dd if=$tmp of=$of >/dev/null
- ser restart exim4
fi
-fi
+ ser restart exim4
+
+ fi
-# linode image has a root alias. completely useless, remove it.
-sudo sed -i '/^root:/d' /etc/aliases
-s newaliases
+ # linode image has a root alias. completely useless, remove it.
+ sudo sed -i '/^root:/d' /etc/aliases
-# based on http://www.postfix.org/qmgr.8.html and my notes in gnus
-dir=/nocow/$type
-sdir=/var/spool/$type
-if [[ $(readlink -f $sdir) != $dir ]]; then
- ser stop $type
- if [[ ! -e $dir && -d $sdir ]]; then
- s mv $sdir $dir
+ s newaliases
+
+
+ # based on http://www.postfix.org/qmgr.8.html and my notes in gnus
+ dir=/nocow/$type
+ sdir=/var/spool/$type
+ if [[ $(readlink -f $sdir) != $dir ]]; then
+ ser stop $type
+ if [[ ! -e $dir && -d $sdir ]]; then
+ s mv $sdir $dir
+ fi
+ s lnf -T $dir $sdir
fi
- s lnf -T $dir $sdir
-fi
-sgo $type
+ sgo $type
-# if I wanted the from address to be renamed and sent to a different address,
-# echo "sdx@localhost development@localhost" | sudo dd of=/etc/postfix/recipient_canonical
-# sudo postmap hash:/etc/postfix/recipient_canonical
-# sudo service postfix reload
+ # if I wanted the from address to be renamed and sent to a different address,
+ # echo "sdx@localhost development@localhost" | sudo dd of=/etc/postfix/recipient_canonical
+ # sudo postmap hash:/etc/postfix/recipient_canonical
+ # sudo service postfix reload
if ! ssh root@$tg bash <<'EOF'
set -e
chmod +x /usr/local/bin/{mount-latest-subvol,check-subvol-stale}
-mount-latest-subvol
+/usr/local/bin/mount-latest-subvol
EOF
then
echo "$0: warning: failed mount-latest-subvol on $tg"
# See the License for the specific language governing permissions and
# limitations under the License.
-
+# usage: mount-latest-subvol
[[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
ret=0
+##### begin setup fstab for subvols we care about ######
first_root_crypt=$(awk '$2 == "/" {print $1}' /etc/mtab)
tu /etc/fstab <<EOF
$first_root_crypt /a btrfs noatime,subvol=a 0 0
treetowl|x2|frodo)
tu /etc/fstab <<EOF
$first_root_crypt /q btrfs noatime,subvol=q 0 0
+$first_root_crypt /m btrfs noatime,subvol=m 0 0
/q/p /p none bind 0 0
EOF
;;
esac
+##### end setup fstab for subvols we care about ######
-for vol in q a; do
+for vol in q a m; do
d=/$vol
if ! awk '{print $2}' /etc/fstab | grep -xF $d &>/dev/null; then
continue
fi
- binds=()
- roots=($d)
+ ##### begin building up list of bind mounts ######
+ binds=() # list of bind mounts
+ roots=($d) # list of bind mounts, plus the original mount
while true; do
new_roots=()
for r in ${roots[@]}; do
- # example
+ # eg. when r=/q/p, for lines like
# /q/p /p none bind 0 0
+ # output /p
new_roots+=($(sed -rn "s#^$r/\S+\s+(\S+)\s+none\s+bind\s.*#\1#p" /etc/fstab))
done
(( ${#new_roots} )) || break
binds+=(${new_roots[@]})
roots=( ${new_roots[@]} )
done
+ ##### end building up list of bind mounts ######
+
# if latest is already mounted, make sure binds are mounted and move on
if e check-subvol-stale $d; then
--- /dev/null
+#!/bin/bash
+
+set -eE -o pipefail
+trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
+
+offlineimap -u quiet
+shopt -s nullglob
+
+omv() { # offlineimap mv
+ src="$1"
+ dst="$2"
+ found_files=false
+ for x in new cur; do
+ files=("$src_base"/"$src"/$x/*)
+ if [[ $files ]]; then
+ found_files=true
+ mv "${files[@]}" /m/md/"$dst"/$x
+ fi
+ done
+}
+
+src_base=/m/offlineimap
+omv "Sent Items" "Sent"
+omv INBOX offlineimaptmp
+src_base=/m/md
+if $found_files; then
+ sieve-filter -eW ~/sieve/main.sieve offlineimaptmp &>/dev/null
+ # the default folder is INBOX for anything leftover
+ omv offlineimaptmp INBOX
+ # remove messages from remote host
+ offlineimap -u quiet
+ mu index &>/dev/null ||:
+fi
# phabricator complained about wanting arcanist first
pi arcanist/unstable mercurial
-for x in /a/bin/bash_unpublished/*; do source $x; done
-
# duplicated in mediawiki setup. todo fix that.
s DEBIAN_FRONTEND=noninteractive pi mysql-server
cd # mysql_secure_installation writes some temp files to the current dir,
--- /dev/null
+#!/bin/bash -l
+
+[[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
+
+# davdroid setup instructions at the bottom
+
+# main docs:
+# http://radicale.org/user_documentation/
+# https://davdroid.bitfire.at/configuration/
+
+
+# created password file with:
+# htpasswd -c /etc/nginx/caldav/htpasswd ian
+
+
+# python-dulwich, to track changes with git, per
+# http://radicale.org/user_documentation/#idgit-support
+pi nginx python-dulwich radicale
+
+# I moved /var/lib/radicale after it's initialization.
+# I did a sudo -u radicale git init in the collections subfolder
+# after it gets created, per the git docs.
+lnf -T /q/radicale /var/lib/radicale
+
+acme-tiny -n cal.iank.pw
+
+# from https://www.williamjbowman.com/blog/2015/07/24/setting-up-webdav-caldav-and-carddav-servers/
+mkdir /etc/nginx/caldav
+touch /etc/nginx/caldav/htpasswd
+chmod 640 /etc/nginx/caldav/htpasswd
+chgrp www-data /etc/nginx/caldav/htpasswd
+nginx-site -f 5232 - cal.iank.pw <<'EOF'
+ auth_basic "Not currently available";
+ auth_basic_user_file /etc/nginx/caldav/htpasswd;
+EOF
+
+# coment in this file says this is needed for it to run on startup
+sed -ri 's/^\s*#+\s*(ENABLE_RADICALE\s*=\s*yes\s*)/\1/' /etc/default/radicale
+sgo radicale
+
+setini() {
+ key="$1" value="$2" section="$3"
+ file="/etc/radicale/config"
+ sed -ri "/ *\[$section\]/,/^ *\[[^]]+\]/{/^\s*$key[[:space:]=]/d};/ *\[$section\]/a $key = $value" "$file"
+}
+
+# comments say default is 0.0.0.0:5232
+setini hosts 127.0.0.1:5232 server
+
+# davdroid from f-droid. username ian,
+# url https://iank.pw/radicale/ian.
+# I disabled power management feature, it's got 240 min sync interval,
+# so it shouldn't be bad.
+#
+
+# when setting up davdroid, switch to groups are per-contact categories,
+# per https://davdroid.bitfire.at/configuration/radicale/
+#
+# set account name as ian@iankelling.org, per help text below the
+# field.
+#
+# After setting up account, I added one address book, named
+# ian. calender was already created, named ian. checked boxes under
+# both. synced.
+#
+# ignorable background info:
+#
+# When debugging, tailed /var/log/radicale/radicale.log and nginx log,
+# both show the requests happening. Without creating the address book,
+# after creating a contact, a sync would delete it.
+#
+# Address books correspond to .props files in the radicale dir.
+#
+# Some background is here,
+# https://davdroid.bitfire.at/faq/entry/cant-manage-groups-on-device/
+# which shows separate vcard option is from rfc 6350, the other is 2426,
+# radicale page says it implements the former not the latter,
+# which conflicts with the documentation of which to select, but whatever.
+# http://radicale.org/technical_choices/
+# https://davdroid.bitfire.at/faq/entry/cant-manage-groups-on-device/
+#
+# Note, url above says only cayanogenmod 13+ and omnirom can manage groups.
[[ $EUID == 0 ]] || exec sudo "$BASH_SOURCE" "$@"
if test -e /q/root/.ssh; then
- /a/exe/lnf /q/root/.ssh /root
+ dest=/q/root/.ssh
+ /a/exe/lnf $dest /root
else
+ dest=/root/.ssh
mkdir -p /root/.ssh
chmod 700 /root/.ssh
fi
# -t times, so it won't rewrite the file every time,
# -L resolve links
-rsync -rtL $(eval echo ~${SUDO_USER:-$USER})/.ssh /root
+rsync -rtL $(eval echo ~${SUDO_USER:-$USER})/.ssh $dest
chown -R root:root /root/.ssh
--- /dev/null
+#!/bin/bash
+
+set -eE -o pipefail
+trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
+
+[[ $EUID == 0 ]]
+
+# to deal with this bug until it\'s fixed
+# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=741521
+# I just happened to notice it in my journal.
+str="dns: sendto() to \S\+ failed: Connection refused, failing over"
+if journalctl --since=-9m --unit=spamassassin | \
+ grep "$str" &>/dev/null; then
+ echo "dns bug, restarting spamassassin"
+ systemctl restart spamassassin
+fi
--- /dev/null
+#!/bin/bash
+
+set -eE -o pipefail
+trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
+
+do-forward() {
+ cmd=$1; shift
+ for port; do
+ /sbin/iptables -t nat $cmd PREROUTING -i eth0 -p tcp -m tcp --dport $port -j DNAT --to-destination 10.8.0.4:$port
+ done
+}
+
+ports=(25 993)
+case $1 in
+ start)
+ do-forward -A ${ports[@]}
+ ;;
+ stop)
+ do-forward -D ${ports[@]}
+ ;;
+ *)
+ echo "$0: error: expected 1 argument of start or stop"
+ exit 1
+ ;;
+esac