ipr-cnrs.nftables
Nftables
Overview
This role helps to manage Nftables rules and packages. It is inspired by Mike Gleason's firewall role, which has a three-level structure for defining and templating rules. Thanks to Mike for the groundwork!
Role Variables
- nft_enabled: Turn on or off Nftables support (default:
true
). - nft_pkg_state: State of the Nftables package(s) (default:
present
). - nft_old_pkg_list: List of unnecessary packages to remove, like Iptables (default:
iptables
). - nft_old_pkg_state: State of old package(s) (default:
absent
). - nft_old_pkg_manage: Manage old package(s) with this role (default:
true
). - nft_conf_dir_path: Directory for Nftables configuration files (default:
/etc/nftables.d
). - nft_main_conf_path: Main configuration file for systemd (default:
/etc/nftables.conf
). - nft_main_conf_content: Template for the main configuration file (default:
etc/nftables.conf.j2
). - nft_input_conf_path: Input configuration file included in the main file (default:
{{ nft_conf_dir_path }}/filter-input.nft
). - nft_input_conf_content: Template for the input configuration file (default:
etc/nftables.d/filter-input.nft.j2
). - nft_output_conf_path: Output configuration file included in the main file (default:
{{ nft_conf_dir_path }}/filter-output.nft
). - nft_output_conf_content: Template for the output configuration file (default:
etc/nftables.d/filter-output.nft.j2
). - nft_forward_conf_path: Forward configuration file included in the main file (default:
{{ nft_conf_dir_path }}/filter-forward.nft
). - nft_forward_conf_content: Template for the forward configuration file (default:
etc/nftables.d/filter-forward.nft.j2
). - nft_define_conf_path: Variables definition file included in the main file (default:
{{ nft_conf_dir_path }}/defines.nft
). - nft_define_conf_content: Template for the variables definition file (default:
etc/nftables.d/defines.nft.j2
). - nft_sets_conf_path: Sets and maps definition file included in the main file (default:
{{ nft_conf_dir_path }}/sets.nft
). - nft_sets_conf_content: Template for the sets and maps definition file (default:
etc/nftables.d/sets.nft.j2
). - nft_global_default_rules: Default rules for the global chain; other chains refer to these rules.
- nft_global_rules: Add or change global rules applicable to all hosts.
- nft_global_group_rules: Add or change global rules applicable to a specific group.
- nft_global_host_rules: Hosts can add or change any previous rules.
- nft_custom_content: Additional custom content for Nftables configuration (default:
''
). - nft_input_default_rules: Default rules for the input chain.
- nft_input_rules: Add or change input rules for all hosts.
- nft_input_group_rules: Add or change input rules for a specific group.
- nft_input_host_rules: Hosts can also add or change all prior input rules.
- nft_output_default_rules: Default rules for the output chain.
- nft_output_rules: Add or change output rules for all hosts.
- nft_output_group_rules: Add or change output rules for a specific group.
- nft_output_host_rules: Hosts can also add or change all prior output rules.
- nft_forward_default_rules: Default rules for the forward chain.
- nft_forward_rules: Add or change forward rules for all hosts.
- nft_forward_group_rules: Add or change forward rules for a specific group.
- nft_forward_host_rules: Hosts can also add or change all prior forward rules.
- nft_forward_table_manage: Manage the forward table (default:
False
). - nft_nat_table_manage: Manage the NAT table (default:
False
). - nft_nat_default_prerouting_rules: Default rules for the NAT table's prerouting chain.
- nft_nat_prerouting_rules: Set rules for the NAT table's prerouting chain for all hosts.
- nft_nat_group_prerouting_rules: Set rules for the NAT table's prerouting chain for specific groups.
- nft_nat_host_prerouting_rules: Set rules for the NAT table's prerouting chain for specific hosts.
- nft_nat_prerouting_conf_path: Prerouting configuration file included in the main configuration (default:
{{ nft_conf_dir_path }}/nat-prerouting.nft
). - nft_nat_prerouting_conf_content: Template for the prerouting configuration file (default:
etc/nftables.d/nat-prerouting.nft.j2
). - nft_nat_default_postrouting_rules: Default rules for the NAT table's postrouting chain.
- nft_nat_postrouting_rules: Set rules for the NAT table's postrouting chain for all hosts.
- nft_nat_group_postrouting_rules: Set rules for the NAT table's postrouting chain for specific groups.
- nft_nat_host_postrouting_rules: Set rules for the NAT table's postrouting chain for specific hosts.
- nft_nat_postrouting_conf_path: Postrouting configuration file included in the main configuration (default:
{{ nft_conf_dir_path }}/nat-postrouting.nft
). - nft_nat_postrouting_conf_content: Template for the postrouting configuration file (default:
etc/nftables.d/nat-postrouting.nft.j2
). - nft_define_default: Set default variables available in all rules.
- nft_define: Add or change variables for all hosts.
- nft_define_group: Add or change variables for a specific group.
- nft_define_host: Add or change all previous variables.
- nft_service_manage: Manage the nftables service with this role (default:
true
). - nft_service_name: Name of the nftables service (default:
nftables
). - nft_service_enabled: Make the nftables service available at startup (default:
true
). - nft_service_protect: If the systemd unit should safeguard system and home (default:
true
). - nft_merged_groups: Should variables from Ansible groups be merged (default:
false
). - nft_merged_groups_dir: Directory where the Nftables group rules are located (default:
vars/
). - nft_debug: Turn on or off verbose output (default:
false
).
OS Specific Variables
Refer to OS-specific files in the [vars directory] for default values.
- nft_pkg_list: List of packages for Nftables.
- nft_bin_location: Path to the Nftables executable (default:
/usr/sbin/nft
).
Rule Templates
The nft_templates
dictionary contains a set of useful rules for your firewall. For instance, {{ nft_templates.allow_mdns }}
covers mDNS for both IPv4 and IPv6.
Use it like this in custom rule sets:
nft_host_input_rules:
...
010 allow mdns: "{{ nft_templates.allow_mdns }}"
The nft_templates
dictionary also includes recommended rules for forwarding and input traffic firewalls defined in RFCs. For more details, see the defaults/main.yml.
Rules Dictionaries
Different types of rules dictionaries can be merged, applying the rules in alphabetical order of the keys.
- nft_*_default_rules: Default rules for all nodes, defined in
group_vars/all
. - nft_*_rules: Add rules that override those in nft_*_default_rules, defined in
group_vars/all
. - nft_*_group_rules: Add rules that override those in nft_*_default_rules and nft_*_rules, defined in
group_vars/webservers
.- If
nft_merged_groups
istrue
, rules from different groups will be merged.
- If
- nft_*_host_rules: Can add rules and override those from nft_*_default_rules, nft_*_group_rules, and nft_*_rules, defined in
host_vars/www.local.domain
.
defaults/main.yml
example:
# rules
nft_global_default_rules:
005 state management:
- ct state established,related accept
- ct state invalid drop
nft_global_rules: {}
nft_merged_groups: false
nft_merged_groups_dir: vars/
nft_global_group_rules: {}
nft_global_host_rules: {}
nft_input_default_rules:
000 policy:
- type filter hook input priority 0; policy drop;
005 global:
- jump global
010 drop unwanted:
- ip daddr @blackhole counter drop
015 localhost:
- iif lo accept
210 input tcp accepted:
- tcp dport @in_tcp_accept ct state new accept
nft_input_rules: {}
nft_input_group_rules: {}
nft_input_host_rules: {}
nft_output_default_rules:
000 policy:
- type filter hook output priority 0; policy drop;
005 global:
- jump global
015 localhost:
- oif lo accept
050 icmp:
- ip protocol icmp accept
- ip6 nexthdr icmpv6 counter accept
200 output udp accepted:
- udp dport @out_udp_accept ct state new accept
210 output tcp accepted:
- tcp dport @out_tcp_accept ct state new accept
nft_output_rules: {}
nft_output_group_rules: {}
nft_output_host_rules: {}
# define nft vars
nft_define_default:
broadcast and multicast:
desc: 'broadcast and multicast'
name: badcast_addr
value: '{ 255.255.255.255, 224.0.0.1, 224.0.0.251 }'
input tcp accepted:
name: in_tcp_accept
value: '{ ssh }'
output tcp accepted:
name: out_tcp_accept
value: '{ http, https, hkp }'
output udp accepted:
name: out_udp_accept
value: '{ bootps, domain, ntp }'
nft_define: {}
nft_define_group: {}
nft_define_host: {}
# sets and maps
nft_set_default:
blackhole:
- type ipv4_addr;
- elements = $badcast_addr
in_tcp_accept:
- type inet_service; flags interval;
- elements = $in_tcp_accept
out_tcp_accept:
- type inet_service; flags interval;
- elements = $out_tcp_accept
out_udp_accept:
- type inet_service; flags interval;
- elements = $out_udp_accept
nft_set: {}
nft_set_group: {}
nft_set_host: {}
These defaults will create the following configuration:
#!/usr/sbin/nft -f
# Ansible managed
# clean
flush ruleset
include "/etc/nftables.d/defines.nft"
table inet filter {
chain global {
# 000 state management
ct state established,related accept
ct state invalid drop
}
include "/etc/nftables.d/sets.nft"
include "/etc/nftables.d/filter-input.nft"
include "/etc/nftables.d/filter-output.nft"
}
To view all rules and definitions, display the ruleset on the host: $ nft list ruleset
.
table inet filter {
set blackhole {
type ipv4_addr
elements = { 255.255.255.255, 224.0.0.1, 224.0.0.251}
}
set out_tcp_accept {
type inet_service
flags interval
elements = { http, https, hkp}
}
set out_udp_accept {
type inet_service
flags interval
elements = { domain, bootps, ntp}
}
chain global {
ct state established,related accept
ct state invalid drop
}
chain input {
type filter hook input priority 0; policy drop;
jump global
ip daddr @blackhole counter packets 0 bytes 0 drop
iif "lo" accept
tcp dport @in_tcp_accept ct state new accept
}
chain output {
type filter hook output priority 0; policy drop;
jump global
oif "lo" accept
ip protocol icmp accept
udp dport @out_udp_accept ct state new accept
tcp dport @out_tcp_accept ct state new accept
}
}
Examples
With playbooks
Manage Nftables with default vars (click to expand)
- hosts: serverXYZ
roles:
- role: ipr-cnrs.nftables
Add a new simple filter rule for incoming traffic (e.g., one port for UDP/torrent) (click to expand)
- hosts: serverXYZ
vars:
nft_input_rules:
400 input torrent accepted:
- udp dport 6881 ct state new accept
roles:
- role: ipr-cnrs.nftables
- You can also use nft_input_group_rules or nft_input_host_rules variables.
- The weight (
400
) helps order the merged rules (from *nft_input_rules dictionaries). - The description following the weight (
input torrent accepted
) will be added as a comment in the nft_input_conf_path file on the remote host.
Add a new multi-ports filter rule for incoming traffic (e.g., TCP/http, https, http-alt,…) (click to expand)
- hosts: serverXYZ
vars:
nft_input_rules:
400 input http accepted:
- tcp dport { 80, 443, 8080-8082 } ct state new accept
roles:
- role: ipr-cnrs.nftables
- You can also use nft_input_group_rules or nft_input_host_rules variables.
- The weight (
400
) helps order the merged rules (from *nft_input_rules dictionaries). - The description following the weight (
input http accepted
) will be added as a comment in the nft_input_conf_path file on the remote host. - In this case, brackets define an anonymous set, while for a single element (e.g., port, IP address), brackets are not needed.
Add a new rule with a variable (click to expand)
Variables can be useful for defining generic rules for all hosts, and you can override a variable's value for specific groups or hosts.
- hosts: serverXYZ
vars:
nft_define_group:
input http accepted:
desc: HTTP and HTTPS
name: in_http_accept
value: '{ 80, 443 }'
nft_input_group_rules:
400 input http accepted:
- tcp dport $in_http_accept ct state new accept
roles:
- role: ipr-cnrs.nftables
A new variable is defined for HTTP ports.
A new rule for incoming traffic uses the previously defined variable.
Result of
nft list ruleset
on the remote host will display:table inet filter { … chain input { … tcp dport { http, https } ct state new accept … } … }
- There is no mention of the
$in_http_accept
variable.
- There is no mention of the
- You can also use nft_define or nft_define_host variables.
- You can also use nft_input_rules or nft_input_host_rules variables.
- The weight (
400
) helps order the merged rules from *nft_input_rules dictionaries. - The description following the weight (
input http accepted
) is a comment that will appear in the nft_input_conf_path file on the remote host.
Add a new rule with a named set (click to expand)
Named sets allow you to define generic rules and override specific sets as needed.
- hosts: serverXYZ
vars:
nft_set_group:
in_udp_accept:
- type inet_service; flags interval;
- elements = { 6881-6887, 6889 }
nft_input_group_rules:
200 input udp accepted:
- udp dport @in_udp_accept ct state new accept
roles:
- role: ipr-cnrs.nftables
A new named set is defined for torrent ports.
A new rule for incoming traffic uses the defined set.
To add a port to this set on the remote host:
nft add element inet filter in_udp_accept \{ 6999 \}
Result of
nft list ruleset
on the remote host will show:table inet filter { … set in_udp_accept { type inet_service flags interval elements = { 6881-6887, 6889, 6999 } } chain input { … udp dport @in_udp_accept ct state new accept … } … }
- You can also use nft_set or nft_set_host variables.
- You can also use nft_input_rules or nft_input_host_rules variables.
- The weight (
200
) helps order the merged rules from *nft_input_rules dictionaries. - The description following the weight (
input udp accepted
) is a comment added in the nft_input_conf_path file on the remote host.
Override a default rule with 2 new rules (click to expand)
- hosts: serverXYZ
vars:
nft_input_host_rules:
050 icmp:
- ip protocol icmp ip saddr != 192.168.0.0/24 counter drop
- ip protocol icmp icmp type echo-request ip length <= 84 counter limit rate 10/minute accept
roles:
- role: ipr-cnrs.nftables
- Get rule description from the
defaults/main.yml
file (e.g.,050 icmp
). - Drop any ICMP request not coming from the 192.168.0.0 network.
- Ensure that requests are less than or equal to 84 bytes, and limit to 10 requests per minute.
- You can also use nft_input_rules or nft_input_group_rules variables.
- The weight (
050
) helps order the merged rules from *nft_input_rules dictionaries. - The description following the weight (
icmp
) is a comment in the nft_input_conf_path file on the remote host.
Override some of the default defined sets (click to expand)
- hosts: serverXYZ
vars:
nft_define:
input tcp accepted:
desc: Custom SSH port and torrent
name: in_tcp_accept
value: '{ 2201, 6881 }'
roles:
- role: ipr-cnrs.nftables
- Get item name (e.g.,
input tcp accepted
) and variable name (e.g.,in_tcp_accept
) from thedefaults/main.yml
file. - Set a new value (e.g.,
'{ 2201, 6881 }'
). - You can add a
desc
attribute for a comment in the nft_input_conf_path file on the remote host.
- You can also use nft_define_group or nft_define_host variables.
Override all default rules (e.g., for outgoing traffic) (click to expand)
When default rules are too lenient or mostly overridden, you can redefine them:
- hosts: serverXYZ
vars:
nft_output_default_rules:
000 policy:
- type filter hook output priority 0; policy drop;
005 state management:
- ct state established,related accept
- ct state invalid drop
015 localhost:
- oif lo accept
050 my rule for XXX hosts and services:
- tcp dport 2000 ip saddr { xxx.xxx.xxx.xxx, yyy.yyy.yyy.yyy } ct state new accept
250 reset-ssh: # allow the host to reset SSH connections to avoid 10 min delay from Ansible controller
- tcp sport ssh tcp flags { rst, psh | ack } counter accept
roles:
- role: ipr-cnrs.nftables
- Remember to set a default
policy
. - Manage the
established
state. - Accept
rst, psh | ack
flags for ssh to avoid a 10-minute delay during the first run of this Nftables role. - Add your desired rules, ordering them with weight from *nft_output_rules dictionaries.
Remove a default rule (click to expand)
- hosts: serverXYZ
vars:
nft_output_host_rules:
210 output tcp accepted:
-
roles:
- role: ipr-cnrs.nftables
- Get rule description from the
defaults/main.yml
file (210 output tcp accepted
). - The default policy for outgoing traffic (drop) will now apply to the ports defined in the out_tcp_accept variable. Ensure this is correct before proceeding.
- This rule will be removed from the
nft list ruleset
output, leaving just a comment (210 output tcp accepted
) in the nft_output_conf_path file on the remote host.
- You can also use nft_output_rules or nft_output_group_rules variables.
- The weight (
210
) helps order the merged rules from *nft_output_rules dictionaries.
With group_vars and host_vars
Use default rules and allow, for first_group, incoming ICMP and count both ICMP and default policy (drop) packets (click to expand)
group_vars/first_group
:
nft_input_group_rules:
020 icmp:
- ip protocol icmp icmp type echo-request ip length <= 84 counter limit rate 1/minute accept
999 count policy packet:
- counter
Use merged group rules from multiple Ansible groups (click to expand)
- Enable merging of group variables:
- hosts: serverXYZ vars: nft_merged_groups: true nft_merged_groups_dir: vars/ roles: - role: ipr-cnrs.nftables
- Add additional rules in the "vars" folder named after your Ansible groups for serverXYZ:
vars/first_group
:nft_input_group_rules: 020 icmp: - ip protocol icmp icmp type echo-request ip length <= 84 counter limit rate 1/minute accept 999 count policy packet: - counter
vars/second_group
:nft_input_group_rules: 021 LAN: - iif eth0 accept
- These rules from the two groups will be merged if the host is a member of both groups.
Configuration
This role will:
- Install
nftables
on the system. - Enable the
nftables
service at startup by default. - Create a default configuration file that includes the necessary files and is loaded by systemd.
- Generate input and output rules files referenced by the main configuration file.
- Create variable definitions in a specific file and sets and maps in another.
- Ensure that
nftables
is running and set to start on boot. - (Re)start
nftables
service on first run or when systemd units change. - Reload
nftables
service on subsequent runs to prevent the host from lacking firewall rules due to invalid syntax.
Fail2ban Integration
Before Debian Bullseye, the systemd unit for Fail2ban lacked proper integration with Nftables. This role creates an override file for the fail2ban
unit, unless nft_fail2ban_service_override
is set to false
. By default, it will be added whether or not it is already available on the host. This ensures:
- The
fail2ban
unit starts after thenftables
unit. - The
fail2ban
unit restarts automatically when thenftables
unit restarts.
Development
This code originates from our Gitea instance with a GitHub repository for sharing the role on Ansible Galaxy.
Feel free to report issues or submit pull requests here!
Thanks to the hook, updates from our Gitea instance are automatically reflected on GitHub.
License
The code is licensed under WTFPL.
Author Information
Jérémy Gardais
- IPR (Institut de Physique de Rennes)
ansible-galaxy install ipr-cnrs.nftables