Packages

Elixir library for working with Linux nftables rules. Provides high-level APIs for building tables, chains, rules, maps and sets. Works with NFTables.Port for communicating with the kernel firewall.

Current section

Files

Jump to
nftables examples README.md
Raw

examples/README.md

# NFTables Examples
This directory contains practical examples demonstrating how to use NFTables for common nftables operations.
## Prerequisites
Before running these examples, ensure the NFTables port binary has the required capability:
```bash
sudo setcap cap_net_admin=ep priv/port_nftables
```
Verify it's set correctly:
```bash
getcap priv/port_nftables
# Should show: priv/port_nftables = cap_net_admin+ep
```
## Running Examples
From the project root directory:
```bash
# Run examples in order (recommended for first-time setup)
mix run examples/01_sysctl_management.exs
mix run examples/02_basic_firewall.exs
mix run examples/03_firewall_rules.exs
# ... etc
# Or run a specific example
mix run examples/04_ip_blocklist.exs
# Or make executable and run directly
chmod +x examples/04_ip_blocklist.exs
./examples/04_ip_blocklist.exs
```
## Available Examples
### Current API Examples (v0.4.0+)
These examples use the current JSON-based API (libnftables) and are ordered in the logical sequence for setting up a complete firewall from scratch:
#### 1. Sysctl Management (`01_sysctl_management.exs`)
**First Step:** Configure kernel network parameters before setting up firewall rules.
Demonstrates safe management of Linux kernel network parameters via NFTables's sysctl API.
**Topics covered:**
- Reading current network parameters (IPv4/IPv6 forwarding, TCP settings, etc.)
- Writing network parameters with validation
- High-level helpers (`Network.enable_ipv4_forwarding/1`, etc.)
- Composite operations (`configure_router/2`, `harden_security/1`)
- Parameter whitelist security
- Value validation and restoration
**Use case:** Router/gateway configuration, security hardening, network optimization, connection tracking tuning
#### 2. Basic Firewall (`02_basic_firewall.exs`)
**Second Step:** Set up the foundational firewall structure with secure defaults.
Complete secure firewall setup with defense-in-depth approach.
**Topics covered:**
- Default DROP policy
- Loopback traffic acceptance
- Established/related connection tracking
- Invalid packet dropping
- SSH rate limiting
- High-level Policy module usage
**Use case:** Secure server baseline, VPS hardening
#### 3. Firewall Rules (`03_firewall_rules.exs`)
**Third Step:** Add specific allow/block rules for trusted or malicious IPs.
Demonstrates creating dynamic firewall rules to block malicious IPs and allow trusted sources.
**Topics covered:**
- Using `NFTables.Rule.block_ip/4` for simple IP blocking
- Using `NFTables.Rule.accept_ip/4` for allowlist rules
- Listing rules with `NFTables.Rule.list/4`
- Automatic counter addition for traffic monitoring
- Dynamic rule creation without system restart
**Use case:** IDS integration, dynamic IP blocking, security incident response
#### 4. IP Blocklist (`04_ip_blocklist.exs`)
**Fourth Step:** Set up efficient dynamic IP blocking using nftables sets.
Demonstrates how to create and manage an IP address blocklist using nftables sets.
**Topics covered:**
- Creating sets in the kernel
- Adding multiple IP addresses (string format)
- Listing blocked IPs
- Removing IPs from blocklist
- Checking if sets exist
**Use case:** Dynamic IP blocklisting for firewall applications
#### 5. Rate Limiting (`05_rate_limiting.exs`)
**Fifth Step:** Add DDoS protection through rate limiting.
DDoS protection and resource management through rate limiting.
**Topics covered:**
- Per-service rate limits (SSH, HTTP, ICMP)
- New connection rate limiting
- Burst handling
- SYN flood protection
- Match API usage
**Use case:** Public-facing servers, API endpoints, DDoS mitigation
#### 6. Query Tables (`06_query_tables.exs`)
**Sixth Step:** Query and inspect your firewall configuration.
Shows how to query and inspect your current nftables configuration.
**Topics covered:**
- Listing all tables
- Enumerating chains (base and regular)
- Viewing sets and their properties
- Listing rules
- Examining set elements
**Use case:** Auditing firewall configuration, building management dashboards
### Future Examples
Additional examples for advanced features are planned:
- NAT Gateway and port forwarding
- Anti-spoofing with FIB expressions
- Advanced logging configurations
- Load balancing with DNAT
These will be implemented using the current v0.4.0 API (`JSONBuilder` with nft syntax strings).
## API Quick Reference
### NFTables.Policy - Pre-built Firewall Policies (New in 0.3.0)
High-level functions for common firewall configurations:
```elixir
alias NFTables.{Policy, Builder}
{:ok, pid} = NFTables.start_link()
# Quick setup: Complete basic firewall in one call
:ok = NFTables.Policy.setup_basic_firewall(pid,
allow_services: [:ssh, :http, :https],
ssh_rate_limit: 10
)
# Individual policies (composable - all in one transaction)
:ok =
Builder.new()
|> Policy.accept_loopback()
|> Policy.accept_established()
|> Policy.drop_invalid()
|> Builder.submit(pid: pid)
# Service-specific allows (composable)
:ok =
Builder.new()
|> Policy.allow_ssh(rate_limit: 10, log: true)
|> Policy.allow_http(rate_limit: 100)
|> Policy.allow_https()
|> Policy.allow_dns()
|> Builder.submit(pid: pid)
```
### NFTables.Match - Fluent API for Rules
Chainable API for building complex rules intuitively:
```elixir
alias NFTables.Match
# Block IP with logging
Match.new(pid, "filter", "INPUT")
|> Match.source_ip("192.168.1.100")
|> Match.log("BLOCKED: ")
|> Match.drop()
|> Match.commit()
# Rate-limited SSH
Match.new(pid, "filter", "INPUT")
|> Match.dest_port(22)
|> Match.rate_limit(10, :minute, burst: 20)
|> Match.counter()
|> Match.accept()
|> Match.commit()
# Match established connections
Match.new(pid, "filter", "INPUT")
|> Match.ct_state([:established, :related])
|> Match.accept()
|> Match.commit()
# Interface-specific rules
Match.new(pid, "filter", "INPUT")
|> Match.iif("eth0")
|> Match.source_ip("10.0.0.0")
|> Match.reject(:icmp_port_unreachable)
|> Match.commit()
```
**Available match functions:**
- `match_source_ip/2` - Match source IP address
- `match_dest_ip/2` - Match destination IP address
- `match_source_port/2` - Match source port
- `match_dest_port/2` - Match destination port
- `match_ct_state/2` - Match connection tracking state (`:established`, `:related`, `:new`, `:invalid`)
- `match_iif/2` - Match input interface
- `match_oif/2` - Match output interface
**Available action functions:**
- `counter/1` - Add packet/byte counter
- `log/2` - Log packets with prefix
- `rate_limit/3` - Rate limit (rate, unit, opts)
**Available verdict functions:**
- `accept/1` - Accept packets
- `drop/1` - Drop packets silently
- `reject/1` - Reject with ICMP error
### NFTables.Chain - Chain Management (New in 0.3.0)
High-level chain operations with automatic resource management:
```elixir
# Create base chain (hooked into netfilter)
:ok = NFTables.Chain.add(pid, %{
table: "filter",
name: "INPUT",
family: :inet,
type: :filter,
hook: :input,
priority: 0,
policy: :drop
})
# Create regular chain (for organizing rules)
:ok = NFTables.Chain.add(pid, %{
table: "filter",
name: "my_custom_rules",
family: :inet
})
# List all chains
{:ok, chains} = NFTables.Chain.list(pid, family: :inet)
# Check if chain exists
if NFTables.Chain.exists?(pid, "filter", "INPUT", :inet) do
IO.puts("Chain exists")
end
# Set chain policy
:ok = NFTables.Chain.set_policy(pid, "filter", "INPUT", :inet, :drop)
# Delete chain
:ok = NFTables.Chain.delete(pid, "filter", "INPUT", :inet)
```
### NFTables.Rule - High-level rule operations
```elixir
{:ok, pid} = NFTables.start_link()
# Block an IP address
ip = "192.168.1.100"
:ok = NFTables.Rule.block_ip(pid, "filter", "INPUT", ip)
# Accept an IP address
:ok = NFTables.Rule.accept_ip(pid, "filter", "INPUT", ip)
# List rules in a chain
{:ok, rules} = NFTables.Rule.list(pid, "filter", "INPUT", family: :inet)
```
### NFTables.Set - High-level set operations
```elixir
{:ok, pid} = NFTables.start_link()
# Add elements to existing set (string format)
ips = ["192.168.1.100", "10.0.0.50"]
:ok = NFTables.Set.add_elements(pid, "filter", "blocklist", :inet, ips)
# Delete elements
:ok = NFTables.Set.delete_elements(pid, "filter", "blocklist", :inet, ips)
# List elements
{:ok, elements} = NFTables.Set.list_elements(pid, "filter", "blocklist")
# Check if set exists
exists = NFTables.Set.exists?(pid, "filter", "blocklist", :inet)
# List all sets
{:ok, sets} = NFTables.Set.list(pid, family: :inet)
```
### NFTables.Query - Query operations
```elixir
{:ok, pid} = NFTables.start_link()
# List tables
{:ok, tables} = NFTables.Query.list_tables(pid, family: :inet)
# List chains
{:ok, chains} = NFTables.Query.list_chains(pid, family: :inet)
# List sets
{:ok, sets} = NFTables.Query.list_sets(pid, family: :inet)
# List rules
{:ok, rules} = NFTables.Query.list_rules(pid, family: :inet)
# List set elements
{:ok, elements} = NFTables.Query.list_set_elements(pid, "filter", "blocklist")
```
### NFTables.Sysctl - Network Parameter Management (New in 0.5.0)
Safe, whitelist-based access to kernel network parameters:
```elixir
alias NFTables.{Sysctl, Sysctl.Network}
{:ok, pid} = NFTables.start_link()
# Low-level API - Direct parameter access
{:ok, "0"} = Sysctl.get(pid, "net.ipv4.ip_forward")
:ok = Sysctl.set(pid, "net.ipv4.ip_forward", "1")
# High-level helpers for common operations
:ok = Network.enable_ipv4_forwarding(pid)
:ok = Network.enable_ipv6_forwarding(pid)
:ok = Network.enable_syncookies(pid)
# Check status
{:ok, true} = Network.ipv4_forwarding_enabled?(pid)
# Connection tracking
:ok = Network.set_conntrack_max(pid, 131072)
{:ok, 131072} = Network.get_conntrack_max(pid)
# ICMP configuration
:ok = Network.ignore_ping(pid) # Stealth mode
:ok = Network.allow_ping(pid) # Normal mode
# Composite operations
:ok = Network.configure_router(pid,
ipv4_forwarding: true,
ipv6_forwarding: true,
syncookies: true,
send_redirects: false
)
:ok = Network.harden_security(pid)
```
**Security Features:**
- 44 whitelisted network parameters only
- Value validation per parameter type
- Limited to `/proc/sys/net/*`
- Uses existing CAP_NET_ADMIN capability
**Supported Parameter Categories:**
- IPv4/IPv6 forwarding and configuration
- TCP settings (syncookies, timestamps, keepalive, port ranges)
- Connection tracking (nf_conntrack_max, timeouts)
- ICMP settings (echo ignore, rate limits)
- Security parameters (rp_filter, source routing, ICMP redirects)
## Common Patterns
### IP Address Format
```elixir
# Use string format for IP addresses (v0.4.0+)
ip = "192.168.1.100"
:ok = NFTables.Rule.block_ip(pid, "filter", "INPUT", ip)
# NFTables.Query automatically converts hex keys to readable IPs
{:ok, elements} = NFTables.Set.list_elements(pid, "filter", "blocklist")
for elem <- elements do
IO.puts(elem.key_ip) # "192.168.1.100"
end
```
### Error Handling
```elixir
case NFTables.Set.add_elements(pid, "filter", "blocklist", :inet, ips) do
:ok ->
IO.puts("IPs blocked successfully")
{:error, reason} ->
IO.puts("Failed to block IPs: #{reason}")
end
```
### Protocol Families
```elixir
:inet # IPv4 (2)
:inet6 # IPv6 (10)
:ip # IPv4 (alias for :inet)
:ip6 # IPv6 (alias for :inet6)
:arp # ARP (3)
:bridge # Bridge (7)
:netdev # Netdev (5)
```
## Integration with nftables Rules
After creating a set with NFTables, use it in nftables rules:
```bash
# Block IPs in the blocklist
nft add rule filter input ip saddr @banned_ips drop
# Allow only whitelisted IPs
nft add rule filter input ip saddr @allowed_ips accept
nft add rule filter input drop
```
## Troubleshooting
### "Operation not permitted"
Ensure CAP_NET_ADMIN capability is set:
```bash
sudo setcap cap_net_admin=ep priv/port_nftables
```
### "Set not found"
Create the set first using the low-level API or `nft` command:
```bash
nft add set filter blocklist { type ipv4_addr\; }
```
### "Port failed to start"
Rebuild the native code:
```bash
cd native && zig build && cd ..
```
## Next Steps
- Read the module documentation: `h NFTables.Set` and `h NFTables.Query`
- Explore the test files in `/tmp/test_*.exs` for more examples
- Check out the main project README for advanced usage