Ensure Base Chains Exist for Nftables

Classification:

compliance

Framework:

Control:

Description

Tables in nftables hold chains. Each table only has one address family and only applies to packets of this family. Tables can have one of six families. Chains are containers for rules. They exist in two kinds, base chains and regular chains. A base chain is an entry point for packets from the networking stack, a regular chain may be used as jump target and is used for better rule organization.

Rationale

If a base chain doesn’t exist with a hook for input, forward, and delete, packets that would flow through those chains will not be touched by nftables.

Remediation

Shell script

The following script can be run on the host to remediate the issue.

# Remediation is applicable only in certain platforms
if dpkg-query --show --showformat='${db:Status-Status}\n' 'nftables' 2>/dev/null | grep -q installed; then

#Name of the table
var\_nftables\_table='filter'

#Familiy of the table 
var\_nftables\_family='inet'

#Name(s) of base chain
var\_nftables\_base\_chain\_names=''

#Type(s) of base chain
var\_nftables\_base\_chain\_types=''

# Hooks for base chain
var\_nftables\_base\_chain\_hooks=''

#Priority
var\_nftables\_base\_chain\_priorities=''

#Policy 
var\_nftables\_base\_chain\_policies=''


#Transfer some of strings to arrays
IFS="," read -r -a names <<< "$var\_nftables\_base\_chain\_names"
IFS="," read -r -a types <<< "$var\_nftables\_base\_chain\_types"
IFS="," read -r -a hooks <<< "$var\_nftables\_base\_chain\_hooks"
IFS="," read -r -a priorities <<< "$var\_nftables\_base\_chain\_priorities"
IFS="," read -r -a policies <<< "$var\_nftables\_base\_chain\_policies"

my\_cmd="nft list tables | grep '$var\_nftables\_family $var\_nftables\_table'"
eval IS\_TABLE\_EXIST=\$\($my\_cmd\)
if [ -z "$IS\_TABLE\_EXIST" ]
then
 # We create a table and add chains to it 
 nft create table "$var\_nftables\_family" "$var\_nftables\_table"
 num\_of\_chains=${#names[@]}
 for ((i=0; i < num\_of\_chains; i++))
 do
 chain\_to\_add="add chain $var\_nftables\_family $var\_nftables\_table ${names[$i]} { type ${types[$i]} hook ${hooks[$i]} priority ${priorities[$i]} ; policy ${policies[$i]} ; }"
 my\_cmd="nft '$chain\_to\_add'"
 eval $my\_cmd
 done 
else
 # We add missing chains to the existing table
 num\_of\_chains=${#names[@]}
 for ((i=0; i < num\_of\_chains; i++))
 do
 IS\_CHAIN\_EXIST=$(nft list table "$var\_nftables\_family" "$var\_nftables\_table" | grep "hook ${hooks[$i]}")
 if [ -z "$IS\_CHAIN\_EXIST" ]
 then
 chain\_to\_add="add chain '$var\_nftables\_family' '$var\_nftables\_table' ${names[$i]} { type ${types[$i]} hook ${hooks[$i]} priority ${priorities[$i]} ; policy ${policies[$i]} ; }"
 my\_cmd="nft '$chain\_to\_add'"
 eval $my\_cmd
 fi
 done 
fi

else
 >&2 echo 'Remediation is not applicable, nothing was done'
fi

Ansible playbook

The following playbook can be run with Ansible to remediate the issue.

- name: Gather the package facts
 package\_facts:
 manager: auto
 tags:
 - low\_complexity
 - low\_disruption
 - medium\_severity
 - no\_reboot\_needed
 - restrict\_strategy
 - set\_nftables\_base\_chain
- name: XCCDF Value var\_nftables\_table # promote to variable
 set\_fact:
 var\_nftables\_table: !!str filter
 tags:
 - always
- name: XCCDF Value var\_nftables\_family # promote to variable
 set\_fact:
 var\_nftables\_family: !!str inet
 tags:
 - always
- name: XCCDF Value var\_nftables\_base\_chain\_names # promote to variable
 set\_fact:
 var\_nftables\_base\_chain\_names: !!str 
 tags:
 - always
- name: XCCDF Value var\_nftables\_base\_chain\_types # promote to variable
 set\_fact:
 var\_nftables\_base\_chain\_types: !!str 
 tags:
 - always
- name: XCCDF Value var\_nftables\_base\_chain\_hooks # promote to variable
 set\_fact:
 var\_nftables\_base\_chain\_hooks: !!str 
 tags:
 - always
- name: XCCDF Value var\_nftables\_base\_chain\_priorities # promote to variable
 set\_fact:
 var\_nftables\_base\_chain\_priorities: !!str 
 tags:
 - always
- name: XCCDF Value var\_nftables\_base\_chain\_policies # promote to variable
 set\_fact:
 var\_nftables\_base\_chain\_policies: !!str 
 tags:
 - always

- name: Ensure Base Chains Exist for Nftables - Check Existence of Nftables Table
 ansible.builtin.shell: nft list tables | grep '{{ var\_nftables\_family }} {{ var\_nftables\_table
 }}'
 register: existing\_nftables
 changed\_when: false
 failed\_when: false
 when: '"nftables" in ansible\_facts.packages'
 tags:
 - low\_complexity
 - low\_disruption
 - medium\_severity
 - no\_reboot\_needed
 - restrict\_strategy
 - set\_nftables\_base\_chain

- name: Ensure Base Chains Exist for Nftables - Set NFTables Table
 ansible.builtin.command: nft create table {{ var\_nftables\_family }} {{ var\_nftables\_table
 }}
 when:
 - '"nftables" in ansible\_facts.packages'
 - existing\_nftables.rc > 0
 tags:
 - low\_complexity
 - low\_disruption
 - medium\_severity
 - no\_reboot\_needed
 - restrict\_strategy
 - set\_nftables\_base\_chain

- name: Ensure Base Chains Exist for Nftables - Add Base Chains
 ansible.builtin.command: nft 'add chain {{ var\_nftables\_family }} {{ var\_nftables\_table
 }} {{ item.0 }} { type {{ item.1 }} hook {{ item.2 }} priority {{ item.3 }} ;
 policy {{ item.4 }} ; }'
 with\_together:
 - '{{ var\_nftables\_base\_chain\_names.split(",") }}'
 - '{{ var\_nftables\_base\_chain\_types.split(",") }}'
 - '{{ var\_nftables\_base\_chain\_hooks.split(",") }}'
 - '{{ var\_nftables\_base\_chain\_priorities.split(",") }}'
 - '{{ var\_nftables\_base\_chain\_policies.split(",") }}'
 when: '"nftables" in ansible\_facts.packages'
 tags:
 - low\_complexity
 - low\_disruption
 - medium\_severity
 - no\_reboot\_needed
 - restrict\_strategy
 - set\_nftables\_base\_chain

Warning

Configuring rules over ssh, by creating a base chain with policy drop will cause loss of connectivity. Ensure that a rule allowing ssh has been added to the base chain prior to setting the base cahin’s policy to drop