Files
2026-10-15 20:44:05 -03:00

91 lines
4.3 KiB
Bash
Executable File

#!/bin/bash
# Base nftables ruleset: inbound traffic is dropped, outbound traffic is rejected
# except loopback, established connections, the local proxy (web), the local
# DNS filter and the time servers that /refresh_rules.sh adds.
# Web traffic (ports 80 and 443) of every program is redirected to the local
# proxy (contest-proxy.service), which checks the site name. Only the proxy
# may connect to the outside on port 443.
CONF=/etc/contest-firewall
# Remove any persisting nftables rules
rm -f /etc/nftables.rules
touch /etc/nftables.rules
# Configuration of the proxy: the allowed names and the DNS resolver
mkdir -p /run/contest-firewall
for name in $(sed -e 's/#.*//' -e 's/[[:space:]]//g' "$CONF/allowed_domains.txt"); do
echo "$name $name:443;"
done > /run/contest-firewall/proxy_names.conf
# The proxy resolves the names through the DNS filter below
echo "resolver 127.0.0.1 ipv6=off valid=30s;" > /run/contest-firewall/proxy_resolver.conf
# Local DNS filter (contest-dns.service): the allowed domains and the time
# servers are forwarded to the DNS resolvers, every other name gets no answer.
# Programs on the machine ask it through /etc/resolv.conf, and only the proxy
# account (which also runs the filter) may reach the DNS resolvers. This
# closes DNS as a way to exchange data with a server outside.
{
echo "listen-address=127.0.0.1"
echo "bind-interfaces"
echo "no-resolv"
echo "no-hosts"
echo "user=contest-proxy"
echo "group=nogroup"
echo "cache-size=1000"
echo "log-queries"
echo "server=/#/"
for name in $(sed -e 's/#.*//' -e 's/[[:space:]]//g' "$CONF/allowed_domains.txt" "$CONF/ntp_servers.txt"); do
for dns in $(sed -e 's/#.*//' -e 's/[[:space:]]//g' "$CONF/dns_servers.txt"); do
echo "server=/$name/$dns"
done
done
} > /run/contest-firewall/dnsmasq.conf
# /run/resolvconf is one of the places the AppArmor profiles (dig, ...) accept,
# and the resolvconf package that would also use it is not installed
mkdir -p /run/resolvconf
echo "nameserver 127.0.0.1" > /run/resolvconf/resolv.conf
ln -sfn /run/resolvconf/resolv.conf /etc/resolv.conf
# Flush all rules, then create the table, chains and address sets
nft flush ruleset
nft add table inet filter
nft add chain inet filter input '{ type filter hook input priority 0 ; policy drop ; }'
nft add chain inet filter forward '{ type filter hook forward priority 0 ; policy drop ; }'
nft add chain inet filter output '{ type filter hook output priority 0 ; policy accept ; }'
nft add set inet filter ntp_ips '{ type ipv4_addr ; flags interval ; }'
# Redirect web traffic to the proxy, except the proxy's own connections and
# the local sites of the image (DevDocs)
nft add table inet nat
nft add chain inet nat output '{ type nat hook output priority -100 ; }'
nft add rule inet nat output meta skuid contest-proxy return
nft add rule inet nat output ip daddr 127.0.0.0/8 return
nft add rule inet nat output meta nfproto ipv4 tcp dport 443 redirect to :8443
nft add rule inet nat output meta nfproto ipv4 tcp dport 80 redirect to :8080
# Established/related connections and loopback
nft add rule inet filter input ct state related,established accept
nft add rule inet filter output ct state related,established accept
nft add rule inet filter input iif lo accept
nft add rule inet filter output oif lo accept
# Connections redirected to the proxy (they still show the network interface here)
nft add rule inet filter output ip daddr 127.0.0.1 tcp dport '{ 8080, 8443 }' ct status dnat accept
# Web: only the proxy connects to the outside, by name, to the allowed sites
# it resolves itself through the DNS filter (no list of addresses to keep up
# to date, content delivery networks change them all the time)
nft add rule inet filter output meta skuid contest-proxy tcp dport 443 accept
# Time servers, for chrony only (addresses filled by /refresh_rules.sh)
nft add rule inet filter output meta skuid _chrony ip daddr @ntp_ips udp dport 123 accept
# DNS resolvers, for the proxy and the DNS filter only
for dns in $(sed -e 's/#.*//' -e 's/[[:space:]]//g' "$CONF/dns_servers.txt"); do
nft add rule inet filter output meta skuid contest-proxy ip daddr "$dns" udp dport 53 accept
nft add rule inet filter output meta skuid contest-proxy ip daddr "$dns" tcp dport 53 accept
done
# Reject any other outgoing connection
nft add rule inet filter output reject