nftables: origin sport after dstnat
Hi!
I want to use nftables along with Docker. Docker itself does not support nftables, so I use the nf_tables backend for iptables. This way, Docker uses iptables commands to setup all needed chains and rules. So far so good.
The following chains will be setup by Docker (I shorten it for the relevant information):
table ip filter {
chain FORWARD {
type filter hook forward priority filter; policy drop;
counter packets 6 bytes 360 jump DOCKER-USER
}
chain DOCKER {
iifname != "docker0" oifname "docker0" ip daddr 172.17.0.2 tcp dport 5000 counter packets 0 bytes 0 accept
}
chain DOCKER-USER {
}
}
table ip nat {
chain DOCKER {
iifname "docker0" counter packets 0 bytes 0 return
iifname != "docker0" tcp dport 8448 counter packets 6 bytes 360 # xt_DNAT
}
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
# xt_addrtype counter packets 6 bytes 360 jump DOCKER
}
}Here I started a Docker container with "-p 8448:5000" (map the host port 8448 to the docker internal port 5000). If a package enters, it will land in the PREROUTING NAT chain (dstnat), jump to the DOCKER chain and here the dnat happen. This is all set up from the Docker daemon itself. The only way for me to intercept traffic is the DOCKER-USER chain (FORWARD FILTER), which will be created by Docker and will be put on top of all other rules in this chain.
Now I want to stablish a whitelist for host ports. Meaning: I want to control which ports are "open" from the outside, regardless what Docker is doing.
My first idea was to use the DOCKER-USER chain and add a simple rule which will drop all traffic which is not coming from the whitelisted port list:
tcp sport != {
8448,
} drop
The problem is that the DOCKER-USER chain is behind the dnat, so I can't use "sport", because it is already a random port from the dnat :(
With a trace in that chain, I got this (sport is 57484, a random port) and the filter rule does not work.
trace id 2a3fb2fd ip filter DOCKER-USER packet: iif "eth0" oif "docker0" ether saddr 52:54:00:8e:37:c7 ether daddr 52:54:00:9c:e5:b7 ip saddr 192.168.122.1 ip daddr 172.17.0.2 ip dscp cs0 ip ecn not-ect ip ttl 63 ip id 57921 ip length 60 tcp sport 57484 tcp dport 5000 tcp flags == syn tcp window 64240 trace id 2a3fb2fd ip filter DOCKER-USER rule tcp sport != 8448
counter packets 6 bytes 360 drop (verdict drop)
Is there a way to somehow access the origin sport (8448 in my case), so I can filter for it in the FORWARD chain?
Thanks!
Aaron
Quoted 6 lines · Aaron Fischer
The problem is that the DOCKER-USER chain is behind the dnat, so I can't use "sport", because it is already a random port from the dnat :(
With a trace in that chain, I got this (sport is 57484, a random port) and the filter rule does not work.
Quoted 6 lines
trace id
2a3fb2fdip filter DOCKER-USER packet: iif "eth0" oif "docker0" ether saddr 52:54:00:8e:37:c7 ether daddr 52:54:00:9c:e5:b7 ip saddr 192.168.122.1 ip daddr 172.17.0.2 ip dscp cs0 ip ecn not-ect ip ttl 63 ip id 57921 ip length 60 tcp sport 57484 tcp dport 5000 tcp flags == syn tcp window 64240 trace id2a3fb2fdip filter DOCKER-USER rule tcp sport != 8448
counter packets 6 bytes 360 drop (verdict drop)Is there a way to somehow access the origin sport (8448 in my case), so I can filter for it in the FORWARD chain?
Yes, the original addresses are stored in conntrack: 'ct original proto-src 8448'
Quoted 6 lines · On Wed, 7 Dec 2022 12:29:00 +0100 Florian Westphal <[email protected]> wrote:
Quoted 2 lines · Aaron Fischer
Is there a way to somehow access the origin sport (8448 in my case), so I can filter for it in the FORWARD chain?
Yes, the original addresses are stored in conntrack: 'ct original proto-src 8448'
Thank you for this valuable hint! I've tested it with this bare minimum config:
alpine-test:/etc# cat nft-ct.nft #!/usr/sbin/nft -f
flush ruleset
table ip filter {
chain INPUT {
type filter hook input priority filter
policy accept
}
chain FORWARD {
type filter hook forward priority filter
policy drop
ct original proto-src 8448 accept
}
}But the config seems invalid:
alpine-test:/etc# nft -c -f /etc/nft-ct.nft
/etc/nft-ct.nft:14:31-34: Error: Can't parse symbolic invalid
expressions ct original proto-src 8448 accept
^^^^Not sure why. Various examples and the man page also show it that way you mentioned. The sourcecode gives me not much more insight how the syntax should be. Why is 8448 an "invalid" type?
I use the latest version of the nft tool (v1.0.5).
Aaron
Quoted 4 lines · Aaron Fischer
chain FORWARD {
type filter hook forward priority filter
policy drop
ct original proto-src 8448 accept
You need to prepend the l4 protocol that needs to be matched.
meta l4proto tcp ct original proto-src 8448 accept
Quoted 1 line
Why is 8448 an "invalid" type?
Because nft can't infer it from the available info.