107 lines
4.5 KiB
Bash
Executable File
107 lines
4.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Installs the contest firewall without Ansible, on the booted contest image
|
|
# (Ubuntu 26.04, as in the ICPC image). Run it as root from this directory.
|
|
#
|
|
# sudo ./install_firewall.sh # install, the firewall starts at the next boot
|
|
# sudo ./install_firewall.sh --start # install and start it now
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")"
|
|
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
echo "Run this script as root (sudo)." >&2
|
|
exit 1
|
|
fi
|
|
START=no
|
|
[ "${1:-}" = "--start" ] && START=yes
|
|
TIMEZONE=${TIMEZONE:-America/Moncton}
|
|
|
|
entries() {
|
|
sed -e 's/#.*//' -e 's/[[:space:]]//g' "$1" | grep -v '^$'
|
|
}
|
|
|
|
echo "Installing nftables, dig, chrony and the nginx stream module"
|
|
apt-get update
|
|
apt-get install -y nftables dnsutils chrony nginx libnginx-mod-stream dnsmasq-base
|
|
id contest-proxy >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin contest-proxy
|
|
|
|
echo "Copying the lists, scripts and systemd units"
|
|
install -d -m 0755 /etc/contest-firewall
|
|
install -m 0644 allowed_domains.txt dns_servers.txt ntp_servers.txt files/contest-proxy.conf /etc/contest-firewall/
|
|
install -m 0500 files/firewall.sh files/refresh_rules.sh files/enable_firewall.sh files/disable_firewall.sh /
|
|
install -m 0755 test_firewall.sh /
|
|
install -m 0644 files/firewall.service files/refresh_rules.service files/refresh_rules.timer files/contest-proxy.service files/contest-dns.service /etc/systemd/system/
|
|
|
|
echo "Configuring DNS (systemd-resolved)"
|
|
dns_list=$(entries dns_servers.txt | tr '\n' ' ' | sed 's/ $//')
|
|
if grep -qE '^#?DNS=' /etc/systemd/resolved.conf; then
|
|
sed -i -E "s|^#?DNS=.*|DNS=${dns_list}|" /etc/systemd/resolved.conf
|
|
else
|
|
echo "DNS=${dns_list}" >> /etc/systemd/resolved.conf
|
|
fi
|
|
if grep -qE '^#?DNSStubListener=' /etc/systemd/resolved.conf; then
|
|
sed -i -E 's|^#?DNSStubListener=.*|DNSStubListener=no|' /etc/systemd/resolved.conf
|
|
else
|
|
echo "DNSStubListener=no" >> /etc/systemd/resolved.conf
|
|
fi
|
|
|
|
echo "Configuring NTP (chrony) and the timezone"
|
|
sed -i -E '/^# (BEGIN|END) CONTEST FIREWALL NTP SERVERS$/,/^# END CONTEST FIREWALL NTP SERVERS$/d' /etc/chrony/chrony.conf
|
|
sed -i -E '/^(pool|server)[[:space:]]/d' /etc/chrony/chrony.conf
|
|
{
|
|
echo "# BEGIN CONTEST FIREWALL NTP SERVERS"
|
|
for ntp in $(entries ntp_servers.txt); do
|
|
echo "pool ${ntp} iburst"
|
|
done
|
|
echo "# END CONTEST FIREWALL NTP SERVERS"
|
|
cat /etc/chrony/chrony.conf
|
|
} > /etc/chrony/chrony.conf.new
|
|
mv /etc/chrony/chrony.conf.new /etc/chrony/chrony.conf
|
|
# Ubuntu 26.04 adds NTS time sources that the firewall blocks, and chrony
|
|
# does not use the plain NTP servers while those sources are configured
|
|
if [ -f /etc/chrony/sources.d/ubuntu-ntp-pools.sources ]; then
|
|
mv /etc/chrony/sources.d/ubuntu-ntp-pools.sources /etc/chrony/sources.d/ubuntu-ntp-pools.sources.disabled
|
|
fi
|
|
# The contest machines may also run Windows, which keeps the hardware clock
|
|
# in local time: chrony must not write its time into that clock
|
|
sed -i -E '/^rtcsync/d' /etc/chrony/chrony.conf
|
|
grep -q '^makestep' /etc/chrony/chrony.conf || echo 'makestep 1 3' >> /etc/chrony/chrony.conf
|
|
# Fallback: time from the contest site if NTP does not work
|
|
install -m 0500 files/time/time_from_contest.sh /
|
|
install -m 0644 files/time/time_from_contest.service files/time/time_from_contest.timer /etc/systemd/system/
|
|
ln -sf "/usr/share/zoneinfo/${TIMEZONE}" /etc/localtime
|
|
|
|
echo "Blocking open.kattis.com"
|
|
grep -q 'open\.kattis\.com' /etc/hosts || printf '127.0.0.1\topen.kattis.com\n' >> /etc/hosts
|
|
|
|
# Firefox must send the real site name, which encrypted client hello (ECH) hides
|
|
POLICIES=/usr/lib/firefox/distribution/policies.json
|
|
if [ -f "$POLICIES" ]; then
|
|
echo "Turning off encrypted client hello in Firefox"
|
|
python3 - "$POLICIES" <<'PY'
|
|
import json, sys
|
|
path = sys.argv[1]
|
|
data = json.load(open(path))
|
|
prefs = data.setdefault("policies", {}).setdefault("Preferences", {})
|
|
for name in ("network.dns.echconfig.enabled", "network.dns.http3_echconfig.enabled"):
|
|
prefs[name] = {"Value": False, "Status": "locked"}
|
|
json.dump(data, open(path, "w"), indent=2)
|
|
PY
|
|
fi
|
|
|
|
echo "Restarting services"
|
|
systemctl restart systemd-resolved
|
|
systemctl enable chrony
|
|
systemctl restart chrony
|
|
timedatectl set-ntp true || true
|
|
systemctl daemon-reload
|
|
systemctl enable firewall.service refresh_rules.service refresh_rules.timer time_from_contest.timer
|
|
systemctl start refresh_rules.timer
|
|
|
|
if [ "$START" = yes ]; then
|
|
systemctl start firewall.service
|
|
/refresh_rules.sh
|
|
echo "Firewall installed and active."
|
|
else
|
|
echo "Firewall installed. It will start at the next boot (or run: sudo /enable_firewall.sh)."
|
|
fi
|