#!/usr/bin/env bash # Restrict wg-easy's admin API (tcp/51821) to loopback and the gateway's own # docker bridge network. Run this once on the VPS after `docker compose up # -d`, and again if the "internal" network's subnet ever changes. # # Why this exists: wg-easy runs with network_mode: host (see # docker-compose.yaml's comment on why — Traefik needs a route to # WireGuard peer addresses that only exists in whatever network namespace # wg-easy's wg0 interface lives in). Host networking means Docker itself # can no longer keep wg-easy's admin port off the VPS's public interface # the way it does for every other container here — this script is that # missing piece, done with iptables instead. # # NOT yet validated against a real multi-interface VPS. Review # DOCKER_BRIDGE_SUBNET against your actual `docker network inspect # furtka-gateway_internal` output before relying on this as your only line # of defense. A cloud provider security group that blocks 51821/tcp # entirely is a good belt-and-suspenders addition on top of this — wg-easy's # admin API has no legitimate reason to ever be reached from outside this # host. # # IPv4 only. If this VPS also has a public IPv6 address, ip6tables needs # the equivalent rules added by hand — not yet handled here. set -euo pipefail ADMIN_PORT=51821 # Must match docker-compose.yaml's networks.internal.ipam.config subnet. DOCKER_BRIDGE_SUBNET="172.28.0.0/24" if [[ $EUID -ne 0 ]]; then echo "must be run as root" >&2 exit 1 fi # Insert ACCEPT rules at the head of the chain, then append DROP at the # tail — this ordering holds regardless of how many times the script runs, # since -I always lands above whatever's already there (including a # previous run's DROP) and -A always lands below. iptables -C INPUT -p tcp --dport "$ADMIN_PORT" -s 127.0.0.1 -j ACCEPT 2>/dev/null \ || iptables -I INPUT -p tcp --dport "$ADMIN_PORT" -s 127.0.0.1 -j ACCEPT iptables -C INPUT -p tcp --dport "$ADMIN_PORT" -s "$DOCKER_BRIDGE_SUBNET" -j ACCEPT 2>/dev/null \ || iptables -I INPUT -p tcp --dport "$ADMIN_PORT" -s "$DOCKER_BRIDGE_SUBNET" -j ACCEPT iptables -C INPUT -p tcp --dport "$ADMIN_PORT" -j DROP 2>/dev/null \ || iptables -A INPUT -p tcp --dport "$ADMIN_PORT" -j DROP echo "wg-easy admin API (tcp/$ADMIN_PORT) now restricted to loopback + $DOCKER_BRIDGE_SUBNET" echo "NOTE: these rules do not persist across reboot on most distros —" echo "install iptables-persistent (Debian/Ubuntu) or an equivalent, or add" echo "this script to a boot-time hook."