Skip to main content

Next-Generation Firewall (NGFW) Complete Guide 2026: nftables, Suricata, and OPNsense

Created: March 2, 2026 Larry Qu 5 min read

Introduction

A Next-Generation Firewall (NGFW) goes beyond port/protocol filtering to include application-layer inspection, intrusion prevention (IDS/IPS), SSL/TLS decryption, and threat intelligence integration. While commercial appliances (Palo Alto, Fortinet) dominate the enterprise market, the same capabilities can be built using open-source components: nftables for stateful filtering with dynamic sets, Suricata for IDS/IPS with emerging-threats rules, and OPNsense for a unified management interface with VLANs, traffic shaping, and captive portal.

This guide covers building an NGFW policy architecture with nftables (including dynamic allowlists/blocklists), deploying Suricata in IPS mode with rule management, configuring OPNsense as a complete NGFW with VLAN segmentation and traffic shaping, and a reference policy design for zero trust network segmentation.

nftables: Modern Linux Stateful Firewall

nftables is the successor to iptables, providing a cleaner syntax and native support for sets, maps, and dynamic rule updates — essential for NGFW capabilities.

Basic Stateful Policy with Application Filtering

#!/usr/bin/nft -f

table inet ngfw {
    # Define sets for dynamic allow/block lists
    set blocklist {
        type ipv4_addr
        flags timeout
        auto-merge
        elements = { 185.220.101.0/24 timeout 1h, 51.15.0.0/16 timeout 1d }
    }

    set allowlist {
        type ipv4_addr
        elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 }
    }

    # Dynamic sets for threat intelligence feeds
    set emerging_threats {
        type ipv4_addr
        flags timeout
        auto-merge
    }

    chain input {
        type filter hook input priority 0; policy drop;

        # Allow established connections (stateful)
        ct state established,related accept

        # Allow loopback
        iif lo accept

        # Block known bad IPs
        ip saddr @blocklist drop
        ip saddr @emerging_threats drop

        # Allow management access (jump to mgmt chain)
        tcp dport { 22, 443 } ip saddr @allowlist accept

        # Log and drop everything else
        log prefix "NGFW-DROP: " accept
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        ct state established,related accept
        ip saddr @blocklist drop
        ip saddr @emerging_threats drop

        # Application-level forwarding rules
        tcp dport 80 jump web_filter
        tcp dport 443 jump tls_inspect
    }

    chain web_filter {
        # HTTP application inspection
        tcp dport 80 log prefix "HTTP: "
        # Add Suricata integration point
        queue num 1
    }

    chain tls_inspect {
        # Forward to SSL inspection proxy
        tcp dport 443 log prefix "TLS: "
        queue num 1
    }
}

Adding IPs to the Blocklist Dynamically

# Add a timeout-entry to the blocklist (auto-expires after 1 hour)
nft add element inet ngfw blocklist { 203.0.113.5 timeout 1h }

# Load threat intelligence feed (run via cron)
curl -s https://rules.emergingthreats.net/blockips/compromised-ips.txt | \
    while read ip; do
        nft add element inet ngfw emerging_threats { $ip timeout 6h }
    done

Suricata IDS/IPS Integration

Suricata operates in IPS mode when it receives packets via nftables queue:

# Install Suricata
sudo apt install suricata

# /etc/suricata/suricata.yaml — IPS mode configuration
af-packet:
  - interface: eth0
    cluster-id: 1
    cluster-type: cluster_flow
    defrag: yes
    # IPS mode (inline)
    copy-mode: ips
    copy-iface: eth1

Rule Management

# Update Emerging Threats rules (free community ruleset)
sudo suricata-update
sudo systemctl restart suricata

# Custom local rules
cat >> /etc/suricata/rules/local.rules << 'RULES'
# Alert on SQL injection attempts
alert http any any -> $HOME_NET any (
    msg:"SQL Injection Attempt";
    content:"SELECT|20|*|20|FROM|20|";
    nocase; sid:1000001; rev:1;
)

