This commit is contained in:
2026-05-17 18:57:12 +02:00
parent 54ac5e9ab7
commit a08a8f7408

View File

@@ -130,10 +130,8 @@ start_vpn() {
ip link add "$INTERFACE" type wireguard
# Apply the WireGuard config (keys, peer, endpoint)
# We filter out Address/DNS/MTU/PreUp/PostUp/PreDown/PostDown/SaveConfig
# AllowedIPs is kept because WireGuard needs it to know which traffic to tunnel.
# We remove the auto-created default route afterwards and set our own.
wg setconf "$INTERFACE" <(grep -v -i '^\(Address\|DNS\|MTU\|PreUp\|PostUp\|PreDown\|PostDown\|SaveConfig\)' "$CONFIG_FILE")
# Filter out wg-quick directives that wg setconf doesn't understand
wg setconf "$INTERFACE" <(grep -v -i '^\(Address\|DNS\|MTU\|Table\|PreUp\|PostUp\|PreDown\|PostDown\|SaveConfig\)' "$CONFIG_FILE")
# Log public key so it can be verified against the server's peer list
local PUBKEY
@@ -143,38 +141,35 @@ start_vpn() {
# Assign the address
ip -4 address add "$VPN_ADDRESS" dev "$INTERFACE"
# Set MTU
# Set MTU and bring up
ip link set mtu 1420 up dev "$INTERFACE"
# Remove the auto-created default route by wg setconf (if AllowedIPs = 0.0.0.0/0)
# We set our own routes manually to avoid breaking the endpoint connection
# ── fwmark-based routing (mirrors wg-quick behavior) ──
# WireGuard marks its own encapsulated UDP packets with this fwmark.
# Policy rules then ensure:
# - Normal packets (no mark) → VPN routing table → wg0
# - WireGuard-encapsulated packets (marked) → main table → eth0
local FW_MARK=51820
local FW_TABLE=51820
wg set "$INTERFACE" fwmark "$FW_MARK"
# Remove any auto-created default route on wg0
ip route del default dev "$INTERFACE" 2>/dev/null || true
# Find default gateway/interface for the endpoint route
# VPN routing table: send everything through the tunnel
ip -4 route add default dev "$INTERFACE" table "$FW_TABLE"
# Policy rules:
# 1. Packets NOT marked by WireGuard use the VPN table (→ wg0)
# 2. suppress_prefixlength 0: ignore bare default routes in main table,
# but keep more-specific routes (e.g. LAN, endpoint) working
ip -4 rule add not fwmark "$FW_MARK" table "$FW_TABLE"
ip -4 rule add table main suppress_prefixlength 0
# Find default gateway/interface
DEFAULT_GW=$(ip route | grep '^default' | head -1 | awk '{print $3}')
DEFAULT_IF=$(ip route | grep '^default' | head -1 | awk '{print $5}')
# Route VPN endpoint through the container's default gateway
if [ -n "$DEFAULT_GW" ] && [ -n "$DEFAULT_IF" ]; then
ip route add "$VPN_ENDPOINT/32" via "$DEFAULT_GW" dev "$DEFAULT_IF" 2>/dev/null || true
fi
# Parse AllowedIPs from config and add routes dynamically
ALLOWED_IPS=$(grep -i '^AllowedIPs' "$CONFIG_FILE" | head -1 | sed 's/.*= *//;s/ //g')
if [ -n "$ALLOWED_IPS" ]; then
for ip in $(echo "$ALLOWED_IPS" | tr ',' ' '); do
if [ "$ip" = "0.0.0.0/0" ]; then
# Use the split route trick to avoid overriding the default route
# (which would break the endpoint connection)
ip route add 0.0.0.0/1 dev "$INTERFACE" 2>/dev/null || true
ip route add 128.0.0.0/1 dev "$INTERFACE" 2>/dev/null || true
else
ip route add "$ip" dev "$INTERFACE" 2>/dev/null || true
fi
done
fi
# ── Policy routing: ensure responses to incoming LAN traffic go back via eth0 ──
if [ -n "$DEFAULT_GW" ] && [ -n "$DEFAULT_IF" ]; then
# Get the container's eth0 IP address (BusyBox-compatible, no grep -P)
@@ -201,8 +196,12 @@ start_vpn() {
fi
echo "[vpn] WireGuard interface ${INTERFACE} is up."
echo "[vpn] Routes:"
echo "[vpn] Main routes:"
ip route show | sed 's/^/[vpn] /'
echo "[vpn] VPN table ($FW_TABLE):"
ip route show table "$FW_TABLE" 2>/dev/null | sed 's/^/[vpn] /'
echo "[vpn] Policy rules:"
ip rule show | sed 's/^/[vpn] /'
echo "[vpn] WireGuard status:"
wg show "$INTERFACE" 2>/dev/null | sed 's/^/[vpn] /'
}
@@ -213,23 +212,19 @@ start_vpn() {
stop_vpn() {
echo "[vpn] Stopping WireGuard interface ${INTERFACE}..."
# Remove routes added for AllowedIPs
ALLOWED_IPS=$(grep -i '^AllowedIPs' "$CONFIG_FILE" | head -1 | sed 's/.*= *//;s/ //g')
if [ -n "$ALLOWED_IPS" ]; then
for ip in $(echo "$ALLOWED_IPS" | tr ',' ' '); do
if [ "$ip" = "0.0.0.0/0" ]; then
ip route del 0.0.0.0/1 dev "$INTERFACE" 2>/dev/null || true
ip route del 128.0.0.0/1 dev "$INTERFACE" 2>/dev/null || true
else
ip route del "$ip" dev "$INTERFACE" 2>/dev/null || true
fi
done
fi
local FW_MARK=51820
local FW_TABLE=51820
# Remove endpoint route
if [ -n "$VPN_ENDPOINT" ]; then
ip route del "$VPN_ENDPOINT/32" 2>/dev/null || true
fi
# Remove fwmark-based policy rules
ip -4 rule del not fwmark "$FW_MARK" table "$FW_TABLE" 2>/dev/null || true
ip -4 rule del table main suppress_prefixlength 0 2>/dev/null || true
# Flush VPN routing table
ip -4 route flush table "$FW_TABLE" 2>/dev/null || true
# Remove LAN policy routing
ip -4 rule del table 100 2>/dev/null || true
ip -4 route flush table 100 2>/dev/null || true
ip link del "$INTERFACE" 2>/dev/null || true
}
@@ -246,14 +241,14 @@ health_loop() {
while true; do
sleep "$CHECK_INTERVAL"
if curl -sf --max-time 5 "http://$CHECK_HOST" > /dev/null 2>&1; then
if ping -c 1 -W 5 "$CHECK_HOST" > /dev/null 2>&1; then
if [ "$failures" -gt 0 ]; then
echo "[health] VPN recovered."
failures=0
fi
else
failures=$((failures + 1))
echo "[health] Check failed ($failures/$max_failures) — curl http://${CHECK_HOST} timed out"
echo "[health] Check failed ($failures/$max_failures) — ping ${CHECK_HOST} failed"
# Dump WireGuard stats to show if handshake is stale and how much data flows
echo "[health] wg stats:"
wg show "$INTERFACE" 2>/dev/null | grep -E 'latest handshake|transfer|endpoint' | sed 's/^/[health] /' || echo "[health] wg0 not found"
@@ -316,16 +311,16 @@ check_vpn_connectivity() {
fi
# 2. Check whether traffic actually flows through the tunnel
echo "[check] Testing traffic through tunnel (http://${CHECK_HOST})..."
echo "[check] Testing traffic through tunnel (ping ${CHECK_HOST})..."
local rx_before
rx_before=$(wg show "$INTERFACE" transfer 2>/dev/null | awk '{print $2}' | head -1)
if curl -sf --max-time 8 "http://${CHECK_HOST}" > /dev/null 2>&1; then
if ping -c 1 -W 8 "${CHECK_HOST}" > /dev/null 2>&1; then
echo "[check] OK Traffic flows — tunnel is fully working"
else
local rx_after
rx_after=$(wg show "$INTERFACE" transfer 2>/dev/null | awk '{print $2}' | head -1)
echo "[check] FAIL http://${CHECK_HOST} unreachable through tunnel"
echo "[check] FAIL ping ${CHECK_HOST} unreachable through tunnel"
if [ -n "$rx_before" ] && [ -n "$rx_after" ]; then
if [ "$rx_after" -le "$rx_before" ]; then