port forwarding to application in network namespace with vpn

Interconnecting network namespace with main namespace always bothers me. The reason I usually create a namespace is because I want it isolated. Depending on what it is you are trying to achieve with namespaces creating interconnects can defeat that purpose.

But even isolated I still want to poke it over the network, for convenience.

This solution lets you keep isolation and forward some connections to it anyway. You don't need to create all that network between the two network namespaces just to forward one port. Run this in the namespace where you want to accept connections. Must be run as root for ip netns exec to work.

socat tcp-listen:8112,fork,reuseaddr \
  exec:'ip netns exec myvpn socat STDIO "tcp-connect:127.0.0.1:8112"',nofork

It listens for connections in one network namespace where you run it, on port 8112, then connected client gets exec to run ip netns exec myvpn ... to execute the rest inside the myvpn network namespace, then once inside the myvpn network namespace it creates second connection again with another socat.

Or run it as systemd service

This also uses socat.

Create service config file /etc/systemd/system/deluge-web-netns.service with content:

[Unit]
Description=Forwarder to deluge-web in netns
After=network-online.target
#Requires=deluge-web.service
#After=deluge-web.service

[Service]
Type=simple

ExecStart=socat tcp-listen:8112,fork,reuseaddr exec:'ip netns exec vpn-netns socat STDIO "tcp-connect:127.0.0.1:8112"',nofork
#User=deluge
#Group=deluge
SyslogIdentifier=deluge-web-fwd

Restart=on-failure

# Time to wait before forcefully stopped.
TimeoutStopSec=300

[Install]
WantedBy=multi-user.target

Consider setting correct values and enabling the directives Requires and After.

Then enable and start with commands:

systemctl daemon-reload
systemctl enable deluge-web-netns
systemctl start  deluge-web-netns

Or run it under xinetd

This also uses socat.

create conf file, e.g. /etc/xinetd.d/deluge-web-fwd with content

service deluge-web-vpn-netns
{
    type            = UNLISTED
    socket_type     = stream
    protocol        = tcp
    port            = 8112
    flags           = IPv4 KEEPALIVE
    wait            = no
    user            = root
    server          = /sbin/ip
    server_args     = netns exec vpn-netns socat STDIO tcp-connect:127.0.0.1:8112
}

Restart xinetd

service xinetd restart

Program ncat can also be used similar to socat.


I've always had issues with iptables redirections (probably my fault, I'm pretty sure it's doable). But for a case like yours, it's IMO easier to do it in user-land without iptables.

Basically, you need to have a daemon in your "default" workspace listening on TCP port 8112 and redirecting all traffic to 10.200.200.2 port 8112. So it's a simple TCP proxy.

Here's how to do it with socat:

socat tcp-listen:8112,reuseaddr,fork tcp-connect:10.200.200.2:8112

(The fork option is needed to avoid socat from stopping after the first proxied connection is closed).

EDIT: added reuseaddr as suggested in the comments.

If you absolutely want to do it with iptables, there's a guide on the Debian Administration site. But I still prefer socat for more advanced stuff -- like proxying IPv4 to IPv6, or stripping SSL to allow old Java programs to connect to secure services...

Beware however that all connections in Deluge will be from your server IP instead of the real client IP. If you want to avoid that, you will need to use a real HTTP reverse proxy that adds the original client IP to the proxied request in a HTTP header.


For deluge here is my solution. No need for iptables. Here are the steps:

  1. Start your openvpn tunnel
  2. Create namespace and bring your openvpn tunnel there:
ip netns add $NS
# Wait for the TUN to come up
while [[ $(ip route|grep $TUN|wc -l) == 0 ]]; do sleep 1; done
MY_IP=$(ip addr show $TUN|grep inet|cut -d' ' -f6|cut -d'/' -f1)
# The way you extract gateway IP might be different for your openvpn connection
GATEWAY_IP=$MY_IP
# jail my $TUN (VPN interface) into the namespace
ip link set $TUN netns $NS
# Bring the interface up with a subnet (equivalent to the one given to me by VPN server)
ip netns exec $NS ifconfig $TUN $MY_IP/24 up
# Bring loopback up
ip netns exec $NS ifconfig lo 127.0.0.1/8 up
# Set up remote gateway (your pointtopoint VPN IP address)
ip netns exec $NS route add default gw $GATEWAY_IP
  1. Establish veth connection between your default namespace and the one you've created:
# Set up veth interfaces for communication between namespaces
ip link add veth0 type veth peer name veth1
# Move the second veth to your namespace
ip link set veth1 netns $NS
# give an IP from unused IP range to first veth
ifconfig veth0 10.1.1.1/24 up
# And the second one
ip netns exec $NS ifconfig veth1 10.1.1.2/24 up
# TODO: set up a bridge between veth1 and eth interface to let it communicate with LAN
# Set up DNS client. ip netns will emulate /etc/resolv.conf using this file:
mkdir -p /etc/netns/$NS
echo "nameserver 8.8.4.4" >/etc/netns/$NS/resolv.conf
  1. Run your deluged in the $NS and your deluge-web in your default namespace. Point deluge-web to the 10.1.1.2 veth IP address, where deluged will be listening for its connection.

Voila! You've got deluged secured behind the VPN while your deluge-web is freely accessible on your home network