# Block known malware C2 patterns
drop tls $EXTERNAL_NET any -> $HOME_NET any (
    msg:"Malware C2 - Known Bad Certificate";
    tls.cert_subject; content:"evil.example.com";
    sid:1000002; rev:1;
)

# Alert on data exfiltration via DNS
alert dns $HOME_NET any -> $EXTERNAL_NET any (
    msg:"Possible DNS Tunneling - Long Subdomain";
    dns.query; length:>50; sid:1000003; rev:1;
)
RULES

# Load custom rules
sudo suricata-update --local /etc/suricata/rules/local.rules
sudo systemctl restart suricata

Check Suricata Alerts

# View real-time alerts
tail -f /var/log/suricata/fast.log
# Example output:
# 05/18/2026-14:23:01.234567 [**] [1:1000001:1] SQL Injection Attempt [**]
#   [Classification: Attempted SQL Injection] [Priority: 1]
#   {TCP} 10.0.1.100:54321 -> 10.0.2.10:80

# Stats dashboard
suricatasc -c dump-counters

OPNsense NGFW Configuration

OPNsense provides a web UI for managing nftables + Suricata + traffic shaping:

# Install OPNsense
# Download from https://opnsense.org/download/ and write to hardware/VM

# Initial CLI setup:
# - Set LAN IP: 192.168.1.1/24
# - Set WAN: DHCP (or static)
# - Enable SSH for remote management

VLAN Segmentation

Configure VLANs in the OPNsense web UI (Interfaces → Other Types → VLAN):

VLAN 10 — Management  (10.0.10.0/24) — IT admins only
VLAN 20 — Corporate   (10.0.20.0/24) — Employee workstations
VLAN 30 — Guest       (10.0.30.0/24) — Internet-only access
VLAN 40 — IoT         (10.0.40.0/24) — Devices, no internet access
VLAN 50 — DMZ         (10.0.50.0/24) — Public-facing servers

Firewall Rules with Application Detection

# OPNsense firewall rule pseudocode (configured via Web UI or config.xml):
#
# Rule 1: Allow Management to any (IT team)
#   Source: VLAN 10
#   Destination: any
#   Application: any
#   Log: enabled
#
# Rule 2: Corporate to internet (web only)
#   Source: VLAN 20
#   Destination: any
#   Application: HTTP, HTTPS, DNS
#   Suricata: enabled
#   SSL Inspection: enabled
#
# Rule 3: Guest to internet (isolated)
#   Source: VLAN 30
#   Destination: any
#   Application: HTTP, HTTPS
#   Block: all other ports
#
# Rule 4: IoT to internal only
#   Source: VLAN 40
#   Destination: 10.0.0.0/8 (internal)
#   Application: MQTT, HTTPS (API calls only)
#   Internet: blocked
#
# Rule 5: DMZ to internal (specific ports only)
#   Source: VLAN 50
#   Destination: 10.0.20.50 (database server)
#   Port: 5432 (PostgreSQL)
#   Application: PostgreSQL protocol

Traffic Shaping (QoS)

# Limit guest WiFi bandwidth
# Firewall → Shaper → By Interface
# Guest interface: max 50 Mbps down, 10 Mbps up, burst 10s

# Prioritize VoIP traffic
# Firewall → Shaper → By Protocol
# SIP/RTP: priority 7 (highest), guaranteed 1 Mbps per call

Reference NGFW Policy Architecture

Layer 1: Network segmentation (VLANs)
Layer 2: Stateful firewall (nftables stateful inspection)
Layer 3: Application identification (nftables port + DPI)
Layer 4: Intrusion prevention (Suricata IPS)
Layer 5: SSL/TLS inspection (forward proxy)
Layer 6: Threat intelligence (dynamic blocklist feeds)
Layer 7: User identity (VPN auth, 802.1x)
Layer 8: Traffic shaping (QoS per VLAN/application)

Resources

Comments

Share this article

Scan to read on mobile

👍 Was this article helpful?