saltstack / salt

Software to automate the management and configuration of any infrastructure or application at scale. Get access to the Salt software package repository here:

Home Page:https://repo.saltproject.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[BUG] nftables state module not idempotent for conntrack

lkubb opened this issue · comments

Description
nftables.append keeps appending rules that have connstate set.

Edit: This also applies for if/of with a different cause (nft list does not include meta before iifname/oifname).

Edit2: This also applies for jump: dnat with to-destination set (nft list prints dnat to <target>, not dnat <target>).

Setup
Salt minion on Debian 11 VM

Steps to Reproduce the behavior

testtable:
  nftables.table_present: []

testchain:
  nftables.chain_present:
    - table: testtable

This is not idempotent:
  nftables.append:
    - table: testtable
    - chain: testchain
    - jump: accept
    - connstate: established,related

Expected behavior
Be idempotent.

Screenshots

$ nft list ruleset
$ salt-call state.apply nft_idem
$ nft list ruleset
table ip testtable {
	chain testchain {
		ct state { established, related } accept
	}
}
$ salt-call state.apply nft_idem
$ nft list ruleset
table ip testtable {
	chain testchain {
		ct state { established, related } accept
		ct state { established, related } accept
	}
}

Versions Report

salt --versions-report (Provided by running salt --versions-report. Please also mention any differences in master/minion versions.)
Salt Version:
          Salt: 3004.2

Dependency Versions:
          cffi: Not Installed
      cherrypy: Not Installed
      dateutil: 2.8.1
     docker-py: Not Installed
         gitdb: Not Installed
     gitpython: Not Installed
        Jinja2: 2.11.3
       libgit2: Not Installed
      M2Crypto: Not Installed
          Mako: Not Installed
       msgpack: 1.0.0
  msgpack-pure: Not Installed
  mysql-python: Not Installed
     pycparser: Not Installed
      pycrypto: Not Installed
  pycryptodome: 3.9.7
        pygit2: Not Installed
        Python: 3.9.2 (default, Feb 28 2021, 17:03:44)
  python-gnupg: Not Installed
        PyYAML: 5.3.1
         PyZMQ: 20.0.0
         smmap: Not Installed
       timelib: Not Installed
       Tornado: 4.5.3
           ZMQ: 4.3.4

System Versions:
          dist: debian 11 bullseye
        locale: utf-8
       machine: x86_64
       release: 5.10.0-13-amd64
        system: Linux
       version: Debian GNU/Linux 11 bullseye

Additional context
This behavior is caused by nftables.check execution module not recognizing the rule correctly. It lists the rules with the --numeric switch, which translates the connection states to hex values, but searches for them using their string representation.

Example output:

$ nft --handle --numeric --numeric --numeric list chain ip testtable testchain # why 3x numeric btw? copied from source
table ip testtable {
	chain testchain { # handle 1
		ct state { 0x2, 0x4 } accept # handle 3
		ct state { 0x2, 0x4 } accept # handle 5
	}
}

Workaround possible by specifying the values in hex (which should break the analogy to iptables afaik):

testtable:
  nftables.table_present: []

testchain:
  nftables.chain_present:
    - table: testtable

This is idempotent:
  nftables.append:
    - table: testtable
    - chain: testchain
    - jump: accept
    - connstate: '0x2, 0x4'

Thank you for reporting. I noticed the hex weirdness in addition to #61975. following 👀

I ran into this today.

This happens because modules/nftables.py in check() uses --numeric argument to nft which converts many things (including connstate) to numeric.

However, removing the --numeric arguments to nft command does not solve this issue because when salt builds the command it encapsulates connstate and port in {}, e.g. { new } or {established,related}, but when salt runs nft to print the rules (so that it can search for exact match), the nftables removes the {} for single connstate or port.

Consider a state element

allow_dns_udp:
  nftables.append:
    - save: True
    - table: filter
    - chain: OUTPUT
    - jump: accept
    - match: state
    - connstate: new
    - proto: udp
    - dport: 53
    - destination: 10.1.1.1

Thus if salt built the following rule

ct state { new } ip daddr 10.1.1.1 udp dport { 53 } accept

when it echoes the existing rules (without --numeric), it gets

ct state new ip daddr 10.1.1.1 udp dport 53 accept

Hence it can never match the rule exactly. The rule matching logic needs to probably use regular expressions for this issue to be fixed.