Migration from iptables to nftables
This document describes the migration of all hosts from iptables to nftables firewall backend.
Status: COMPLETED
The migration to nftables has been completed for the WireGuard server module.
Summary
NixOS 23.11+ uses nftables as the default firewall backend. Since all hosts in this repository are using NixOS 25.11 or unstable, nftables is already the default backend.
Changes Made
1. Global nftables Enablement (systems/common/base/default.nix:25-32)
ADDED: Explicit global nftables enablement for all hosts
networking = {
hostName = hostname;
# Use nftables as the firewall backend (default since NixOS 23.11)
# Explicitly enabled for clarity and to ensure iptables is not used
nftables.enable = lib.mkDefault true;
};
Why: While NixOS 23.11+ uses nftables by default, explicitly enabling it ensures:
- iptables kernel modules are disabled
- All hosts consistently use nftables
- Clear intent in configuration
- Prevents accidental iptables usage
Impact: Affects all hosts in the repository.
Docker Compatibility: Hosts with Docker (aomi, kyushu, sakhalin) are compatible because:
- Modern Docker versions work with nftables
- aomi already has firewall disabled
- Docker will use nftables rules internally
2. WireGuard Server Module (modules/wireguard-server.nix:33-61)
CHANGED: Replaced iptables-specific rules with nftables configuration
Before:
networking.firewall.extraCommands = ''
iptables -t nat -A POSTROUTING -s10.100.0.0/32 -j MASQUERADE
iptables -A FORWARD -i wg+ -j ACCEPT
'';
After:
networking.nftables = {
enable = true;
tables = {
wireguard-nat = {
family = "ip";
content = ''
chain postrouting {
type nat hook postrouting priority 100; policy accept;
ip saddr 10.100.0.0/24 masquerade
}
'';
};
wireguard-filter = {
family = "inet";
content = ''
chain forward {
type filter hook forward priority 0; policy accept;
iifname "wg0" accept
oifname "wg0" accept
}
'';
};
};
};
Why: The module was using raw iptables commands via networking.firewall.extraCommands, which doesn’t work properly with nftables backend. The new configuration uses the native networking.nftables.tables option.
Impact: This change affects kerkouane (the WireGuard server/VPN endpoint).
2. WireGuard Client Module (modules/wireguard-client.nix)
NO CHANGES NEEDED: This module only uses high-level networking.firewall.trustedInterfaces option, which is backend-agnostic and works with both iptables and nftables.
3. Docker Configuration (systems/common/services/docker.nix)
NO CHANGES NEEDED: This configuration only uses:
networking.firewall.trustedInterfaces = [ "docker0" ]networking.firewall.checkReversePath = false
Both options are backend-agnostic.
4. Hosts with Firewall Disabled
These hosts have networking.firewall.enable = false and are unaffected:
- aomi: Firewall disabled, uses Docker
- athena: Firewall disabled
- demeter: Firewall disabled
- aix: Firewall disabled
5. Host Firewall Configurations
NO CHANGES NEEDED: All host-specific firewall configurations use high-level options:
networking.firewall.allowedTCPPortsnetworking.firewall.allowedUDPPortsnetworking.firewall.allowPingnetworking.firewall.trustedInterfaces
These are all backend-agnostic and automatically work with nftables.
Affected Hosts
All hosts now have networking.nftables.enable = true set globally.
WireGuard Server
- kerkouane: VPN endpoint using WireGuard server module (requires nftables rules)
WireGuard Clients
- rhea: Uses WireGuard client module (no changes needed)
- aion: Uses WireGuard client module (no changes needed)
- demeter: Uses WireGuard client module (no changes needed)
- athena: Uses WireGuard client module (no changes needed)
- aix: Uses WireGuard client module (no changes needed)
- sakhalin: Uses WireGuard client module (no changes needed)
Other Hosts
- aomi: No WireGuard configuration
- kyushu: No WireGuard configuration
Migration Verification
The configuration has been verified with:
nix build .#nixosConfigurations.kerkouane.config.system.build.toplevel --dry-run
Build succeeded without errors.
Deployment Plan
Phase 1: Deploy to kerkouane (READY)
# Build and deploy to kerkouane
make host/kerkouane/build
make host/kerkouane/switch
# Or build and activate on next boot
make host/kerkouane/boot
Phase 2: Verify VPN connectivity
After deploying to kerkouane:
- Verify all WireGuard clients can still connect
- Check that NAT/masquerading still works
- Test forwarding between VPN clients
- Verify no connection drops or issues
Phase 3: Monitor
- Check
journalctl -u wireguard-wg0for any issues - Monitor nftables rules:
nft list ruleset - Verify traffic flow with:
nft monitor
Testing Commands
On kerkouane after deployment:
# View current nftables ruleset
sudo nft list ruleset
# Check WireGuard interface status
sudo wg show
# Check WireGuard systemd service
systemctl status wireguard-wg0
# Monitor nftables events
sudo nft monitor
# Test VPN connectivity from a client
ping 10.100.0.1 # kerkouane VPN IP
Rollback Plan
If issues occur, rollback options:
- Quick rollback: Reboot and select previous generation from bootloader
- Revert commit:
git revert <commit-hash>and redeploy - Emergency iptables: Temporarily add to kerkouane’s extra.nix:
networking.firewall.enable = lib.mkForce false; networking.nftables.enable = lib.mkForce false; # Add custom iptables rules here
Technical Details
NAT Rule Translation
iptables:
iptables -t nat -A POSTROUTING -s 10.100.0.0/32 -j MASQUERADE
nftables:
table ip wireguard-nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
ip saddr 10.100.0.0/24 masquerade
}
}
Note: Fixed subnet from /32 to /24 to match actual VPN network.
Forwarding Rule Translation
iptables:
iptables -A FORWARD -i wg+ -j ACCEPT
nftables:
table inet wireguard-filter {
chain forward {
type filter hook forward priority 0; policy accept;
iifname "wg0" accept
oifname "wg0" accept
}
}
Note: Changed from wildcard wg+ to explicit wg0 since that’s the only WireGuard interface in use.
Benefits of nftables
- Native NixOS support: Default backend since NixOS 23.11
- Better performance: Single kernel subsystem vs multiple (iptables, ip6tables, ebtables, arptables)
- Cleaner syntax: More readable rule definitions
- Better atomicity: Rules can be updated atomically
- Improved debugging: Better error messages and monitoring