NFTables.Policy (NFTables v0.8.2)
View SourcePre-built firewall policies and common rule patterns.
This module provides high-level functions for common firewall configurations, making it easy to set up secure defaults without low-level rule management.
All functions follow a builder-first pattern, taking a Builder as the first parameter and returning a modified Builder. This allows composing multiple policy rules before submitting them in a single transaction.
Quick Start
{:ok, pid} = NFTables.start_link()
# Create table and chain
Builder.new()
|> NFTables.add(table: "filter", family: :inet)
|> NFTables.add(
table: "filter",
chain: "INPUT",
family: :inet,
type: :filter,
hook: :input,
priority: 0,
policy: :drop
)
|> NFTables.submit(pid: pid)
# Apply common policies
Builder.new()
|> NFTables.Policy.accept_loopback()
|> NFTables.Policy.accept_established()
|> NFTables.Policy.allow_ssh()
|> NFTables.submit(pid: pid)See Also
NFTables.Expr- Fluent API for custom rulesNFTables.Builder- Configuration builderNFTables.Local- Local execution requestor
Summary
Functions
Accept established and related connections.
Accept all loopback traffic.
Accept all traffic.
Allow DNS queries (port 53, UDP).
Allow HTTP connections (port 80).
Allow HTTPS connections (port 443).
Allow SSH connections (port 22).
Drop all traffic.
Drop invalid packets.
Setup basic firewall with common defaults.
Setup stateful firewall rules.
Functions
@spec accept_established( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Accept established and related connections.
This allows return traffic for existing connections, essential for any stateful firewall.
Examples
Builder.new()
|> NFTables.Policy.accept_established()
|> NFTables.submit(pid: pid)
# With custom table
Builder.new()
|> NFTables.Policy.accept_established(table: "myfilter")
|> NFTables.submit(pid: pid)
@spec accept_loopback( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Accept all loopback traffic.
Loopback traffic (lo interface) should always be accepted as it's internal system communication.
Examples
# Single rule
Builder.new()
|> NFTables.Policy.accept_loopback()
|> NFTables.submit(pid: pid)
# Compose with other policies
Builder.new()
|> NFTables.Policy.accept_loopback(table: "filter")
|> NFTables.Policy.accept_established(table: "filter")
|> NFTables.submit(pid: pid)
@spec allow_any( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Accept all traffic.
Creates a rule that accepts all packets without any matching criteria. Useful as a catch-all rule or for testing purposes.
Warning: This creates a permissive rule. Use with caution in production.
Options
:table- Table name (default: "filter"):chain- Chain name (default: "INPUT"):family- Protocol family (default: :inet):log- Log accepted packets (default: false)
Examples
Builder.new()
|> NFTables.Policy.allow_any()
|> NFTables.submit(pid: pid)
# With logging
Builder.new()
|> NFTables.Policy.allow_any(log: true)
|> NFTables.submit(pid: pid)
@spec allow_dns( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Allow DNS queries (port 53, UDP).
Examples
Builder.new()
|> NFTables.Policy.allow_dns()
|> NFTables.submit(pid: pid)
@spec allow_http( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Allow HTTP connections (port 80).
Options
:rate_limit- Limit connections per minute:log- Log accepted connections:table- Table name (default: "filter"):chain- Chain name (default: "INPUT"):family- Protocol family (default: :inet)
Examples
Builder.new()
|> NFTables.Policy.allow_http()
|> NFTables.submit(pid: pid)
# With rate limiting
Builder.new()
|> NFTables.Policy.allow_http(rate_limit: 100)
|> NFTables.submit(pid: pid)
@spec allow_https( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Allow HTTPS connections (port 443).
Examples
Builder.new()
|> NFTables.Policy.allow_https()
|> NFTables.submit(pid: pid)
# Compose HTTP and HTTPS
Builder.new()
|> NFTables.Policy.allow_http()
|> NFTables.Policy.allow_https()
|> NFTables.submit(pid: pid)
@spec allow_ssh( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Allow SSH connections (port 22).
Options
:rate_limit- Limit connections per minute (default: no limit):log- Log accepted connections (default: false):table- Table name (default: "filter"):chain- Chain name (default: "INPUT"):family- Protocol family (default: :inet)
Examples
# Basic SSH allow
Builder.new()
|> NFTables.Policy.allow_ssh()
|> NFTables.submit(pid: pid)
# With rate limiting
Builder.new()
|> NFTables.Policy.allow_ssh(rate_limit: 10)
|> NFTables.submit(pid: pid)
# With logging
Builder.new()
|> NFTables.Policy.allow_ssh(log: true)
|> NFTables.submit(pid: pid)
# Compose multiple services
Builder.new()
|> NFTables.Policy.allow_ssh(rate_limit: 10)
|> NFTables.Policy.allow_http()
|> NFTables.Policy.allow_https()
|> NFTables.submit(pid: pid)
@spec deny_all( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Drop all traffic.
Creates a rule that drops all packets without any matching criteria. Useful as a catch-all deny rule at the end of a chain or for testing.
Options
:table- Table name (default: "filter"):chain- Chain name (default: "INPUT"):family- Protocol family (default: :inet):log- Log dropped packets (default: false)
Examples
Builder.new()
|> NFTables.Policy.deny_all()
|> NFTables.submit(pid: pid)
# With logging
Builder.new()
|> NFTables.Policy.deny_all(log: true)
|> NFTables.submit(pid: pid)
@spec drop_invalid( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Drop invalid packets.
Drops packets with invalid connection tracking state.
Examples
Builder.new()
|> NFTables.Policy.drop_invalid()
|> NFTables.submit(pid: pid)
Setup basic firewall with common defaults.
Creates a table and INPUT chain with these rules:
- Accept loopback
- Accept established/related
- Drop invalid packets
- Allow specified services (default: SSH with rate limiting)
- Default policy: DROP (only in production mode)
This is a convenience function that still takes a pid and executes
immediately, as it needs to create infrastructure (table and chain) before
applying policies. The policy rules themselves are composed using the
builder pattern internally.
Options
:table- Table name (default: "filter"):family- Protocol family (default: :inet):ssh_rate_limit- SSH connections per minute (default: 10):allow_services- List of services to allow (default: [:ssh]):test_mode- If true, creates chains WITHOUT hooks (safe for testing) (default: false)
Test Mode
IMPORTANT: When test_mode: true, chains are created WITHOUT netfilter hooks.
This prevents the chains from filtering actual network traffic, making tests safe.
In test mode, the table name is automatically prefixed with "nftablestest" if not already prefixed.
Examples
# Production use (creates hooked chains that filter traffic)
:ok = NFTables.Policy.setup_basic_firewall(pid)
:ok = NFTables.Policy.setup_basic_firewall(pid, allow_services: [:ssh, :http, :https])
# Test use (creates regular chains without hooks - SAFE)
:ok = NFTables.Policy.setup_basic_firewall(pid, test_mode: true, table: "my_test")
@spec stateful( NFTables.Builder.t(), keyword() ) :: NFTables.Builder.t()
Setup stateful firewall rules.
Combines accept_established/2 and drop_invalid/2 to set up basic
connection tracking rules. This is essential for any stateful firewall,
allowing return traffic for established connections while dropping
packets with invalid connection tracking state.
Options
:table- Table name (default: "filter"):chain- Chain name (default: "INPUT"):family- Protocol family (default: :inet)
Examples
Builder.new()
|> NFTables.Policy.stateful()
|> NFTables.submit(pid: pid)
# With custom options
Builder.new()
|> NFTables.Policy.stateful(table: "filter", chain: "INPUT")
|> NFTables.submit(pid: pid)