#!/bin/bash # Turns the contest restrictions off and on, for maintenance. Root only, so # it is used through sudo from the administrator account (icpc). # # sudo contest-restrictions status # sudo contest-restrictions report [SINCE] (refused attempts, default: today) # sudo contest-restrictions off [all|firewall|usb|network] # sudo contest-restrictions on [all|firewall|usb|network] # # firewall firewall, web proxy, DNS filter (/disable_firewall.sh, /enable_firewall.sh) # usb USBGuard, USB disks and phones for the team account # network network settings (Wi-Fi, hotspot, ...) for the team account # # "off" stays in effect after a reboot, until "on". set -u DISABLED=/etc/contest-restrictions/disabled USB_FILES="/etc/polkit-1/rules.d/10-acpc-no-removable-media.rules /etc/udev/rules.d/72-acpc-no-phone-access.rules" NETWORK_FILES="/etc/polkit-1/rules.d/10-acpc-no-network-changes.rules" usage() { sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//' exit 2 } [ "$(id -u)" -eq 0 ] || { echo "Run it with sudo, from the administrator account." >&2; exit 1; } mkdir -p "$DISABLED" chmod 700 /etc/contest-restrictions # Moves rule files out of the way (off) or back (on) move_rules() { action=$1 shift for f in "$@"; do if [ "$action" = off ] && [ -f "$f" ]; then mv "$f" "$DISABLED/" elif [ "$action" = on ] && [ -f "$DISABLED/$(basename "$f")" ]; then mv "$DISABLED/$(basename "$f")" "$f" fi done } firewall() { if [ "$1" = off ]; then /disable_firewall.sh else /enable_firewall.sh /refresh_rules.sh fi } usb() { if [ "$1" = off ]; then systemctl disable --now usbguard.service # USBGuard leaves the controllers refusing new devices when it stops for f in /sys/bus/usb/devices/usb*/authorized_default; do [ -e "$f" ] && echo 1 > "$f" done move_rules off $USB_FILES udevadm control --reload echo "USB devices allowed (unplug and plug again a device refused before)." echo "USB disks and phones can be used from the team account." else # Removable media mounted while the restriction was off findmnt -rno TARGET | grep '^/run/media/' | while read -r m; do umount "$m" && echo "Unmounted $m" done move_rules on $USB_FILES udevadm control --reload systemctl enable --now usbguard.service echo "USB restrictions on. The devices plugged in right now stay allowed until they are unplugged." fi } network() { if [ "$1" = off ]; then move_rules off $NETWORK_FILES echo "The team account can change the network settings." else move_rules on $NETWORK_FILES echo "Network settings restricted to the administrators." fi } state() { if [ "$1" = yes ]; then echo "on"; else echo "OFF"; fi } status() { fw=no; systemctl is-active --quiet firewall.service && fw=yes us=no; systemctl is-active --quiet usbguard.service && [ -f /etc/polkit-1/rules.d/10-acpc-no-removable-media.rules ] && us=yes nw=no; [ -f /etc/polkit-1/rules.d/10-acpc-no-network-changes.rules ] && nw=yes echo "firewall $(state $fw)" echo "usb $(state $us)" echo "network $(state $nw)" } # What the machine refused: web sites, DNS names, USB devices report() { since=${1:-today} echo "== Web sites refused by the proxy (since $since)" journalctl -t contest_proxy --since "$since" --no-pager -o cat 2>/dev/null \ | awk '$3 != "200" || $4 == "-" {print $2}' | sort | uniq -c | sort -rn | head -40 echo echo "== DNS names refused by the filter" # Names that Firefox, VS Code, the IDEs and Kattis pages look up on their # own in the background background='(^|\.)(mozilla\.(com|org|net)|firefox-portal-detection\.com|googleapis\.com|gvt1\.com|microsoft\.com|vscode-cdn\.net|vscode-unpkg\.net|exp-tas\.com|openh264\.org|ipv4only\.arpa|sentry\.io|cloudflare-dns\.com|jetbrains\.com|ubuntu\.com|canonical\.com|snapcraft\.io)$' names=$(journalctl -u contest-dns --since "$since" --no-pager -o cat 2>/dev/null \ | sed -n 's/^config \(.*\) is NXDOMAIN$/\1/p') printf '%s\n' "$names" | grep -v -E "$background" | grep . | sort | uniq -c | sort -rn | head -40 echo " ($(printf '%s\n' "$names" | grep -c -E "$background") lookups by Firefox, VS Code and the IDEs in the background, not listed)" echo echo "== USB devices rejected" journalctl -u usbguard --since "$since" --no-pager -o short 2>/dev/null \ | grep "target.new='reject'" \ | sed -E 's/^([A-Za-z]+ +[0-9]+ [0-9:]+).* name "([^"]*)".*/\1 \2/' | tail -20 } case "${1:-}" in status) status ;; report) report "${2:-today}" ;; on|off) what=${2:-all} case "$what" in all) firewall "$1"; usb "$1"; network "$1" ;; firewall|usb|network) "$what" "$1" ;; *) usage ;; esac echo status ;; *) usage ;; esac