#firewall #nf-tables #rollback #linux #versioning

app nftguard

Atomic nftables ruleset versioning with rollback — invisible firewall guardian

1 stable release

Uses new Rust 2024

4.4.0 Apr 18, 2026

#611 in Network programming

Apache-2.0

62KB
1.5K SLoC

nftguard

Crates.io License

Atomic nftables ruleset versioning with rollback — invisible firewall guardian.

nftguard snapshots your nftables configuration every time you apply changes, compares rule fingerprints to detect drift, and lets you rollback to any previous state instantly. It runs as a transparent wrapper around nft and a systemd boot service, so your firewall is always versioned — without changing your workflow.

Features

  • Atomic snapshots — JSON snapshots with SHA-256 checksums, metadata, and per-rule fingerprints
  • Circular buffer — 100-slot ring buffer, oldest snapshots rotate out automatically
  • Fingerprint diffing — normalized rule comparison detects semantic changes (ignores counters, handles)
  • Retention guard — rejects bulk changes below 60% rule retention (prevents accidental wipes)
  • Safe hot-reloadhot-apply validates syntax, shows diff, selectively flushes tables, then applies
  • Boot protectionensure command loads the latest valid snapshot at boot; cascading fallback if corrupted
  • Rollback — restore any snapshot instantly: nftguard rollback 42
  • nft wrapper — transparent interception of nft -f calls (kube-proxy, scripts, manual)
  • Zero dependencies at runtime — single static binary, no daemons, no databases

Quick Start

Install from crates.io

cargo install nftguard

Install from source

git clone https://github.com/OnCeUponTry/nftguard.git
cd nftguard
cargo build --release
sudo install -m 755 target/release/nftguard /usr/local/sbin/nftguard

First use

# Apply your current nftables conf and create the first snapshot
sudo nftguard hot-apply /etc/nftables.conf

# List snapshots
sudo nftguard list

# Check kernel state
sudo nftguard status

# Rollback to the previous snapshot
sudo nftguard rollback

How It Works

                    ┌──────────────┐
 /etc/nftables.conf │  Your rules  │ ◄── You edit this
                    └──────┬───────┘
                           │
                    nftguard hot-apply
                           │
                    ┌──────▼───────┐
                    │  Syntax OK?  │──No──► REJECT (kernel unchanged)
                    └──────┬───────┘
                           │ Yes
                    ┌──────▼───────┐
                    │  Diff + show │ fingerprint comparison
                    └──────┬───────┘
                           │
                    ┌──────▼───────┐
                    │ Selective    │ flush only tables in conf
                    │ flush        │ (preserves DROP policies)
                    └──────┬───────┘
                           │
                    ┌──────▼───────┐
                    │ nft -f apply │──► Kernel updated
                    └──────┬───────┘
                           │
                    ┌──────▼───────┐
                    │  Snapshot    │──► /var/lib/nftguard/snapshot-N.json
                    └──────────────┘

Boot flow (ensure)

  1. If kernel already has rules → snapshot current state, done
  2. If kernel is empty → load latest snapshot
  3. If latest fails → try previous snapshots (circular)
  4. If all snapshots fail → try conf file
  5. If conf fails → try /etc/nftables.conf
  6. If everything fails → CRITICAL, exit 1

The boot service runs before network-pre.target, so your firewall is up before any interface.

Commands

Command Description
hot-apply [conf] Recommended. Validate → diff → selective flush → apply → snapshot
auto [conf] Automatic mode for nft wrapper. Applies if retention ≥ 60%
ensure Boot-only. Loads latest valid snapshot into empty kernel
rollback [N] Restore snapshot N (or previous if no N given)
compare [N] [M] Compare snapshots or snapshot vs conf file
list List all snapshots with timestamps, rule counts, hashes
status Show kernel state: tables, chains, latest snapshot info
config Show active configuration (paths, limits)
--version Show version

Configuration

Create /etc/nftguard/config (optional):

# Path to your nftables configuration file
conf_path=/etc/nftables.conf

If no config exists, nftguard auto-detects from common locations.

Default paths

Path Purpose
/etc/nftguard/config Configuration file
/var/lib/nftguard/ Snapshot storage (JSON)
/var/log/nftguard/ Log files
/usr/local/sbin/nftguard Binary

Systemd Integration

Install the boot service to protect your firewall across reboots:

sudo cp systemd/nftguard.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable nftguard.service

The service runs nftguard ensure before networking starts, guaranteeing your firewall rules are loaded even after crashes or failed updates.

nft Wrapper (Optional)

You can install nftguard as a transparent wrapper for nft -f:

# Create wrapper at /usr/local/sbin/nft (takes precedence over /usr/sbin/nft)
sudo tee /usr/local/sbin/nft << 'WRAPPER'
#!/bin/bash
# nftguard transparent wrapper
NFT_REAL="/usr/sbin/nft"
case "$*" in
  *-c*) exec "$NFT_REAL" "$@" ;;           # syntax check: pass through
  *-f\ -) nftguard auto_stdin ;;            # stdin: ephemeral (no snapshot)
  *-f\ *) conf="${*##*-f }"; conf="${conf%% *}"; nftguard auto "$conf" ;;
  *) exec "$NFT_REAL" "$@" ;;               # everything else: pass through
esac
WRAPPER
sudo chmod +x /usr/local/sbin/nft

This automatically versions every nft -f call from any source (scripts, kube-proxy, manual).

Snapshots

Each snapshot is a JSON file containing:

{
  "meta": {
    "timestamp": "2025-01-15 14:30:22",
    "sha256": "a1b2c3...",
    "line_count": 142,
    "chain_count": 8,
    "rule_count": 67,
    "table_names": ["inet filter", "ip nat"]
  },
  "fingerprints": [
    {
      "hash": "d4e5f6...",
      "original": "tcp dport 22 accept",
      "chain": "input",
      "table": "inet filter"
    }
  ],
  "content": "table inet filter {\n  chain input {\n    ..."
}

Requirements

  • Linux with nftables (nft v0.9.3+)
  • Root privileges (firewall management requires it)
  • Rust 1.70+ (build only)

License

Licensed under the Apache License, Version 2.0.

Copyright 2025 OnCeUponTry.

Dependencies

~2–3.5MB
~72K SLoC