I’ve set up LXD and want to be able to block containers from accessing services on my local network. The most obvious option (at least to me) would be do to do something like
# iptables -A INPUT -i lxdbr0 --src 10.0.0.0/8 -j DROP
# iptables -A OUTPUT -o lxdbr0 --dest 10.0.0.0/8 -j DROP
But this doesn’t work. My containers can still ping 10.42.10.128 (which is a different host to my LXD host). Even if I try prepending the rules like so:
The issue still occurs. And I even tried to set this up with ebtables (I read online that ebtables can better handle the filtering of traffic which originates from bridges) and it still doesn’t work.
# ebtables -A INPUT --proto ipv4 -i lxdbr0 --ip-source 10.0.0.0/8 -j DROP
# ebtables -A OUTPUT --proto ipv4 -o lxdbr0 --ip-destination 10.0.0.0/8 -j DROP
I even tried to mark all the traffic going through lxdbr0:
# iptables -A OUTPUT -o lxdbr0 -j MARK --set-mark 1337
# iptables -A INPUT -i lxdbr0 -j MARK --set-mark 1337
# iptables -A FORWARD -i lxdbr0 -j MARK --set-mark 1337
# iptables -A FORWARD -o lxdbr0 -j MARK --set-mark 1337
# iptables -A OUTPUT -m mark --mark 1337 --dest 10.0.0.0/8 -j DROP
# iptables -A INPUT -m mark --mark 1337 --src 10.0.0.0/8 -j DROP
# iptables -A FORWARD -m mark --mark 1337 --dest 10.0.0.0/8 -j DROP
# iptables -A FORWARD -m mark --mark 1337 --src 10.0.0.0/8 -j DROP
And it still didn’t have an impact. What is the correct way of doing this?
In addition, it might be useful to provide a way to specify raw.iptables for a network so that users can get LXD to auto-insert relevant rules (obviously with substitution of %I or similar for the interface name). The current mix of ipv4.firewall and ipv4.nat options can get a little confusion and it still requires configuring iptables-persistent and making sure there isn’t some weird reloading issue between LXD and systemd.
iptables -I INPUT -i lxdbr0 --src 10.0.0.0/8 -j DROP
iptables -I OUTPUT -o lxdbr0 --dest 10.0.0.0/8 -j DROP
This will only prevent traffic to your host and from the host to the containers, it will not prevent any routed traffic as that’s the FORWARD table.
Adding
iptables -I FORWARD -i lxdbr0 -s 10.0.0.0/8 -j DROP
Though indeed if you don’t want any outside traffic, the best is usually to set ipv4.firewall and ipv4.nat to false and then either have the FORWARD policy to DROP or have a DROP rule in your firewall.
If you don’t have at least ipv4.firewall set to false, LXD will prepend ACCEPT rules which will still let traffic through.
iptables -I FORWARD -i lxdbr0 -d 10.0.0.0/8 -j DROP
works (containers can no longer ping things on my local network but can ping linuxcontainers.org). It isn’t necessary to set ipv4.firewall = false because the DROP rule is matched before the default rules. It is a bit of a shame that users can’t configure iptables rules with a raw.iptables key for LXD-managed network devices. Would there be any interest in something like that?
I’ve been somewhat reluctant to do such a raw.iptables due to the current switch away from traditional iptables to nftables. Not exposing this directly to users means we can do a graceful transition from iptables to nft based on what’s used on the system at the time.
Fair enough. I can set this up with a service that runs after lxd.service is started. I’m not sure what will happen if lxdbr0 gets reset and reconfigured by LXD – it would make sense for the rules to remain in the FILTER table but I don’t know if they would get associated with a new lxdbr0 interface.
By the way (slightly offtopic), have you already looked into how rule injection would work with nftables? From what I understand, it doesn’t have default tables like iptables does, so one e.g. couldn’t call -A INPUT … to target the filter/input chain. I’m probably missing something here, but otherwise I wonder how that would be handled.
Yes, though LXD does do iptables -I which means that after a restart (or reconfigure of lxdbr0) you do end up with the LXD rules above the ones I insert. But I can work around that. Just one tip for anyone following along with this – don’t block 10.0.0.0/8 because that appears to stop DHCP from working correctly for containers (since the default CIDR for lxdbr0 is 10.133.x.x).