How to restrict network access of LXC container
Table of Contents
So let’s say you have to run that sketchy or untrusted executable/project/binary/whatever and you want to do in a safe manner. Wide range of applicable solutions based on virtualization and contenerization technologies allow secure examination of suspicious software in a sandboxed environment.
If you’re a Linux user, you’d probably point your attention to LXC[0]. Just a few commands to bring up shiny new environment based on a distro selected from wide variety of available options, running on the host kernel with no excessive emulation overhead.
That alone provides decent separation from whatever you don’t want an object of research to touch in your workstation. But what about connectivity?
Permitting an unknown actor unrestricted access to the Internet or LAN poses serious threat. At the same time, the container is going to need it to some extent, for installing packages or remote access from host. Wouldn’t it be convenient to switch the network access restrictions of the container on and off at any time?
#
Use a NAT bridge
Fairly simple way to achieve it is to use iptables in conjunction with lxc-net. Using lxc-net assumes running the container behind a NAT bridge. If you want to bridge the container to LAN, the tool of your choice might provide a built-in mechanism to achieve similar effect[1]. In my opinion, having the container behind local NAT provides higher level of resilience and control. In case of having to expose the container to wider publicity, you can always redirect traffic from host to lxc.
# iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 2221 -j DNAT --to-destination 10.0.3.100:22
The example rule above forwards TCP traffic origination on port 2221
of eth0
interface to lxc SSH server running on 10.0.3.100:22
[2].
Arch Linux Wiki article[2] I took this rule from serves as a pretty good introduction to using lxc-net and lxc as whole. There’s no point in me repeating most of its content, so give it a read if you’re not familiar with lxc. At the end of a day, you should have a lxc container running behind NAT managed by lxc-net.
#
Firewall setup
Assuming that you have used default configuration, CIDR of your lxc subnet is 10.0.3.0/24
. Running lxc-ls -f
returns a list of containers similar to this:
NAME STATE AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED
lxc0 RUNNING 0 - 10.0.3.250 - false
lxc1 STOPPED 0 - - - false
We’ll be imposing restrictions on lxc0
container. Take note of its IP address – 10.0.3.250
in my case.
You should also know name of the interface managed by lxc-net. It’s mentioned in the config file /etc/lxc/default.conf
by lxc.net.0.link
key and by default it’s lxcbr0
.
If you run iptables -L FORWARD -v
you should see two rules related to it at top of the chain.
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- any lxcbr0 anywhere anywhere
0 0 ACCEPT all -- lxcbr0 any anywhere anywhere
They allow all kinds of traffic on lxcbr0
interface in any direction.
We want to forbid ANY incoming and outgoing traffic from the container except for traffic between the container and host. The address of the host can be obtained from /etc/default/lxc-net
file under LXC_ADDR
key and its default value is 10.0.3.1
.
Therefore, this rule should be applied on host iptables:
# iptables -I FORWARD 1 -i lxcbr0 ! -d 10.0.3.1 -j DROP
It can be explained in a following way:
-I FORWARD 1
inserts the rule at index 1 ofFORWARD
chain-i lxcbr0
applies the rule to traffic received on interface calledlxcbr0
! -d 10.0.3.1
applies the rule to traffic directed to any IP address, except for10.0.3.1
which is host IP-j DROP
blocks all the traffic matching criteria above
Additionally, you might block inbound traffic:
# iptables -I FORWARD 2 -i lxcbr0 ! -s 10.0.3.1 -j DROP
In order to test the solution, try connecting to the container’s shell via SSH. It should work fine. Now try pinging any host inside or outside LAN. None of the packets should go through.
Restriction can be lifted at any time with this command:
# iptables -D FORWARD 1
It removes the rule from chain FORWARD specified by the index. Note that you need to run it twice if you applied both inbound and outbound blocking rules.
#
Allowing multiple containers to communicate
If you want to run several containers in the same subnet and allow the traffic to occur between them, but not leak outside, you can modify the rule to drop anything except for packets sourced and addressed to 10.0.3.0/24
subnet.
This turns the first rule into:
# iptables -I FORWARD 1 -i lxcbr0 ! -d 10.0.3.0/24 -j DROP
Equivalent modification of -d
argument can be applied to the outbound rule.
#
Links
[0]: https://linuxcontainers.org/
[1]: https://wiki.archlinux.org/title/Network_bridge
[2]: https://wiki.archlinux.org/title/Linux_Containers#Example_iptables_rule