Score:1

nftables (nft) chain priority issues

fr flag

nft is causing me endless headaches, no matter how I tweak the policy, I still cannot get it to function.

The concept I have in mind :

  • One "base" chain where common rules live (e.g. allow ssh etc.)
  • One or more application specific where daemon specific rules live (e.g. http server chain)

I have tried many different permutations of rules, but I can never get both "base" + daemon traffic flowing, I always end up blocking one or the other ! ;-(

Here is my current (simplified) config (as presently constituted it allows ssh but fails to allow http)

/etc/nftables.conf:

#!/usr/sbin/nft -f                                                                                                                                                                                                                                              
flush ruleset                                                                                                                       
table inet filter {   
     counter input_ssh {}     
        set my_admin_ipv4 {                                                                                                        
                type ipv4_addr                                                                                                      
                flags interval                                                                                                      
                counter                                                                                                             
                elements = {                                                                                                        
                        10.0.0.0/8,                                                                                                 
                        172.16.0.0/12,                                                                                              
                        192.168.0.0/16                                                                                                                                                                                      
                }                                                                                                                   
        }         
       chain input {
                type filter hook input priority filter;
                iifname lo accept comment "Allow loopback traffic";
                ct state established,related accept comment "Allow established/related connections";
                ct state invalid drop comment "Deny invalid connections";

                # SSH
                tcp dport ssh ip saddr @my_admin_ipv4 counter name input_ssh accept comment "Allow IPv4 SSH from Admin";
    policy drop;
        }
        chain forward {
                type filter hook forward priority 0;
                policy drop;
        }
        chain output {
                type filter hook output priority 0;
        }
 include "/etc/nft/*.conf"
}

/etc/nft/http.conf:

counter input_http {} 
   chain http {
    type filter hook input priority filter - 1;
      # HTTP #
      tcp dport {80,443} counter name input_nginx accept comment "Allow HTTP";
    policy accept; 
    }
Score:1
cl flag
A.B

You can choose to mark packets to be accepted as a safe conduct in following chains of the same hook.

  • each accept rule should mark the packet

    Rules doing an explicit accept should mark the packet right before accepting it. Any occurence of:

    ... accept
    

    should be replaced with:

    ... meta mark set 0xf00 accept
    

    The value isn't important as long it's not 0 if there's no other role to the mark.

  • each chain should accept a marked packet since that's the safe conduct

    by using this rule early in the chain:

    meta mark != 0 accept
    

That's the general idea. Adaptations and exceptions can still be made if it makes more sense.

Here is the revisited ruleset following this approach. The mark is accepted after the ct ... invalid drop rule which is an important rule that should not be bypassed (and could actually have been done in an earlier chain once and for all since it's a drop rule). If below, input is the last chain in the filter/input hook it's not really needed to mark accepted packets, but it doesn't hurt to do so.

/etc/nftables.conf::

flush ruleset 
table inet filter {   
    counter input_ssh {}     
    set my_admin_ipv4 {                                                                                                        
        type ipv4_addr                                                                                                      
        flags interval                                                                                                      
        counter                                                                                                             
        elements = {                                                                                                        
            10.0.0.0/8,                                                                                                 
            172.16.0.0/12,                                                                                              
            192.168.0.0/16                                                                                                                                                                                      
        }                                                                                                                   
    }         
    chain input {
        type filter hook input priority filter; policy drop;
        iifname lo meta mark set 0x1 accept comment "Allow loopback traffic";
        ct state established,related meta mark set 0x1 accept comment "Allow established/related connections";
        ct state invalid drop comment "Deny invalid connections";
        meta mark != 0 accept

        # SSH
        tcp dport ssh ip saddr @my_admin_ipv4 counter name input_ssh meta mark set 0x1 accept comment "Allow IPv4 SSH from Admin";
    }
    chain forward {
        type filter hook forward priority 0; policy drop;                      
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
    include "/etc/nft/*.conf"
}

/etc/nft/http.conf (replaced counter_nginx with counter_http). The meta mark != 0 accept rule is probably not needed here since there might not be any other chain before anyway, but it doesn't hurt to have it too.

    counter input_http {} 
    chain http {
        type filter hook input priority filter - 1; policy accept;
         meta mark != 0 accept
         # HTTP #
         tcp dport {80,443} counter name input_http meta mark set 0x2 accept comment "Allow HTTP";
    }

This method uses marks so will be harder to integrate with other firewall rules that were already using marks for other purposes. It's still possible by reserving a few bits of the mark with bitwise operations.

Little Code avatar
fr flag
This looks interesting, I will test and let you know. But one question, I see you've changed `policy drop` to `policy accept` on the main input filter ? Surely this means anything unmatched gets accepted ?
A.B avatar
cl flag
A.B
Typo with cut/pastes (and simplification of the initial answer using a dedicated "verdict" chain). I'll put back drop.
Little Code avatar
fr flag
I see, so you're saying there could be a third chain with nothing in it apart from `policy drop` and `meta mark != 0 accept` ? I guess that perhaps makes it cleaner in a way. I'm just loading up the config now, will let you know how it goes.
Little Code avatar
fr flag
Hmm, doesn't seem to be showing me the love (have updated the original Q with the fresh syntax). SSH is working but not web.
Little Code avatar
fr flag
Interesting that the missing `set` was not picked up by `nft -c -f /etc/nftables.conf` ? But now it works !
A.B avatar
cl flag
A.B
meta mark is an expression (the equivalent of iptables' match) while meta mark set is a statement (the equivalent of iptables' target, except it's not terminal for the rule). Both are valid.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.