Skip to content

Instantly share code, notes, and snippets.

@josepereza
Last active December 8, 2019 08:57
Show Gist options
  • Save josepereza/9c3594cc5fa5fe2b5d076aee42848df3 to your computer and use it in GitHub Desktop.
Save josepereza/9c3594cc5fa5fe2b5d076aee42848df3 to your computer and use it in GitHub Desktop.
Routing lxc container ports

Routing ports for lxc containers

Reference: Forward port to LXC guest using UFW, answer by Steve Zahn. March 17, 2014.

The lxd service can be used to manage lxc system containers on Linux hosts, very much like Docker does for application containers. One Docker feature that LXD lacks is a native parameter for routing container ports so they can be accessed on the network beyond the lxd host. Routing using the iptables command

On an lxd host protected by the standard iptables kernel firewall, the following command will route external traffic to a specific port of a specific container:

$ sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 8080 -j DNAT --to 10.1.0.5:80

In the example above the lxd host's address is assigned to an interface named "ens3", the container has an address of 10.1.0.5 and the target service is using port 80 at that address. The port external traffic will be routed through is 8080. If the host's address were assigned to a bridge called "br0", then "-i" would be set to that name instead:

$ sudo iptables -t nat -A PREROUTING -i br0 -p tcp --dport 8080 -j DNAT --to 10.1.0.5:80

Note that routing can be done for any port not already being used by another service on the lxd host.


Esto es iptables, pueden usar todos los otros parámetros que conocemos, por ejemplo, si solo deseamos redireccionar tráfico de una IP en específica, sería agregando -s … por ejemplo redireccionaré solo el tráfico que venga desde 10.10.0.51:

iptables -t nat -A PREROUTING -p tcp -s 10.10.0.51 --dport 80 -j DNAT --to-destination 10.10.0.2:443

O bien una red entera (/24):

iptables -t nat -A PREROUTING -p tcp -s 10.10.0.0/24 --dport 80 -j DNAT --to-destination 10.10.0.2:443

También podemos especificar la interfaz de red con -i :

iptables -t nat -A PREROUTING -p tcp -i eth1 --dport 80 -j DNAT --to-destination 10.10.0.2:443

Routing using ufw

Directly invoking the iptables command will not work when the host firewall is controlled by ufw. Instead, some configuration changes to ufw need to be made. To configure ufw:

First, be sure to allow access to the port to be exposed:

$ sudo ufw allow 8080

Next, edit /etc/default/ufw to set MANAGE_BUILTINS=yes (the default is "no").

Then edit /etc/ufw/before.rules to insert the following lines at the top of the file before the *filter section:
*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -i ens3 -p tcp --dport 8080 -j DNAT --to 10.1.0.5:80
COMMIT

If the interface were a bridge named "br0", use the following instead:

*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -i br0 -p tcp --dport 8080 -j DNAT --to 10.1.0.5:80
COMMIT
Finally, restart ufw:

$ sudo ufw disable && sudo ufw enable

Testing rule iptables command

This command will list all nat rules, of which your new rule is part:

$ sudo iptables -L -n -t nat

Reference: How to check PREROUTING list. Answer by Yuriy Zhigulskiy. January 5, 2017. nmap port scan

I like to use nmap for quickly scanning to see what ports are open on a host. First, check the container:

$ sudo nmap -p 80 10.1.0.5

Then the lxd host:

$ sudo nmap -p 8080 mylxdhost

Removing the rule without rebooting

Where ufw is managing iptables, you'll need to edit before.rules to remove the PREROUTING directive and then restart ufw. Otherwise, first list all nat rules by line number:

$ sudo iptables -t nat -L --line-numbers

Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination         
1    DNAT       tcp  --  anywhere             anywhere             tcp dpt:http-alt to:10.1.0.5:80

Then delete by rule number:

$ sudo iptables -t nat -D PREROUTING 1

Reference: SvennD, Remove iptables PREROUTING NAT rule. March 14, 2017.

Forward port 80 and 443 from WAN to container

Here are the commands to run on the host. You only need the name of the container to perform the setup. In my example I’ll use mycontainer.

lxc config device add mycontainer myport80 proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:80

lxc config device add mycontainer myport443 proxy listen=tcp:0.0.0.0:443 connect=tcp:127.0.0.1:443

What we do here is:

We add a proxy device on container mycontainer, giving it an arbitrary name (myport80).
We set it up to listen on all (0.0.0.0) network interfaces on the host, port 80. You can change the IP address to something more specific, if you want to.
We set it up to make connections to the container mycontainer on the 127.0.0.1 (localhost) interface at port 80. PREVIOUS GUIDES SUGGESTED TO USE localhost. DOES NOT WORK ANYMORE; PLEASE USE 127.0.0.1 INSTEAD!

To verify that LXD is listening on port 80 (http), run

$ sudo lsof -i -n | grep http
lxd       2157            root    5u  IPv6 668213      0t0  TCP *:http (LISTEN)
lxd       2157            root    8u  IPv6 668213      0t0  TCP *:http (LISTEN)

To remove a proxy device, run

lxc config device remove mycontainer myport80

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment