Configure nftables with IPv6 NAT masquerading, dual-stack IPv4/IPv6 forwarding, and comprehensive firewall rules for secure network routing and traffic management on modern Linux systems.
Prerequisites
- Root access to Linux server
- Basic understanding of network concepts
- Two network interfaces (physical or virtual)
- IPv6-enabled network environment
What this solves
IPv6 adoption requires proper network address translation and dual-stack configuration to ensure seamless connectivity between IPv4 and IPv6 networks. This tutorial configures nftables with IPv6 NAT masquerading, traffic forwarding between network interfaces, and comprehensive firewall rules for secure dual-stack networking.
Step-by-step configuration
Install nftables and enable IPv6 kernel modules
Install nftables package and verify IPv6 support is enabled in the kernel for dual-stack networking.
sudo apt update
sudo apt install -y nftables
sudo systemctl enable --now nftables
sudo modprobe ip6_tables
sudo modprobe ip6table_nat
echo 'ip6_tables' | sudo tee -a /etc/modules
echo 'ip6table_nat' | sudo tee -a /etc/modules
Enable IPv4 and IPv6 forwarding
Configure kernel parameters to enable packet forwarding between network interfaces for dual-stack routing.
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.default.forwarding = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
sudo sysctl -p /etc/sysctl.d/99-ip-forwarding.conf
sudo sysctl net.ipv4.ip_forward
sudo sysctl net.ipv6.conf.all.forwarding
Create nftables configuration with dual-stack tables
Configure separate tables for IPv4 and IPv6 traffic processing with input, forward, and output chains.
#!/usr/sbin/nft -f
Clear all existing rules
flush ruleset
IPv4 table configuration
table ip filter {
chain input {
type filter hook input priority filter; policy drop;
# Allow loopback traffic
iif "lo" accept
# Allow established and related connections
ct state established,related accept
# Allow ICMP for network diagnostics
ip protocol icmp accept
# Allow SSH access (adjust port if needed)
tcp dport 22 accept
# Allow HTTP/HTTPS
tcp dport { 80, 443 } accept
# Log dropped packets
log prefix "nftables input drop: " drop
}
chain forward {
type filter hook forward priority filter; policy drop;
# Allow established and related connections
ct state established,related accept
# Allow forwarding from internal to external
iifname "eth1" oifname "eth0" accept
# Allow forwarding from external to internal (established)
iifname "eth0" oifname "eth1" ct state established,related accept
# Log dropped forwards
log prefix "nftables forward drop: " drop
}
chain output {
type filter hook output priority filter; policy accept;
}
}
IPv4 NAT table
table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
# Port forwarding example (HTTP to internal server)
iifname "eth0" tcp dport 8080 dnat to 192.168.1.100:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
# Masquerade outgoing traffic
oifname "eth0" masquerade
}
}
IPv6 table configuration
table ip6 filter {
chain input {
type filter hook input priority filter; policy drop;
# Allow loopback traffic
iif "lo" accept
# Allow established and related connections
ct state established,related accept
# Allow ICMPv6 for neighbor discovery and diagnostics
ip6 nexthdr ipv6-icmp accept
# Allow SSH access
tcp dport 22 accept
# Allow HTTP/HTTPS
tcp dport { 80, 443 } accept
# Allow DHCPv6
udp dport 546 accept
# Log dropped packets
log prefix "nftables ipv6 input drop: " drop
}
chain forward {
type filter hook forward priority filter; policy drop;
# Allow established and related connections
ct state established,related accept
# Allow forwarding from internal to external
iifname "eth1" oifname "eth0" accept
# Allow forwarding from external to internal (established)
iifname "eth0" oifname "eth1" ct state established,related accept
# Log dropped forwards
log prefix "nftables ipv6 forward drop: " drop
}
chain output {
type filter hook output priority filter; policy accept;
}
}
IPv6 NAT table (requires kernel 3.7+)
table ip6 nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
# IPv6 port forwarding example
iifname "eth0" tcp dport 8080 dnat to [2001:db8::100]:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
# IPv6 masquerading for outgoing traffic
oifname "eth0" masquerade
}
}
Configure network interfaces for dual-stack
Set up network interfaces with both IPv4 and IPv6 addresses using Netplan configuration.
network:
version: 2
renderer: networkd
ethernets:
eth0:
dhcp4: true
dhcp6: true
accept-ra: true
link-local: [ipv4, ipv6]
eth1:
addresses:
- 192.168.1.1/24
- 2001:db8:1::1/64
dhcp4: false
dhcp6: false
accept-ra: false
sudo netplan apply
sudo networkctl status
Apply nftables configuration and enable service
Load the nftables configuration and verify rules are applied correctly for both IPv4 and IPv6 tables.
sudo nft -f /etc/nftables.conf
sudo systemctl restart nftables
sudo systemctl status nftables
Configure IPv6 router advertisements (optional)
Set up router advertisements for IPv6 prefix delegation on the internal network interface.
sudo apt install -y radvd
interface eth1 {
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
prefix 2001:db8:1::/64 {
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr on;
};
RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {
AdvRDNSSLifetime 300;
};
};
sudo systemctl enable --now radvd
Add advanced security rules
Implement additional security measures including rate limiting and connection tracking limits.
sudo nft add rule ip filter input tcp dport 22 ct state new limit rate 5/minute accept
sudo nft add rule ip6 filter input tcp dport 22 ct state new limit rate 5/minute accept
sudo nft add rule ip filter input ct state invalid drop
sudo nft add rule ip6 filter input ct state invalid drop
Create nftables management script
Create a script for easy management of nftables rules and dual-stack configuration.
#!/bin/bash
nftables dual-stack management script
case "$1" in
status)
echo "=== IPv4 Tables ==="
nft list table ip filter
nft list table ip nat
echo "=== IPv6 Tables ==="
nft list table ip6 filter
nft list table ip6 nat
;;
reload)
echo "Reloading nftables configuration..."
nft -f /etc/nftables.conf
systemctl reload nftables
echo "Configuration reloaded"
;;
connections)
echo "=== Active connections ==="
nft list table ip nat
nft list table ip6 nat
;;
*)
echo "Usage: $0 {status|reload|connections}"
exit 1
;;
esac
sudo chmod 755 /usr/local/bin/nftables-manager.sh
Verify your setup
# Check nftables service status
sudo systemctl status nftables
Verify IPv4 and IPv6 forwarding is enabled
sudo sysctl net.ipv4.ip_forward
sudo sysctl net.ipv6.conf.all.forwarding
List all nftables rules
sudo nft list ruleset
Test IPv4 connectivity
ping -c 3 8.8.8.8
Test IPv6 connectivity
ping6 -c 3 2001:4860:4860::8888
Check network interfaces
ip addr show
ip -6 addr show
Verify NAT table entries
sudo /usr/local/bin/nftables-manager.sh status
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| IPv6 NAT rules not working | Kernel doesn't support IPv6 NAT | Check uname -r, upgrade to kernel 3.7+ |
| No IPv6 connectivity | Forwarding disabled or missing modules | Verify net.ipv6.conf.all.forwarding=1 and load ip6_tables |
| Connection refused on forwarded ports | Incorrect NAT rules or firewall blocking | Check DNAT rules and verify destination service is running |
| nftables service fails to start | Syntax error in configuration | Run nft -c -f /etc/nftables.conf to check syntax |
| Internal clients can't reach internet | Missing masquerade rules | Verify postrouting masquerade rules for both IPv4 and IPv6 |
| Router advertisements not working | radvd misconfiguration | Check journalctl -u radvd and verify interface names |
accept without proper filtering. Always implement specific rules based on required services and apply the principle of least privilege.Next steps
- Configure advanced traffic classification and QoS with nftables
- Extend nftables NAT configuration for complex home lab scenarios
- Set up NGINX reverse proxy with SSL for secure dual-stack web services
- Monitor dual-stack network performance with SNMP and Grafana
- Configure WireGuard VPN server with dual-stack IPv4/IPv6 support
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration variables
INTERNAL_INTERFACE="${1:-eth1}"
EXTERNAL_INTERFACE="${2:-eth0}"
INTERNAL_NETWORK="${3:-192.168.1.0/24}"
INTERNAL_IPV6_PREFIX="${4:-fd00::/64}"
# Print colored output
print_status() { echo -e "${GREEN}[INFO]${NC} $1"; }
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Usage message
usage() {
echo "Usage: $0 [internal_interface] [external_interface] [internal_network] [internal_ipv6_prefix]"
echo " internal_interface: Internal network interface (default: eth1)"
echo " external_interface: External network interface (default: eth0)"
echo " internal_network: Internal IPv4 network (default: 192.168.1.0/24)"
echo " internal_ipv6_prefix: Internal IPv6 prefix (default: fd00::/64)"
exit 1
}
# Check prerequisites
check_prerequisites() {
if [[ $EUID -ne 0 ]]; then
print_error "This script must be run as root"
exit 1
fi
if ! command -v systemctl >/dev/null 2>&1; then
print_error "systemd is required"
exit 1
fi
}
# Cleanup function
cleanup() {
print_warning "Script failed. Rolling back changes..."
systemctl stop nftables 2>/dev/null || true
rm -f /etc/nftables.conf.backup
if [ -f /etc/nftables.conf.original ]; then
mv /etc/nftables.conf.original /etc/nftables.conf 2>/dev/null || true
fi
}
# Detect distribution
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
;;
*)
print_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
print_error "Cannot detect distribution"
exit 1
fi
}
# Set up error handling
trap cleanup ERR
# Main execution
print_status "Starting nftables IPv6 NAT and dual-stack networking setup"
check_prerequisites
detect_distro
echo "[1/7] Installing nftables package..."
$PKG_UPDATE
$PKG_INSTALL nftables
echo "[2/7] Loading IPv6 kernel modules..."
modprobe ip6_tables 2>/dev/null || true
modprobe ip6table_nat 2>/dev/null || true
# Add modules to persistent configuration
if [ "$PKG_MGR" = "apt" ]; then
echo 'ip6_tables' >> /etc/modules
echo 'ip6table_nat' >> /etc/modules
else
echo 'ip6_tables' >> /etc/modules-load.d/nftables.conf
echo 'ip6table_nat' >> /etc/modules-load.d/nftables.conf
fi
echo "[3/7] Configuring kernel parameters for IP forwarding..."
cat > /etc/sysctl.d/99-ip-forwarding.conf << EOF
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.default.forwarding = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
EOF
sysctl -p /etc/sysctl.d/99-ip-forwarding.conf
echo "[4/7] Backing up existing nftables configuration..."
if [ -f /etc/nftables.conf ]; then
cp /etc/nftables.conf /etc/nftables.conf.original
fi
echo "[5/7] Creating nftables dual-stack configuration..."
cat > /etc/nftables.conf << EOF
#!/usr/sbin/nft -f
# Clear all existing rules
flush ruleset
# IPv4 table configuration
table ip filter {
chain input {
type filter hook input priority filter; policy drop;
# Allow loopback traffic
iif "lo" accept
# Allow established and related connections
ct state established,related accept
# Allow ICMP for network diagnostics
ip protocol icmp accept
# Allow SSH access
tcp dport 22 accept
# Allow HTTP/HTTPS
tcp dport { 80, 443 } accept
# Log dropped packets
log prefix "nftables input drop: " drop
}
chain forward {
type filter hook forward priority filter; policy drop;
# Allow established and related connections
ct state established,related accept
# Allow forwarding from internal to external
iifname "$INTERNAL_INTERFACE" oifname "$EXTERNAL_INTERFACE" accept
# Allow forwarding from external to internal (established)
iifname "$EXTERNAL_INTERFACE" oifname "$INTERNAL_INTERFACE" ct state established,related accept
# Log dropped forwards
log prefix "nftables forward drop: " drop
}
chain output {
type filter hook output priority filter; policy accept;
}
}
# IPv4 NAT table
table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
# Masquerade outgoing traffic
oifname "$EXTERNAL_INTERFACE" masquerade
}
}
# IPv6 table configuration
table ip6 filter {
chain input {
type filter hook input priority filter; policy drop;
# Allow loopback traffic
iif "lo" accept
# Allow established and related connections
ct state established,related accept
# Allow ICMPv6 for neighbor discovery and diagnostics
ip6 nexthdr ipv6-icmp accept
# Allow SSH access
tcp dport 22 accept
# Allow HTTP/HTTPS
tcp dport { 80, 443 } accept
# Allow DHCPv6
udp dport 546 accept
# Log dropped packets
log prefix "nftables ipv6 input drop: " drop
}
chain forward {
type filter hook forward priority filter; policy drop;
# Allow established and related connections
ct state established,related accept
# Allow forwarding from internal to external
iifname "$INTERNAL_INTERFACE" oifname "$EXTERNAL_INTERFACE" accept
# Allow forwarding from external to internal (established)
iifname "$EXTERNAL_INTERFACE" oifname "$INTERNAL_INTERFACE" ct state established,related accept
# Log dropped forwards
log prefix "nftables ipv6 forward drop: " drop
}
chain output {
type filter hook output priority filter; policy accept;
}
}
# IPv6 NAT table
table ip6 nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
# Masquerade outgoing IPv6 traffic
oifname "$EXTERNAL_INTERFACE" masquerade
}
}
EOF
chmod 644 /etc/nftables.conf
echo "[6/7] Enabling and starting nftables service..."
systemctl enable nftables
systemctl restart nftables
echo "[7/7] Verifying configuration..."
if systemctl is-active --quiet nftables; then
print_status "nftables service is running"
else
print_error "nftables service failed to start"
exit 1
fi
# Verify kernel parameters
if [[ $(sysctl -n net.ipv4.ip_forward) == "1" ]] && [[ $(sysctl -n net.ipv6.conf.all.forwarding) == "1" ]]; then
print_status "IP forwarding is enabled for both IPv4 and IPv6"
else
print_warning "IP forwarding may not be properly configured"
fi
# Display current rules
print_status "Current nftables ruleset:"
nft list ruleset
print_status "nftables IPv6 NAT and dual-stack networking setup completed successfully!"
print_status "Configuration details:"
print_status " Internal interface: $INTERNAL_INTERFACE"
print_status " External interface: $EXTERNAL_INTERFACE"
print_status " Internal IPv4 network: $INTERNAL_NETWORK"
print_status " Internal IPv6 prefix: $INTERNAL_IPV6_PREFIX"
# Remove trap on successful completion
trap - ERR
Review the script before running. Execute with: bash install.sh