UDP NAT for LXD container

I’ve succesfully used iptables to NAT to my containers for the tcp protocol. However I struggle to make it work for a range of UDP ports. Here’s the bash script I used to setup port forwarding in TCP:

#!/bin/bash
HOST_IP=192.168.1.2
CONTAINER_IP=10.10.10.3
CONTAINER_NETWORK=10.10.10.0/24
for PORT in 80 443
do
echo Setting iptables for host $HOST_IP on port $PORT to $CONTAINER_IP…
iptables -t nat -A PREROUTING -d $HOST_IP/32 -p tcp -m multiport --dports $PORT -j DNAT --to-destination $CONTAINER_IP:$PORT
iptables -t nat -A OUTPUT -o lo -p tcp -m multiport --dports $PORT -j DNAT --to-destination $CONTAINER_IP:$PORT
iptables -t nat -A POSTROUTING -s $CONTAINER_NETWORK -o lxdbr0 -p tcp -m multiport --dports $PORT -j SNAT --to-source $HOST_IP
done

Now I would also like to forward UDP ports 16384:32768 to my container. Any suggestions?

I have written a tool to facilitate this. It specifies the protocol with the -p option to iptables. Please test it, and tell me if you needed to adjust anything to make it work for your use case. Note that it can also parse the output of lxc-ls or lxc list to fetch the IP based on the name of the container. You would specify the container with a URI, for example lxc://ubuntu-c1 or lxd://debian-c2.

@CameronNemo, the problem with your script is I have a large range of ports to open (16384 to 32768) so calling your script with a for loop takes an eternity…

I just specified a port range:

portctl i -4 tcp lxc://dropkick 22-28 22:28

It worked out fine. I tested running ssh on port 23.

Unfortunatly, your script don’t work with UDP (single or multiple ports). How I test:

  1. sudo ./portctl i -4 udp lxd://my-container 12000 12000
  2. lxc exec my-container bash
  3. netcat -u -l 12000

On another workstation run:

  1. netcat -u my-host 12000

Then in the container type something like “test”: it should appear in the remote workstation. This works fine for TCP but not UDP. If you test with TCP, remove the “-u” option in netcat.

I found a solution, which is probably a better approach: use the new proxy feature of LXD (available since LXD v3.2). It supports TCP and UDP (single and multiple ports) as well as IPv4 and IPv6. My setup looks like this:

lxc config device add my-container proxy1 proxy listen=tcp:192.168.1.2:80 connect=tcp:10.10.10.3:80 bind=host
lxc config device add my-container proxy2 proxy listen=tcp:192.168.1.2:443 connect=tcp:10.10.10.3:443 bind=host
lxc config device add my-container proxy3 proxy listen=tcp:192.168.1.2:1935 connect=tcp:10.10.10.3:1935 bind=host
lxc config device add my-container proxy4 proxy listen=tcp:192.168.1.2:7443 connect=tcp:10.10.10.3:7443 bind=host
lxc config device add my-container proxy5 proxy listen=udp:192.168.1.2:16384-32768 connect=udp:10.10.10.3:16384-32768 bind=host