ipr-cnrs.nftables

Nftables

  1. Overview
  2. Role Variables
  3. Examples
  4. Configuration
  5. Development
  6. License
  7. Author Information

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 is true, rules from different groups will be merged.
  • 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
  1. A new variable is defined for HTTP ports.

  2. A new rule for incoming traffic uses the previously defined variable.

  3. 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.
  • 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
  1. A new named set is defined for torrent ports.

  2. A new rule for incoming traffic uses the defined set.

  3. To add a port to this set on the remote host: nft add element inet filter in_udp_accept \{ 6999 \}

  4. 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
  1. Get rule description from the defaults/main.yml file (e.g., 050 icmp).
  2. Drop any ICMP request not coming from the 192.168.0.0 network.
  3. 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
  1. Get item name (e.g., input tcp accepted) and variable name (e.g., in_tcp_accept) from the defaults/main.yml file.
  2. Set a new value (e.g., '{ 2201, 6881 }').
  3. 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
  1. Get rule description from the defaults/main.yml file (210 output tcp accepted).
  2. 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.
  3. 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)
  1. Enable merging of group variables:
    - hosts: serverXYZ
      vars:
        nft_merged_groups: true
        nft_merged_groups_dir: vars/
      roles:
        - role: ipr-cnrs.nftables
    
  2. 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
      
  3. 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 the nftables unit.
  • The fail2ban unit restarts automatically when the nftables 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)
Informazioni sul progetto

Manage Nftables rules and packages

Installa
ansible-galaxy install ipr-cnrs.nftables
Licenza
Unknown
Download
99.3k
Proprietario
Institut de Physique de Rennes https://ipr.univ-rennes1.fr/ Tutelles CNRS et Université Rennes 1