Prevent routing of private network to public

Hey there,

recently I got an abuse complaint from my provider for sending packets to private network ranges into their network and I should do necessary precautions to avoid that in future.

Those packages came from an application inside a LXC container.

Blocking the affected IP ranges sounds easy but I realized that this is quite difficult without causing collateral damage.

Anyway my first attempt was to create an additional output chain with netfilter like

       chain OUTPUT3 {
                type filter hook output priority filter - 1; policy accept;
                oifname "eno1" ip daddr 10.0.0.0/8 counter packets 0 bytes 0 reject
                oifname "eno1" ip daddr 192.168.0.0/16 counter packets 0 bytes 0 reject
                oifname "eno1" ip daddr 172.16.0.0/12 counter packets 0 bytes 0 reject

However that did not catch anything whatsoever.
I am not that familiar with nft (yet) but I assume due to the routing LXD with lxdbr0 does it skips the output entirely and goes directly to postrouting.

There though I cannot filter packets.

Next though was using ACLs:

lxc network acl show test1
name: test1
description: ""
egress:
- action: reject
  destination: 172.16.0.0/12
  state: enabled
- action: reject
  destination: 10.0.0.0/8
  state: enabled
- action: reject
  destination: 192.168.0.0/16
  state: enabled
- action: reject
  destination: 100.64.0.0/10
  state: enabled
ingress: []
config: {}
used_by: []

Checking the nft ruleset afterwards it seems like that did something kind a similar to my first attempt.
However this made my containers inaccessible even though I set the default egress action to allow as well.

The last option I had in mind was to add individual iptables/nft rules inside each and every container blocking those ranges. However this then will prevent containers to communicate with each other if I do not whitelist individual addresses of those.

So on the bottom line I am kind a stuck how to prevent packets addressed to private ranges from the public network interface.

Looking forward to some advice.

Cheers

Running Ubuntu 22.04 with LXD 5.5
My current full ruleset of the host machine:

table inet lxd {
        chain pstrt.lxdbr0 {
                type nat hook postrouting priority srcnat; policy accept;
                ip saddr 10.10.10.0/24 ip daddr != 10.10.10.0/24 masquerade
                ip6 saddr fd42:10::/120 ip6 daddr != fd42:10::/120 masquerade
        }

        chain fwd.lxdbr0 {
                type filter hook forward priority filter; policy accept;
                ip version 4 oifname "lxdbr0" accept
                ip version 4 iifname "lxdbr0" accept
                ip6 version 6 oifname "lxdbr0" accept
                ip6 version 6 iifname "lxdbr0" accept
        }

        chain in.lxdbr0 {
                type filter hook input priority filter; policy accept;
                iifname "lxdbr0" tcp dport 53 accept
                iifname "lxdbr0" udp dport 53 accept
                iifname "lxdbr0" icmp type { destination-unreachable, time-exceeded, parameter-problem } accept
                iifname "lxdbr0" udp dport 67 accept
                iifname "lxdbr0" icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-solicit, nd-neighbor-solicit, nd-neighbor-advert, mld2-listener-report } accept
                iifname "lxdbr0" udp dport 547 accept
        }

        chain out.lxdbr0 {
                type filter hook output priority filter; policy accept;
                oifname "lxdbr0" tcp sport 53 accept
                oifname "lxdbr0" udp sport 53 accept
                oifname "lxdbr0" icmp type { destination-unreachable, time-exceeded, parameter-problem } accept
                oifname "lxdbr0" udp sport 67 accept
                oifname "lxdbr0" icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, mld2-listener-report } accept
                oifname "lxdbr0" udp sport 547 accept
        }
}

Packets from lxdbr0 going to the external network will pass through the FORWARD netfilter chain rather than the OUTPUT chain (as OUTPUT is only for packets originating from the LXD host itself not from the instances).

If the destination of the packets is what your ISP is complaining about (rather than the source address of the packets) then one easy way of preventing traffic destined for those address ranges from going out of the external interface without using a firewall is to add unreachable routes to your host.

E.g.

sudo ip route add unreachable 10.0.0.0/8

You can use unreachable, prohibit or blackhole. With the first two giving an ICMP response back to the originator explaining why its blocked and the last one simply dropping the packet.

Any route on your LXD host that is more specific (i.e one towards lxdbr0 for its subnet) will still take precedence.

1 Like

Hey, thanks for the reply.

So my assumption in that matter was correct. Great.

Yep, it is the destination.

To elaborate that a bit further: I had a transmission-daemon running inside a container and due to wrong peer announces (most likely created by peers that communicate via private address ranges) and that caused to those connection attempts to leave the server via physical network interface and ask upstream switch for a route. That triggered the scan detection and created abuse complaint.

Unfortunately I am not that familiar with either nft or routing things. Does this need to be applied to the physical network interfaces (like eno1 or ens1p0 or similar) rather than general?
lxdbr0 is using 10.10.10.1/24 and wireguard (which is also inside a container) 10.1.1.1/24 so those won’t be affected by that change?

Thanks again.

Cheers
Werner

That ip route command is nothing to do with nftables or iptables, it is a routing command.

You would run this on the LXD host and it would ensure any unmatched traffic to those prefixes that would normally go out of the default route would get caught by the route and generate an ICMP unreachable response back to the originator.

You can also get systemd-networkd to configure them at start time by adding these to the *.network file:

[Route]
Destination=10.0.0.0/8
Type=unreachable

[Route]
Destination=172.16.0.0/12
Type=unreachable

[Route]
Destination=192.168.0.0/16
Type=unreachable
1 Like

Thank you. I added a whole bunch of reserved ranges and hopefully this is now not an issue anymore.

# ip r
[...]
unreachable 10.0.0.0/8 
10.10.10.0/24 dev lxdbr0 proto kernel scope link src 10.10.10.1 
unreachable 100.64.0.0/10 
unreachable 169.254.0.0/16 
unreachable 172.16.0.0/12 
unreachable 192.0.0.0/24 
unreachable 192.0.2.0/24 
unreachable 192.88.99.0/24 
unreachable 192.168.0.0/16 
unreachable 198.18.0.0/15 
unreachable 198.51.100.0/24 
unreachable 203.0.113.0/24 
unreachable 224.0.0.0/4 
unreachable 240.0.0.0/4

Cheers

1 Like