Nftables – How to Use Defined Variables Through Terminal

firewallnftables

I want to run these two commands in a interactive shell, one after the other (as root and with the "filter" table and the "input" chain loaded):

nft define lala=1.2.3.4
nft add rule ip filter input ip saddr \$lala accept

You can see I escape the "$" to avoid shell expansion. But I get this error message "Unkown identifier 'lala'". If I don't escape the "$", I get this error message instead: "Syntax error, unexepected accept". If I write \$$lala instead, I get this error message instead: "Syntax error, uneepcted accept, expecting string" and this wrong rule is shown: add rule ip filter input ip saddr $ accept.

So, what's the correct syntax to be able to use Nft variables inside a interactive shell. Doing the same inside a Nft script works well (without worrying about escaping).

Thanks a lot

Best Answer

Symbolic variables in use by the nft userspace command are resolved into their values only by this command. The nft command resolves the variable into its assigned value to assemble the final result. Then the nft commands uses the netlink API to communicate with the kernel and send it a new rule to add. The kernel never sees lala=1.2.3.4 nor has any concept of symbolic variable.

So if you split your nft command in two invocations what happens is:

  • The first nft command defines a symbolic variable for its own later use. But does nothing else. It doesn't even have to communicate with the kernel since no ruleset change is asked. (Actually it does communicate, but to send "nothing", like with the command nft ''). At the end of the command the definition of the symbol is lost, without having been used.

  • A second invocation of the nft command needs to resolve the $lala symbol, but cannot find a definition: nft stops with an error.

So this must be all done in one nft invocation so the second statement still knows about the symbolic variable. There are multiple ways to do this. I left in examples below shell and nft's prompts which shouldn't be typed.

  • interactive nft

    -i, --interactive
    Read input from an interactive readline CLI. You can use quit to exit, or use the EOF marker, normally this is CTRL-D.

    # nft -i
    nft> define lala=1.2.3.4
    nft> add rule ip filter input ip saddr $lala accept
    nft> quit
    
  • two commands in a single nft invocation.

    # nft 'define lala=1.2.3.4; add rule ip filter input ip saddr $lala accept'
    

    Note the additional ; needed because in this context nft receives one line of parameters, but they should be split into two logical commands. Also nft having its own parser, it doesn't care if commands are received as a single command parameter or multiple separate parameters. Here everything was enclosed in a pair of '' to avoid shell issues with both ; and $ characters.

  • Then nft has an option to read from a file. When reading from a file, all the input is part of the same nft command context.

    -f, --file filename
    Read input from filename. If filename is -, read from stdin.

    nft scripts must start #!/usr/sbin/nft -f

    So with a file named test.nft having this content:

    define lala=1.2.3.4
    add rule ip filter input ip saddr $lala accept
    

    This can then be used:

    # nft -f test.nft
    

    The variant below executes a script in the "nftables language". Having a test.nft file content as:

    #!/usr/sbin/nft -f
    define lala=1.2.3.4
    add rule ip filter input ip saddr $lala accept
    

    Then:

    # chmod a+rx test.nft
    # ./test.nft
    

    This would also have worked:

    # nft -f - <<'EOF'
    > define lala=1.2.3.4
    > add rule ip filter input ip saddr $lala accept
    > EOF
    

For each previous command, the resulting ruleset is the same (assuming table and chain were created before):

# nft list chain ip filter input
table ip filter {
    chain input {
        type filter hook input priority filter; policy accept;
        ip saddr 1.2.3.4 accept
    }
}

There won't be anymore any trace of $lala left: the kernel never received $lala but only 1.2.3.4 and that's what it gives back.

Related Topic