- Fix _commit_and_sync infinite recursion - Use admin session for subnet_index allocation (bypass RLS) - Auto-set VPN endpoint from CORS_ORIGINS hostname - Remove server address field from VPN setup UI - Add DELETE endpoint and button for VPN config removal - Add wg-reload watcher for reliable config hot-reload via wg syncconf - Add wg_status.json writer for live peer handshake status in UI - Per-tenant SNAT for poller-to-device routing through VPN - Restrict VPN→eth0 forwarding to Docker networks only (block exit node abuse) - Use 10.10.0.0/16 allowed-address in RouterOS commands - Fix structlog event= conflict (use audit=True) - Export backup_scheduler proxy for firmware/upgrade imports
91 lines
3.9 KiB
Bash
Executable File
91 lines
3.9 KiB
Bash
Executable File
#!/bin/sh
|
|
# Enable forwarding between Docker network and WireGuard tunnel
|
|
# Idempotent: check before adding to prevent duplicates on restart
|
|
# Allow Docker→VPN (poller/API reaching devices)
|
|
iptables -C FORWARD -i eth0 -o wg0 -j ACCEPT 2>/dev/null || iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
|
|
# Allow VPN→Docker ONLY (devices reaching poller/API, NOT the public internet)
|
|
iptables -C FORWARD -i wg0 -o eth0 -d 172.16.0.0/12 -j ACCEPT 2>/dev/null || iptables -A FORWARD -i wg0 -o eth0 -d 172.16.0.0/12 -j ACCEPT
|
|
# Block VPN→anywhere else (prevents using server as exit node)
|
|
iptables -C FORWARD -i wg0 -o eth0 -j DROP 2>/dev/null || iptables -A FORWARD -i wg0 -o eth0 -j DROP
|
|
|
|
# Block cross-subnet traffic on wg0 (tenant isolation)
|
|
# Peers in 10.10.1.0/24 cannot reach peers in 10.10.2.0/24
|
|
iptables -C FORWARD -i wg0 -o wg0 -j DROP 2>/dev/null || iptables -A FORWARD -i wg0 -o wg0 -j DROP
|
|
|
|
# Block IPv6 forwarding on wg0 (prevent link-local bypass)
|
|
ip6tables -C FORWARD -i wg0 -j DROP 2>/dev/null || ip6tables -A FORWARD -i wg0 -j DROP
|
|
|
|
# NAT for return traffic — per-tenant SNAT rules are applied by wg-reload watcher
|
|
# (nat_rules.sh is generated by sync_wireguard_config)
|
|
|
|
echo "WireGuard forwarding and tenant isolation rules applied"
|
|
|
|
# Start config reload watcher in background
|
|
# Polls for .reload flag every 2s, applies changes via wg syncconf
|
|
(
|
|
CONF_DIR="/config/wg_confs"
|
|
RELOAD_FLAG="$CONF_DIR/.reload"
|
|
echo "wg-reload: watcher started (pid $$)"
|
|
# Wait for wg0 interface to be fully up before processing reloads
|
|
while ! wg show wg0 >/dev/null 2>&1; do
|
|
sleep 2
|
|
done
|
|
# Apply NAT rules on startup if they exist
|
|
if [ -f "$CONF_DIR/nat_rules.sh" ]; then
|
|
sh "$CONF_DIR/nat_rules.sh" 2>&1
|
|
echo "wg-reload: startup NAT rules applied"
|
|
fi
|
|
# Clear any reload flag that was set during startup (interface already has the config)
|
|
rm -f "$RELOAD_FLAG"
|
|
echo "wg-reload: wg0 is up, watching for changes"
|
|
while true; do
|
|
if [ -f "$RELOAD_FLAG" ]; then
|
|
rm -f "$RELOAD_FLAG"
|
|
sleep 0.5
|
|
if [ -f "$CONF_DIR/wg0.conf" ]; then
|
|
# Strip Address and comments; keep ListenPort + PrivateKey + Peers
|
|
# wg syncconf rejects Address but needs ListenPort to preserve it
|
|
grep -v "^Address" "$CONF_DIR/wg0.conf" | grep -v "^#" | wg syncconf wg0 /dev/stdin 2>&1
|
|
# Apply per-tenant SNAT rules for poller connectivity
|
|
if [ -f "$CONF_DIR/nat_rules.sh" ]; then
|
|
sh "$CONF_DIR/nat_rules.sh" 2>&1
|
|
echo "wg-reload: NAT rules applied"
|
|
fi
|
|
if [ $? -eq 0 ]; then
|
|
echo "wg-reload: config applied"
|
|
else
|
|
echo "wg-reload: syncconf failed"
|
|
fi
|
|
fi
|
|
fi
|
|
sleep 2
|
|
done
|
|
) &
|
|
|
|
# Start status writer in background
|
|
# Writes wg_status.json every 15 seconds from `wg show wg0 dump`
|
|
(
|
|
STATUS_FILE="/config/wg_status.json"
|
|
# Wait for wg0
|
|
while ! wg show wg0 >/dev/null 2>&1; do
|
|
sleep 2
|
|
done
|
|
echo "wg-status: writer started"
|
|
while true; do
|
|
# Parse `wg show wg0 dump` into JSON array
|
|
# Format: private_key public_key listen_port fwmark
|
|
# Peer lines: public_key preshared_key endpoint allowed_ips latest_handshake transfer_rx transfer_tx persistent_keepalive
|
|
wg show wg0 dump 2>/dev/null | awk -F'\t' '
|
|
BEGIN { printf "[" ; first=1 }
|
|
NR > 1 {
|
|
if (!first) printf ","
|
|
first=0
|
|
printf "{\"public_key\":\"%s\",\"endpoint\":\"%s\",\"allowed_ips\":\"%s\",\"last_handshake\":%s,\"rx\":%s,\"tx\":%s}",
|
|
$1, $3, $4, ($5 == "" ? "0" : $5), ($6 == "" ? "0" : $6), ($7 == "" ? "0" : $7)
|
|
}
|
|
END { printf "]\n" }
|
|
' > "${STATUS_FILE}.tmp" && mv "${STATUS_FILE}.tmp" "$STATUS_FILE"
|
|
sleep 15
|
|
done
|
|
) &
|