ansibleguy.infra_nftables

Ansible Role - NFTables
This role sets up the NFTables firewall on Linux servers.
Tested on:
- Debian 11
- Debian 12
Installation
# Install latest version
ansible-galaxy role install git+https://github.com/ansibleguy/infra_nftables
# From Ansible Galaxy
ansible-galaxy install ansibleguy.infra_nftables
# Or install to a custom path
ansible-galaxy install ansibleguy.infra_nftables --roles-path ./roles
Documentation
- NFTables: Wiki
- Check out the Example!
- Practical Use-Cases (Docker, Proxmox, Network firewall)
- Combining Fail2Ban with NFTables
- Troubleshooting Guide
Troubleshooting
Usage
Want a simple Ansible GUI? Check out my Ansible WebUI
Configuration
Set up the configuration as needed:
nftables:
# Enable features supported by the kernel
# sets: true
# nat: true
# deb11_backport: false # use Debian 11 backports for newer version on Debian 10
# bash_completion: false
_defaults: # defaults for all tables and chains
table:
type: 'inet'
chain:
policy: 'drop'
type: 'filter'
priority: 0
log:
drop: true
rules:
_all: [] # rules for all chains of all tables
incoming: [] # rules for the 'incoming' chain of all tables
tables:
example:
_defaults:
rules:
_all: [] # rules for all chains of this table
vars:
dns_servers: ['1.1.1.1', '1.1.0.0', '8.8.8.8', '8.8.4.4']
private_ranges: ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8']
sets:
blacklist:
flags: ['dynamic', 'timeout']
settings:
timeout: '3m'
counters:
invalid_packages:
comment: 'Invalid'
chains:
incoming:
hook: 'input'
rules:
- sequence: 1
raw: 'ct state invalid counter name invalid_packages log prefix "DROP invalid states" drop'
- seq: 2
raw: 'ct state {established, related} counter accept comment "Allow open sessions"'
- s: 3
raw: 'iifname "lo" accept comment "Allow loopback traffic"'
- {proto: 'icmp', type: 'echo-request', limit: 'rate 10/second', comment: 'Allow icmp-ping'}
- {proto: 'icmpv6', type: 'echo-request', limit: 'rate 10/second', comment: 'Allow icmp-ping'}
- {proto: 'icmp', code: 30, limit: 'rate 10/second', comment: 'Allow icmp-traceroute'}
- {proto: 'icmpv6', limit: 'rate 10/second', comment: 'Allow necessary icmpv6-types for ipv6 to work',
type: ['nd-neighbor-solicit', 'nd-router-advert', 'nd-neighbor-advert']}
- {proto: 'udp', port: 46251, counter: 'invalid_packages'}
outgoing:
hook: 'output'
rules:
- {dest: '$dns_servers', proto: 'udp', port: 53}
- {dest: '$dns_servers', proto: 'tcp', port: [53, 853]}
- {proto: ['tcp', 'udp'], port: [80, 443]}
- {proto: ['icmp', 'icmpv6'], comment: 'Allow outbound icmp'}
route:
hook: 'forward'
translate:
hook: 'postrouting'
type: 'nat'
policy: 'accept'
rules:
- {'src': '$private_ranges', oif: 'eno2', masquerade: true} # dynamic outbound nat
- {'src': '$private_ranges', oif: 'eno3', snat: '192.168.0.1'} # static outbound nat
To combine group and host rules, do it like this:
- name: NFTables
become: true
hosts: all
vars:
nftables:
tables:
example:
chains: "{{ fw_rules_all |
combine(fw_rules_group|default({}), recursive=true, list_merge='append') |
combine(fw_rules_host|default({}), recursive=true, list_merge='append') }}"
pre_tasks:
- debug:
var: nftables
roles:
- ansibleguy.infra_nftables
Execution
Run the playbook:
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml
Useful tags:
- config_table => only provision actual rulesets
- config
- purge
For debugging errors, set the debug variable at runtime:
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml -e debug=yes
Features
Package Installation
- Minimal Ansible dependencies
- NFTables
Configuration
Define
- variables globally
- variables, sets, counters, and limits at the table level
- variables at the chain level
Configuration validation before writing it
Default Configuration:
- Enabled features (must be supported by kernel)
- Sets
- NAT
- No default rules added
- Tables:
- table-type = inet
- Chains:
- chain-type = filter
- chain-policy = drop
- priority = 0
- add counter = yes
- log implicit drops = yes
- Sets:
- set-type = ipv4_addr
- add counter = yes
- Rules:
- policy = accept (set to 'none' to remove it)
- logging drops = yes
- Enabled features (must be supported by kernel)
Default Options:
- Deleting unmanaged config files in '/etc/nftables.d/'
Default Opt-Outs:
- Installing NFTables from Debian 11 backports on Debian 10 (for newer version)
- Adding bash-completion script for 'nft' command
Information
Note: You can enable or disable most functions of the role.
For a full list of options, check the default configuration in the main defaults file!
Warning: Not all settings/variables will be checked for validity. Bad configuration may break the role!
Info: You can add DNS Resolution and IP Blocklist features to NFTables using the ansibleguy.addons_nftables role!
Warning: Some core functionalities (NAT/Sets) might not be supported by all Distribution kernels.
Info: Read the Hook documentation for guidance on configuring hooks and priorities!
Info: Rules can be given in dictionary format as shown in examples.
Below are the available fields and aliases:
Function Keys Note Rule sequence s, id, seq, sequence The sequence ID (integer) to sort the rules inside a chain. Defaults to 1000 if none is provided. Duplicate sequence IDs will cause the role to fail. Input interface if, iif, iifname - Output interface of, oif, oifname - Protocol proto, pr, protocol - Protocol sub-type t, type - Protocol sub-code co, code - Destination Address/Network d, dest, target, destination, 'ip daddr', d6, dest6, target6, 'ip6 daddr' - Destination Port dp, port, dport, dest_port - Source Address/Network s, src, source, 'ip saddr', s6, src6, source6, 'ip6 saddr' - Source Port sp, sport, sport, src_port - Logging / Log message l, log, 'log prefix' If set to 'True' and a comment is provided, it will be used as a message. Otherwise, no message will be used Traffic counter count, counter If set to 'True', a rule-specific counter will be used. Otherwise, it will use the provided counter Traffic Limit lim, limit A limit for the rule, see: Anonymous Limits and Pre-defined Limits Rule action a, action Defaults to 'accept' if no action is provided. Source NAT masquerading m, masque, masquerade If NAT masquerading should be used Source NAT snat, src_nat, source_nat, outbound_nat, 'snat to' - Destination NAT dnat, dest_nat, destination_nat, 'dnat to' - Redirect redir, redirect, 'redirect to' Redirects packets to the local machine Rule comment c, cmt, comment - User user, uid Match traffic from specific users Group group, gid Match traffic from specific groups Firewall-Mark mark - Priority prio, priority - Packet length len, length - Timestamp time, timestamp Match the timestamp of packet reception Weekday day Match the day of the week (0 = Sunday to 6 = Saturday) Time hour Match 24-hour format "HH:MM:SS", seconds are optional You can only set one of Action, Source-NAT, Masquerading, or Destination-NAT for each rule!
Info: For special/complex rules that cannot be configured with the rule dictionary, use the 'raw' key to add any custom rule directly.
Info: You can define variables, sets, counters and limits at the table level.
- Variables are key-value pairs:
var-name: var-value var2-name: ['value1', 'value2']
- Sets have the following structure:
set-name: flags: [list-of-flags] # optional settings: setting: value # optional
- Counters are structured like this:
counter-name: comment: text # optional
- Limits are structured like this:
limit-name: rate: 'over 1024 bytes/second burst 512 bytes' comment: text # optional
- Variables are key-value pairs:
Warning: For 'count-only' rules, set 'action' explicitly to 'none'; otherwise, the default 'accept' will be added!
Info: If any unsupported field is provided to the rule translation, it will result in an error since it could yield unexpected results!
Info: Docker may require IPTables as a package dependency.
See: Use-Case Docker Host
Ansible Role to provision NFTables firewall on linux servers
ansible-galaxy install ansibleguy.infra_nftables