Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFE]: nftables action - allow multiple chain hooks #3443

Open
Xyaren opened this issue Jan 11, 2023 · 4 comments
Open

[RFE]: nftables action - allow multiple chain hooks #3443

Xyaren opened this issue Jan 11, 2023 · 4 comments

Comments

@Xyaren
Copy link

Xyaren commented Jan 11, 2023

Feature request type

Extend the nftables action to hook to support multiple chains. i.e. 'input' AND 'forward' while using a single address set.

Description

In some cases you might need to block within multiple chains, i.e. if one is using docker containers.
To block any traffic going to the containers, a filter with the forward hook is required.

Currently this requires creating two actions, which will lead to two address sets being created, which are identical besides their name.

Configuration Proposal:

action = nftables[type=allports,chain_hook=input,forward]

Considered alternatives

Defining multiple actions:

action = nftables[type=allports] #default chain_hook=input
              nftables[type=allports,name=f2b-fwd-sshd,chain=f2b-chain-fwd,chain_hook=forward]

Unfortunately this will create multiple identical address sets in nftables that have to be updated on each ban/unban.
Marked with <--- Identical !!!
nftables list ruleset


table inet f2b-table {
        set addr-set-sshd {
                type ipv4_addr
                elements = { 1.14.126.238, 1.15.138.95,
                             1.117.144.153, 1.162.34.157,
                             ...
                             223.197.151.55 }
        }

        set addr-set-f2b-fwd-sshd {
                type ipv4_addr
                elements = { 1.14.126.238, 1.15.138.95,
                             1.117.144.153, 1.162.34.157,
                             ...
                             223.197.151.55 }
        }

        set addr6-set-sshd {
                type ipv6_addr
        }

        set addr6-set-f2b-fwd-sshd {
                type ipv6_addr
        }

        chain f2b-chain {
                type filter hook input priority filter - 1; policy accept;
                meta l4proto tcp ip saddr @addr-set-sshd reject with icmp port-unreachable #<--- Identical !!!
                meta l4proto tcp ip6 saddr @addr6-set-sshd reject with icmpv6 port-unreachable
        }

        chain f2b-chain-fwd {
                type filter hook forward priority filter - 1; policy accept;
                meta l4proto tcp ip saddr @addr-set-f2b-fwd-sshd reject with icmp port-unreachable #<--- Identical !!!
                meta l4proto tcp ip6 saddr @addr6-set-f2b-fwd-sshd reject with icmpv6 port-unreachable 
        }
}

Any additional information

@sebres
Copy link
Contributor

sebres commented Jan 11, 2023

I don't think the chain can have more than 1 hook (must be verified yet), so firstly the idea were:

  • either to use the same set in both chains (probably preferred way)
  • or to do something like that:
        chain f2b-chain {
                type filter hook input priority filter - 1; policy accept;
-               meta l4proto tcp ip saddr @addr-set-sshd reject with icmp port-unreachable
-               meta l4proto tcp ip6 saddr @addr6-set-sshd reject with icmpv6 port-unreachable
+               jump f2b-chain-doing
        }

        chain f2b-chain-fwd {
                type filter hook forward priority filter - 1; policy accept;
-               meta l4proto tcp ip saddr @addr-set-f2b-fwd-sshd reject with icmp port-unreachable
-               meta l4proto tcp ip6 saddr @addr6-set-f2b-fwd-sshd reject with icmpv6 port-unreachable 
+               jump f2b-chain-doing
        }

        chain f2b-chain-doing {
                meta l4proto tcp ip saddr @addr-set-sshd reject with icmp port-unreachable
                meta l4proto tcp ip6 saddr @addr6-set-sshd reject with icmpv6 port-unreachable
        }

@Xyaren
Copy link
Author

Xyaren commented Jan 11, 2023

I was thinking the same.
The first way is probably easier based on the current implementation of nftables-common.conf

My expertise on this is pretty low unfortunately, therefore I made this PR instead of implementing it myself.

@sebres
Copy link
Contributor

sebres commented Jan 11, 2023

Yep, as assumed 2 hooks seem to be impossible, PoC:

# nft add table inet f2b-table
# nft -- add chain inet f2b-table f2b-chain \{ type filter hook \{ input,forward \} priority -1 \; \}
Error: syntax error, unexpected '{', expecting string
add chain inet f2b-table f2b-chain { type filter hook { input,forward } priority -1 ; }
                                                      ^
# nft -- add chain inet f2b-table f2b-chain \{ type filter hook "input,forward" priority -1 \; \}
Error: syntax error, unexpected comma, expecting priority
add chain inet f2b-table f2b-chain { type filter hook input,forward priority -1 ; }
                                                           ^
# nft -- add chain inet f2b-table f2b-chain \{ type filter hook input priority -1 \; \}
# nft -- add chain inet f2b-table f2b-chain \{ type filter hook forward priority -1 \; \}
Error: Could not process rule: File exists
add chain inet f2b-table f2b-chain { type filter hook forward priority -1 ; }
                         ^^^^^^^^^

Neither two hooks can be specified directly as list in single command, nor it allows to specify 2 hooks one after the other.

Also with jump or goto it doesn't work as expected for some reason:

# nft add rule inet f2b-table f2b-chain-fwd goto f2b-chain
Error: Could not process rule: Operation not supported
add rule inet f2b-table f2b-chain-fwd goto f2b-chain
                                           ^^^^^^^^^

The only way I see at the moment is to use a common set for both chains.
Just this could be not so simply achieved with pure jail configuration, without to implement special action, repeating the whole stuff for 2nd chain with the same set name.
Jump/goto could extremely simplify that (so it'd be possible within jail without new action), but I don't know the correct way to do that at the moment.

@sebres
Copy link
Contributor

sebres commented Jan 11, 2023

Just to provide a workaround for possibility to share common set for 2 actions (with different hooks):

[jail]
banaction = nftables
action = %(action_)s[actname="<name>-inp", chain="f2b-chain", chain_hook="input"]
         %(action_)s[actname="<name>-fwd", chain="f2b-chain-fwd", chain_hook="forward", actionban="", actionunban=""]

(note that both actions get different actname, but retain the same name, so the set name based on <name> used in actions remains unchanged).
This has only one disadvantage - on shutdown or jail stop it would try to delete the set twice and then write an error in fail2ban.log:

... Error: Could not process rule: Device or resource busy
... delete set inet f2b-table addr-set-jail

because the set is still referenced in the rule of 1st chain as 2nd action trying to remove it (after rule removal).
Besides that it works as expected.


As for the RFE, let's retain it open if I (or someone else) would find better approach or begin to work on nft-action enhancements.
For the record - #3125, #2767

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants