-
Notifications
You must be signed in to change notification settings - Fork 6
Description
First, thank you for this excellent and very useful project.
I've been using it to run services inside the namespace. I discovered that any service running in the namespace that binds to 0.0.0.0 (like a WebUI on port 8080) is publicly exposed on the VPN's public IP address.
This is because a new network namespace has a default iptables policy of INPUT ACCEPT. This is a significant security risk, as users may (like I did) inadvertently expose sensitive services to the entire internet without realizing it.
Proposed Solution
The project should provide a "secure-by-default" posture. I propose adding an optional, modular firewall service that follows the same systemd logic as the existing interface and tunnel services.
This new service (namespaced-wireguard-vpn-firewall.service) will apply iptables rules inside the namespace. Its key features are:
- It depends on both the
interfaceandtunnelservices, ensuring it only runs after bothrezineandveth-vpn1areUP. - It sets the default
INPUTpolicy toDROP. - It explicitly allows traffic on
lo,RELATED,ESTABLISHED, and any user-defined ports. - (Optional but recommended) It's configurable via the
.conffile.
Implementation Details
Here are the files I created to solve this, which could be adapted for the project:
1. New script: /usr/sbin/namespaced-wireguard-vpn-firewall
This script reads variables from the .conf file and applies the rules.
#!/bin/bash
# Load config
if [ -f "/etc/namespaced-wireguard-vpn/namespaced-wireguard-vpn.conf" ]; then
source "/etc/namespaced-wireguard-vpn/namespaced-wireguard-vpn.conf"
fi
# --- Ports to open ---
# (These could also be loaded from the .conf file)
VETH_TCP_IN="8080" # e.g., WebUI ports (from host)
VETH_UDP_IN=""
VPN_TCP_IN="44651" # e.g., P2P ports (from public)
VPN_UDP_IN="44651"
VPN_IFACE="$WIREGUARD_NAME"
VETH_IFACE="$TUNNEL_VPN_NAME"
if [ -z "$NETNS_NAME" ] || [ -z "$VPN_IFACE" ] || [ -z "$VETH_IFACE" ]; then
echo "Firewall Error: Core variables (NETNS_NAME, WIREGUARD_NAME, TUNNEL_VPN_NAME) not set." >&2
exit 1
fi
netns_exec="ip netns exec $NETNS_NAME"
case "$1" in
up)
echo "Applying firewall to namespace '$NETNS_NAME'..."
# Wait for interfaces to have the <...UP...> flag
while ! $netns_exec ip link show dev $VPN_IFACE | grep -q "<.*UP.*>"; do sleep 0.2; done
while ! $netns_exec ip link show dev $VETH_IFACE | grep -q "<.*UP.*>"; do sleep 0.2; done
# 1. Set default policy to DROP
$netns_exec iptables -P INPUT DROP
$netns_exec iptables -P FORWARD DROP
$netns_exec iptables -P OUTPUT ACCEPT # Allow outgoing
# 2. Flush old rules
$netns_exec iptables -F INPUT
# 3. Allow essentials
$netns_exec iptables -A INPUT -i lo -j ACCEPT
$netns_exec iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# 4. Open ports on VETH (from host)
for port in $(echo $VETH_TCP_IN | tr ',' ' '); do
[ -n "$port" ] && $netns_exec iptables -A INPUT -i $VETH_IFACE -p tcp --dport $port -j ACCEPT
done
for port in $(echo $VETH_UDP_IN | tr ',' ' '); do
[ -n "$port" ] && $netns_exec iptables -A INPUT -i $VETH_IFACE -p udp --dport $port -j ACCEPT
done
# 5. Open ports on VPN (from public)
for port in $(echo $VPN_TCP_IN | tr ',' ' '); do
[ -n "$port" ] && $netns_exec iptables -A INPUT -i $VPN_IFACE -p tcp --dport $port -j ACCEPT
done
for port in $(echo $VPN_UDP_IN | tr ',' ' '); do
[ -n "$port" ] && $netns_exec iptables -A INPUT -i $VPN_IFACE -p udp --dport $port -j ACCEPT
done
echo "Firewall rules applied. INPUT policy is now DROP."
$netns_exec iptables -L INPUT -n -v
;;
down)
echo "Resetting firewall for namespace '$NETNS_NAME'..."
$netns_exec iptables -P INPUT ACCEPT
$netns_exec iptables -P FORWARD ACCEPT
$netns_exec iptables -F INPUT
;;
*)
echo "Usage: $0 {up|down}"
exit 1
;;
esac2. New service: /etc/systemd/system/namespaced-wireguard-vpn-firewall.service
This service unit ensures the firewall is the last network item to run.
[Unit]
Description=VPN Namespace Firewall
PartOf=namespaced-wireguard-vpn.target
# CRITICAL: Wait for BOTH interfaces to be created
Requires=namespaced-wireguard-vpn-interface.service
Requires=namespaced-wireguard-vpn-tunnel.service
After=namespaced-wireguard-vpn-interface.service
After=namespaced-wireguard-vpn-tunnel.service
[Service]
Type=oneshot
RemainAfterExit=yes
EnvironmentFile=/etc/namespaced-wireguard-vpn/namespaced-wireguard-vpn.conf
ExecStart=/usr/sbin/namespaced-wireguard-vpn-firewall up
ExecStopPost=/usr/sbin/namespaced-wireguard-vpn-firewall down3. Modify: /etc/systemd/system/namespaced-wireguard-vpn.target
Add the new firewall service as a dependency for the main target.
[Unit]
Description=VPN Network Configuration Target
Requires=namespaced-wireguard-vpn-interface.service
Requires=namespaced-wireguard-vpn-netns.service
Requires=namespaced-wireguard-vpn-tunnel.service
+Requires=namespaced-wireguard-vpn-firewall.service
[Install]
WantedBy=multi-user.targetFinal Recommendation for Users
With this change, downstream services should no longer depend on netns.service. They should depend on the main target to ensure the firewall is active before they start.
Example for /etc/systemd/system/qbittorrent.service.d/10-vpn-netns.conf:
[Unit]
-BindsTo=namespaced-wireguard-vpn-netns.service
-After=namespaced-wireguard-vpn-netns.service
+BindsTo=namespaced-wireguard-vpn.target
+After=namespaced-wireguard-vpn.target
JoinsNamespaceOf=namespaced-wireguard-vpn-netns.service
[Service]
PrivateNetwork=yesThis seems like a robust and secure addition to the project. What do you think?
Thanks !