Hi all, quick question to see if anybody has done any port knocking in a firewalld compatible way?
I've set up a proof of concept directly in nftables:
table inet knock { set set1 { type ipv4_addr size 65535 timeout 5s }
set set2 { type ipv4_addr size 65535 timeout 5s }
set set3 { type ipv4_addr size 65535 timeout 5s }
chain knock { type filter hook input priority raw; policy accept; ip saddr @set2 tcp dport 789 log prefix "KNOCK3 " level info add @set3 { ip saddr } return ip saddr @set1 tcp dport 456 log prefix "KNOCK2 " level info add @set2 { ip saddr } return tcp dport 123 log prefix "KNOCK1 " level info add @set1 { ip saddr } return ip saddr @set3 tcp dport 22 log prefix "ALLOWED " level info accept } }
To do the same thing in a firewalld compatible way, I was thinking that the firewalld ssh allow rule (in nft table inet firewalld chain filter_IN_public_allow) could refer to @set3 but then I would also need to have the add @set3 (and @set3 itself) be defined in the same inet firewalld table. I can define that by hand with nft adding the chain to table inet firewalld, but the problem with doing that is any firewall-cmd --reload will clear out my "knock" chain.
Doing firewall-cmd to define sets with timeouts also seems a bit problematic (I'm in CentOS 8 BTW) as I can define a set with a timeout:
firewall-cmd --permanent --new-ipset set3 --type=hash:ip --family=inet --option=timeout=5 firewall-cmd --reload
however listing the set shows it doesn't have a timeout:
nft list table inet firewalld|grep -A4 set set set3 { type ipv4_addr flags interval }
and in fact when I add something to the running set it doesn't disappear after 5 seconds:
# firewall-cmd --ipset=set3 --add-entry=1.2.3.4 success
# nft list table inet firewalld|grep -A4 set set set3 { type ipv4_addr flags interval elements = { 1.2.3.4 } }
firewalld documentation is clear that you can't list the set members for a set with timeouts since that's all handled by the kernel, which is fine but strange I guess, however it doesn't seem to be that the timeout is actually getting set into the nftables set. (And actually, firewall-cmd could just do "nft list set inet firewalld set4" and extract the elements = {} list so possibly that's a future enhancement.)
But I digress. Has anybody done any port knocking sort of thing in a firewalld compatible way here? I'm interested in hearing some ideas or what has worked for people.
Thanks. -nik
On Fri, Oct 16, 2020 at 12:11:06PM -0000, nik wrote:
Hi all, quick question to see if anybody has done any port knocking in a firewalld compatible way?
I've set up a proof of concept directly in nftables:
table inet knock { set set1 { type ipv4_addr size 65535 timeout 5s }
set set2 { type ipv4_addr size 65535 timeout 5s } set set3 { type ipv4_addr size 65535 timeout 5s } chain knock { type filter hook input priority raw; policy accept; ip saddr @set2 tcp dport 789 log prefix "KNOCK3 " level info add @set3 { ip saddr } return ip saddr @set1 tcp dport 456 log prefix "KNOCK2 " level info add @set2 { ip saddr } return tcp dport 123 log prefix "KNOCK1 " level info add @set1 { ip saddr } return ip saddr @set3 tcp dport 22 log prefix "ALLOWED " level info accept }
}
To do the same thing in a firewalld compatible way, I was thinking that the firewalld ssh allow rule (in nft table inet firewalld chain filter_IN_public_allow) could refer to @set3 but then I would also need to have the add @set3 (and @set3 itself) be defined in the same inet firewalld table. I can define that by hand with nft adding the chain to table inet firewalld, but the problem with doing that is any firewall-cmd --reload will clear out my "knock" chain.
firewalld will only ever touch/clear tables named "firewalld". You could use your own tables, e.g. "foobar".
Doing firewall-cmd to define sets with timeouts also seems a bit problematic (I'm in CentOS 8 BTW) as I can define a set with a timeout:
firewall-cmd --permanent --new-ipset set3 --type=hash:ip --family=inet --option=timeout=5 firewall-cmd --reload
however listing the set shows it doesn't have a timeout:
nft list table inet firewalld|grep -A4 set set set3 { type ipv4_addr flags interval }
and in fact when I add something to the running set it doesn't disappear after 5 seconds:
# firewall-cmd --ipset=set3 --add-entry=1.2.3.4 success
# nft list table inet firewalld|grep -A4 set set set3 { type ipv4_addr flags interval elements = { 1.2.3.4 } }
This looks like a bug. Please file an issue upstream: https://github.com/firewalld/firewalld/issues
firewalld documentation is clear that you can't list the set members for a set with timeouts since that's all handled by the kernel, which is fine but strange I guess, however it doesn't seem to be that the timeout is actually getting set into the nftables set. (And actually, firewall-cmd could just do "nft list set inet firewalld set4" and extract the elements = {} list so possibly that's a future enhancement.)
But I digress. Has anybody done any port knocking sort of thing in a firewalld compatible way here? I'm interested in hearing some ideas or what has worked for people.
I think it should work if you do the port knocking out of band of firewalld. Just make sure your nftables chain hooks have a _higher_ precedence (_lower_ value) than fireawalld. This guarantees they're evaluated before firewalld.
Thanks Eric. I was a bit worried about having my new table/chain doing the actual blocking and then having firewalld just allow the port in case I did something goofy in my table. Plus things could fail open if my table completely broke. At least having a set reference would mean that I couldn't mess things up that badly and firewalld would still be in control, visible, etc.
I've filed the issue - https://github.com/firewalld/firewalld/issues/699
-nik
firewalld-users@lists.fedorahosted.org