Using proxy device to forward network connections from host to container in NAT mode

Whilst it is possible to use manual iptables rules to forward ports from outside to containers. It works, but not well, because it is impossible to connect from container to outside IP or connect from the host itself to outside IP’s ports (like my own DNS server).

Here are the steps to forward port from outside to container properly using LXD’s proxy device in NAT mode (which automates the iptables rules required):

First, enable br_netfilter kernel module. This step is optional, but without it your container will not be able to connect to the proxy’s external address and have connections forwarded back to it. However caution should be taken with this step on systems running existing instances and a firewall as enabling br_netfilter will cause all bridge traffic to flow through the firewall, and may cause some traffic to be unexpectedly blocked if an existing firewall rule matches bridge traffic that wasn’t previously filtered.

In Debian 10 you can do this:

modprobe br_netfilter
echo br_netfilter > /etc/modules-load.d/lxd.conf

Second, set a static IP address on your container’s NIC. Look up your current dynamic address:

lxc list | grep testcontainer
| testcontainer    | RUNNING | 172.16.172.113 (eth0) | fd42:dad8:c4ad:e744:216:3eff:fecf:5770 (eth0) | CONTAINER | 0 |     

And set that address as static IP:

Note: That when setting a static IPv6 address you will need to ensure that the parent managed network has ipv6.dhcp.stateful enabled, otherwise setting a static IPv6 address will fail.

lxc stop testcontainer
lxc config device override testcontainer eth0 ipv4.address=172.16.172.113 ipv6.address=fd42:dad8:c4ad:e744:216:3eff:fecf:5770
lxc start testcontainer

Third, add proxy device to your container, one per port:

lxc config device add testcontainer proxyv4 proxy nat=true listen=tcp:a.b.c.d:7777 connect=tcp:0.0.0.0:7777
 Device proxyv4 added to testcontainer
lxc config device add testcontainer proxyv6 proxy nat=true listen=tcp:[2001:db8::1]:7777 connect=tcp:[::]:7777
 Device proxyv6 added to testcontainer

Here a.b.c.d and 2001:db8::1 is your external IP address and proxyvX is just a string identifier, so error messages about missing device are kinda misleading, it’s not a real network interface.

Now you can telnet to your external IP anywhere; inside the container, inside the host or outside, all iptables rules are created automatically.

To remove that port forward use remove command:

lxc config device remove testcontainer proxyv4
  Device proxyv4 removed from testcontainer
lxc config device remove testcontainer proxyv6
  Device proxyv6 removed from testcontainer

For more information, see the proxy device documentation: https://linuxcontainers.org/lxd/docs/master/instances#type-proxy