diff --git a/Docker/entrypoint.sh b/Docker/entrypoint.sh index ac3887c..e707898 100644 --- a/Docker/entrypoint.sh +++ b/Docker/entrypoint.sh @@ -120,7 +120,10 @@ start_vpn() { ip link add "$INTERFACE" type wireguard # Apply the WireGuard config (keys, peer, endpoint) - wg setconf "$INTERFACE" <(grep -v -i '^\(Address\|DNS\|MTU\|Table\|PreUp\|PostUp\|PreDown\|PostDown\|SaveConfig\)' "$CONFIG_FILE") + # 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") # Assign the address ip -4 address add "$VPN_ADDRESS" dev "$INTERFACE" @@ -128,6 +131,10 @@ start_vpn() { # Set MTU 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 + ip route del default dev "$INTERFACE" 2>/dev/null || true + # Find default gateway/interface for the endpoint route DEFAULT_GW=$(ip route | grep '^default' | head -1 | awk '{print $3}') DEFAULT_IF=$(ip route | grep '^default' | head -1 | awk '{print $5}') @@ -167,11 +174,15 @@ start_vpn() { fi fi - # Set up DNS + # Set up DNS (handle comma-separated DNS servers) VPN_DNS=$(grep -i '^DNS' "$CONFIG_FILE" | head -1 | sed 's/.*= *//;s/ //g') if [ -n "$VPN_DNS" ]; then - echo "nameserver $VPN_DNS" > /etc/resolv.conf - echo "[vpn] DNS set to ${VPN_DNS}" + # Clear resolv.conf and add each DNS server on its own line + > /etc/resolv.conf + for dns in $(echo "$VPN_DNS" | tr ',' ' '); do + echo "nameserver $dns" >> /etc/resolv.conf + done + echo "[vpn] DNS set to: ${VPN_DNS}" fi echo "[vpn] WireGuard interface ${INTERFACE} is up." diff --git a/Docker/test_vpn.py b/Docker/test_vpn.py index 68265e3..9cedd63 100644 --- a/Docker/test_vpn.py +++ b/Docker/test_vpn.py @@ -197,6 +197,8 @@ class TestVPNImage(unittest.TestCase): result = podman_exec(CONTAINER_NAME, ["wg", "show", "wg0"]) self.assertEqual(result.returncode, 0, f"wg show failed:\n{result.stderr}") self.assertIn("peer", result.stdout.lower(), "No peer information in wg show output") + # AllowedIPs should be present in wg show output + self.assertIn("allowed ips", result.stdout.lower(), "AllowedIPs not found in wg show output") def test_03_allowedips_routes_set(self): """Routes are set dynamically based on AllowedIPs from config.""" @@ -208,8 +210,22 @@ class TestVPNImage(unittest.TestCase): # 0.0.0.0/1 dev wg0 and 128.0.0.0/1 dev wg0 self.assertIn("0.0.0.0/1", result.stdout, "Route 0.0.0.0/1 not found") self.assertIn("128.0.0.0/1", result.stdout, "Route 128.0.0.0/1 not found") + # Make sure there is NO default route through wg0 (Table = off should prevent this) + self.assertNotIn("default dev wg0", result.stdout, "Default route through wg0 found — Table = off not working!") logger.info("AllowedIPs routes verified: %s", result.stdout.strip()) + def test_03b_dns_configured(self): + """DNS is configured correctly with multiple nameserver lines.""" + self._skip_if_not_root() + result = podman_exec(CONTAINER_NAME, ["cat", "/etc/resolv.conf"]) + self.assertEqual(result.returncode, 0, f"cat /etc/resolv.conf failed:\n{result.stderr}") + # Should have two separate nameserver lines, not one with commas + self.assertIn("nameserver 198.18.0.1", result.stdout, "DNS 198.18.0.1 not found") + self.assertIn("nameserver 198.18.0.2", result.stdout, "DNS 198.18.0.2 not found") + # Make sure there are no commas in nameserver lines + self.assertNotIn("nameserver 198.18.0.1,198.18.0.2", result.stdout, "DNS servers written on one line with comma!") + logger.info("DNS config verified: %s", result.stdout.strip()) + def test_04_kill_switch_blocks_traffic(self): """When WireGuard is down, traffic is blocked (kill switch).""" self._skip_if_not_root()