Linux DMZ with nftables: Build a Secure VLAN, NAT and Stateful Firewall Architecture
A DMZ is one of the most important network-security patterns to understand when turning a Linux system into a real router or firewall.
The basic idea is simple:
Internet-facing services should not live inside the same trusted network as internal users, databases, management systems, and administrative interfaces.
Instead, we create a separate security zone:
INTERNET
|
|
[ WAN ]
|
+---------------+
| Linux Router |
| nftables |
+---------------+
/ | \
/ | \
LAN DMZ MANAGEMENT
| | |
Users Web/DNS Admin Hosts
But building a useful DMZ requires much more than creating another subnet.
The Linux router must correctly handle:
- routing;
- VLAN separation;
- IP forwarding;
- stateful firewalling;
- DNAT;
- SNAT or masquerading;
- restricted DMZ-to-LAN communication;
- management access;
- logging;
- connection tracking;
- return paths.
This guide builds that architecture step by step.
It extends the CorgiNetwork Linux nftables Inter-VLAN Firewall architecture and applies it to a realistic perimeter-security scenario.
What Is a DMZ?
A DMZ—demilitarized zone—is a network segment positioned between less-trusted networks and protected internal infrastructure.
For example:
Internet
|
v
+----------+
| Firewall |
+----------+
|
+-------- DMZ
|
+-------- Internal LAN
The DMZ may contain services that need to receive connections originating outside the trusted LAN.
Examples include:
Web server
Reverse proxy
Public DNS server
Mail gateway
VPN gateway
Application gateway
The important security principle is that compromising a DMZ server should not automatically provide unrestricted access to the internal network.
Why Not Put the Web Server on the LAN?
Consider:
INTERNET
|
v
WEB SERVER
|
v
INTERNAL LAN
If the web server is compromised and has broad access to internal infrastructure, an attacker may gain a path toward:
Database servers
File servers
Backups
Management interfaces
Directory services
Monitoring systems
Administrator workstations
A DMZ changes the trust relationship:
INTERNET
|
v
FIREWALL
|
v
DMZ
|
X
|
INTERNAL LAN
Communication between zones must be explicitly permitted.
This follows the same default-deny principle used in our nftables Inter-VLAN Firewall guide.
Our Linux DMZ Architecture
We will use four security zones:
INTERNET
|
|
eth0/WAN
|
+----------------+
| |
| Linux Firewall |
| nftables |
| |
+----------------+
/ | \
/ | \
/ | \
VLAN 10 VLAN 20 VLAN 30
LAN DMZ MANAGEMENT
Addressing:
LAN
192.168.10.0/24
DMZ
192.168.20.0/24
MANAGEMENT
192.168.30.0/24
Example DMZ web server:
192.168.20.10
Administrator workstation:
192.168.30.10
Linux router interfaces:
LAN gateway:
192.168.10.1
DMZ gateway:
192.168.20.1
Management gateway:
192.168.30.1
Security Policy
Before writing nftables commands, define the policy.
A reasonable starting point is:
| Source | Destination | Service | Action |
|---|---|---|---|
| Internet | DMZ Web | HTTP/HTTPS | Allow |
| Internet | LAN | Any | Deny |
| Internet | Management | Any | Deny |
| LAN | Internet | Required outbound | Allow |
| LAN | DMZ Web | HTTP/HTTPS | Allow |
| DMZ | LAN | Any | Deny |
| DMZ | Management | Any | Deny |
| Management | DMZ | SSH | Allow |
| Management | LAN Servers | Admin services | Allow |
This table is more important than the commands.
The firewall should implement an intentional security policy—not become the policy itself.
VLANs Create the Layer 2 Boundaries
If the LAN, DMZ, and management networks share physical infrastructure, VLANs can provide Layer 2 separation.
Conceptually:
VLAN 10 = LAN
VLAN 20 = DMZ
VLAN 30 = Management
If you need to build this layer from scratch, the CorgiNetwork Linux Bridge VLAN Filtering guide explains tagged traffic, untagged traffic, PVIDs, trunks, and access ports.
Our architecture then becomes:
Physical NIC
|
Trunk
|
Linux Bridge
|
VLAN Filtering
|
+----+----+----+
| | |
10 20 30
LAN DMZ MGMT
Routing Connects the Security Zones
VLANs isolate the networks.
Routing allows controlled communication between them.
That distinction is fundamental:
VLAN
=
Layer 2 segmentation
Routing
=
Layer 3 reachability
Firewall
=
Security policy
The Linux Inter-VLAN Routing with Network Namespaces guide covers the routing layer in detail.
For our DMZ firewall, Linux becomes the Layer 3 gateway.
Enable IPv4 Forwarding
Linux must be allowed to forward packets between interfaces.
Check:
sysctl net.ipv4.ip_forward
If the output is:
net.ipv4.ip_forward = 0
routing is disabled.
Enable it temporarily:
sudo sysctl -w net.ipv4.ip_forward=1
The Linux kernel documentation defines ip_forward as the boolean control for forwarding packets between interfaces and notes that it is disabled by default. See the official Linux IP sysctl documentation.
For persistence, configure the appropriate sysctl file for your Linux distribution rather than relying only on the temporary command.
Verify again:
sysctl net.ipv4.ip_forward
Expected:
net.ipv4.ip_forward = 1
Confirm Routing Before Building the Firewall
Check:
ip route
The Linux router should know how to reach:
192.168.10.0/24
192.168.20.0/24
192.168.30.0/24
as directly connected networks.
Also confirm the default route:
ip route show default
For example:
default via ISP_GATEWAY dev eth0
Test a specific routing decision:
ip route get 192.168.20.10
and:
ip route get 1.1.1.1
This distinction matters because nftables cannot repair an incorrect routing table.
If Linux selects the wrong egress NIC, use the CorgiNetwork Linux Traffic Uses the Wrong Interface troubleshooting guide before changing firewall rules.
Build the nftables Filter Table
Create:
sudo nft add table inet firewall
Now create the forwarding chain:
sudo nft 'add chain inet firewall forward { type filter hook forward priority 0; policy drop; }'
The important security decision is:
policy drop
Therefore:
Unmatched forwarded traffic
|
v
DROP
This is our default-deny foundation.
Netfilter provides both stateless and stateful filtering capabilities, and nftables reuses the existing Netfilter connection-tracking infrastructure. The official Netfilter nftables project provides the upstream project documentation.
Drop Invalid Connections
Add:
sudo nft add rule inet firewall forward \
ct state invalid drop
Then allow traffic belonging to permitted existing connections:
sudo nft add rule inet firewall forward \
ct state established,related accept
Our stateful foundation becomes:
INVALID
|
v
DROP
ESTABLISHED / RELATED
|
v
ACCEPT
NEW
|
v
Evaluate explicit policy
This prevents us from needing broad reverse-direction rules for every permitted connection.
Protect the Internal LAN
The most important rule is conceptual:
Internet
|
X
|
Internal LAN
Because our forwarding chain already has:
policy drop
we do not need to create an allow rule for unsolicited Internet-to-LAN connections.
No matching allow rule means the packet is dropped.
Likewise:
Internet
|
X
|
Management
remains blocked.
Allow LAN Users to Access the Internet
Suppose the LAN enters the router through:
lan0
and the WAN leaves through:
eth0
A simplified forwarding rule is:
sudo nft add rule inet firewall forward \
iifname "lan0" \
oifname "eth0" \
ip saddr 192.168.10.0/24 \
ct state new \
accept
Return traffic is already handled by:
ct state established,related accept
Conceptually:
LAN
|
| NEW
v
Internet
Internet
|
| ESTABLISHED
v
LAN
NAT Is a Different Layer
Do not confuse firewall filtering with NAT.
Filtering asks:
Should this packet pass?
Routing asks:
Where should this packet go?
NAT asks:
Should an address or port be translated?
Netfilter supports network and port address translation in addition to stateful packet filtering. The official Netfilter project documentation describes NAT and masquerading among its core capabilities.
Create an nftables NAT Table
For IPv4 NAT:
sudo nft add table ip nat
Create prerouting:
sudo nft 'add chain ip nat prerouting { type nat hook prerouting priority -100; }'
Create postrouting:
sudo nft 'add chain ip nat postrouting { type nat hook postrouting priority 100; }'
Now we can implement:
DNAT
SNAT
Masquerading
depending on the traffic direction.
Publish a DMZ Web Server with DNAT
Suppose the firewall’s public address is:
203.0.113.10
and the DMZ web server is:
192.168.20.10
We want:
Internet
|
203.0.113.10:443
|
v
192.168.20.10:443
Create DNAT:
sudo nft add rule ip nat prerouting \
iifname "eth0" \
tcp dport 443 \
dnat to 192.168.20.10:443
For HTTP:
sudo nft add rule ip nat prerouting \
iifname "eth0" \
tcp dport 80 \
dnat to 192.168.20.10:80
But DNAT alone does not mean the forwarding firewall should allow the connection.
We still need explicit filter policy.
Allow the Forwarded Web Traffic
Add:
sudo nft add rule inet firewall forward \
iifname "eth0" \
ip daddr 192.168.20.10 \
tcp dport { 80, 443 } \
ct state new \
counter \
accept
Now the packet path becomes:
Internet Client
|
| 203.0.113.10:443
v
PREROUTING
|
DNAT
|
v
192.168.20.10:443
|
FORWARD
|
Firewall Policy
|
v
DMZ Web Server
This separation is extremely important.
NAT determines translation. The firewall determines permission.
Do Not Forward SSH from the Internet by Default
A tempting configuration is:
Internet
|
TCP/22
|
v
DMZ Server
Avoid exposing administrative services unnecessarily.
Instead, use the management network:
Management VLAN
192.168.30.0/24
|
| SSH
v
DMZ
For example:
sudo nft add rule inet firewall forward \
ip saddr 192.168.30.10 \
ip daddr 192.168.20.10 \
tcp dport 22 \
ct state new \
counter \
accept
This provides:
ADMIN HOST
192.168.30.10
|
SSH
|
v
DMZ SERVER
192.168.20.10
while Internet-originated SSH remains blocked.
Prevent DMZ-to-LAN Lateral Movement
This is one of the central purposes of the architecture.
We want:
DMZ
|
X
|
LAN
Because the chain policy is already drop, arbitrary DMZ-to-LAN traffic receives no permission.
For visibility, you may optionally add a controlled logging rule before the default verdict:
sudo nft add rule inet firewall forward \
ip saddr 192.168.20.0/24 \
ip daddr 192.168.10.0/24 \
limit rate 5/second \
log prefix "DMZ_TO_LAN_BLOCKED: " \
counter \
drop
Rate limiting matters because uncontrolled logging can create excessive log volume during scans or attacks.
What If the DMZ Application Needs a Database?
Real applications often require controlled internal communication.
Suppose:
Web Application:
192.168.20.10
Database:
192.168.10.50
Database service:
PostgreSQL TCP/5432
Do not allow:
DMZ → LAN → ANY
Instead create:
sudo nft add rule inet firewall forward \
ip saddr 192.168.20.10 \
ip daddr 192.168.10.50 \
tcp dport 5432 \
ct state new \
counter \
accept
The security relationship becomes:
DMZ Web Server
192.168.20.10
|
| TCP/5432
v
Database
192.168.10.50
Everything else remains denied.
This is a far stronger design than simply declaring:
DMZ can access LAN
Use nftables Sets as the Environment Grows
Once the network contains several web servers or administration systems, individual rules become repetitive.
Instead of:
192.168.20.10
192.168.20.11
192.168.20.12
you can create:
@dmz_web_servers
Likewise:
@trusted_admins
and:
@web_ports
This is exactly the problem addressed in our previous nftables Sets and Maps architecture.
The design evolves from:
Rule
Rule
Rule
Rule
toward:
Security Groups
|
v
Sets / Maps
|
v
Policy
Masquerading for Outbound Traffic
Private networks normally cannot send RFC1918 source addresses directly across the public Internet and expect normal Internet routing.
If the WAN address is dynamic, masquerading is commonly used.
For LAN traffic:
sudo nft add rule ip nat postrouting \
oifname "eth0" \
ip saddr 192.168.10.0/24 \
masquerade
If DMZ servers require outbound Internet access:
sudo nft add rule ip nat postrouting \
oifname "eth0" \
ip saddr 192.168.20.0/24 \
masquerade
But ask whether DMZ servers genuinely require unrestricted outbound access.
A more secure design restricts outbound services according to operational requirements.
Restrict DMZ Outbound Traffic
A compromised public server should not automatically receive unlimited outbound connectivity.
You might permit only required services such as:
DNS
HTTP
HTTPS
NTP
depending on your infrastructure.
Conceptually:
DMZ
|
+--- DNS ------> Resolver
|
+--- HTTPS ----> Internet
|
X--- arbitrary outbound traffic
This makes command-and-control traffic and lateral movement harder, although firewall policy is only one layer of defense.
DNS Architecture
Rather than allowing DMZ hosts to send DNS queries anywhere, consider a designated resolver.
For example:
DNS Resolver:
192.168.20.53
Then:
DMZ servers
|
DNS
|
v
192.168.20.53
This gives administrators a clearer control point for DNS behavior.
The exact architecture depends on whether the resolver itself belongs in the DMZ, an infrastructure VLAN, or another protected zone.
Management Must Be Its Own Trust Zone
Do not assume:
LAN users = administrators
A stronger design separates:
USERS
from:
MANAGEMENT
For example:
VLAN 10 → Users
VLAN 20 → DMZ
VLAN 30 → Management
Then:
Users
|
X
|
SSH to DMZ
Management
|
| SSH
v
DMZ
This reduces the impact of compromising an ordinary workstation.
Stateful Return Traffic
Suppose the management workstation starts:
192.168.30.10
|
| SSH NEW
v
192.168.20.10
Our explicit SSH rule permits the initial connection.
The reply:
192.168.20.10
|
| ESTABLISHED
v
192.168.30.10
matches:
ct state established,related accept
Therefore we do not need:
DMZ → Management → Allow Everything
This is why connection tracking is fundamental to a stateful DMZ firewall.
Inspect the Firewall
Always inspect the active configuration:
sudo nft list ruleset
For handles:
sudo nft -a list ruleset
Inspect only the forwarding chain:
sudo nft list chain inet firewall forward
Inspect NAT:
sudo nft list table ip nat
Never assume the rules you intended to load are identical to the rules currently active.
Use Counters
Counters answer an important question:
Is traffic actually reaching this rule?
Example:
sudo nft add rule inet firewall forward \
ip saddr 192.168.30.10 \
ip daddr 192.168.20.10 \
tcp dport 22 \
counter \
accept
Generate SSH traffic.
Then:
sudo nft list chain inet firewall forward
If the counter increases, the packet is matching the rule.
If it stays at zero, investigate an earlier stage.
Packet Capture Is Essential
Use:
sudo tcpdump -ni eth0
for WAN traffic.
On the DMZ interface:
sudo tcpdump -ni dmz0
Or target a host:
sudo tcpdump -ni any host 192.168.20.10
For HTTPS:
sudo tcpdump -ni any \
host 192.168.20.10 and tcp port 443
Now you can observe:
WAN
↓
DNAT
↓
FORWARD
↓
DMZ
↓
RETURN
rather than guessing which layer failed.
Troubleshooting: Internet Cannot Reach the DMZ Web Server
Do not immediately rewrite the DNAT rule.
Check in order.
First:
ip addr
Is the WAN interface configured correctly?
Then:
sysctl net.ipv4.ip_forward
Expected:
1
Then:
sudo nft list table ip nat
Is the DNAT rule present?
Then:
sudo nft list chain inet firewall forward
Does the forwarding policy allow TCP/80 or TCP/443?
Then:
sudo tcpdump -ni eth0 tcp port 443
Does the connection reach the WAN interface?
Then:
sudo tcpdump -ni dmz0 host 192.168.20.10
Does the translated packet reach the DMZ?
This workflow identifies the failing layer.
Troubleshooting: Packet Reaches the Server but No Reply Returns
Suppose tcpdump shows:
Client → DMZ Server
but no reply.
Check the DMZ server:
ip route
Its default gateway should normally point toward the Linux firewall for this architecture.
For example:
default via 192.168.20.1
If the reply uses another router, you may create an asymmetric path.
This is especially important with stateful firewalls.
For deeper asymmetric-routing behavior, read Linux rp_filter and Asymmetric Routing.
Troubleshooting: DNAT Works but Forwarding Fails
Remember:
DNAT ≠ ACCEPT
DNAT changes the destination.
The packet still has to pass the forwarding policy.
Inspect:
sudo nft list chain inet firewall forward
Check:
source
destination after translation
protocol
destination port
connection state
and examine counters.
Troubleshooting: LAN Has No Internet
Check:
ip route
Then:
ip route get 1.1.1.1
Then:
sysctl net.ipv4.ip_forward
Then:
sudo nft list chain inet firewall forward
Then:
sudo nft list table ip nat
Confirm masquerading.
Finally:
sudo tcpdump -ni eth0
The failure could be:
Routing
Firewall
NAT
DNS
Upstream gateway
Do not assume all Internet-access failures are NAT failures.
Troubleshooting Wrong Interface Selection
A multi-homed DMZ firewall may have:
WAN1
WAN2
LAN
DMZ
Management
VPN
Linux route selection becomes increasingly important.
Use:
ip route get DESTINATION
For source-specific tests:
ip route get DESTINATION from SOURCE
If source-dependent routing is required, Linux Policy Routing with ip rule and Multiple Routing Tables explains how policy rules select alternative routing tables.
For persistent Ubuntu configurations with multiple default routes, use Netplan Multiple Default Routes and Policy Routing.
Watch for ARP Problems on Multi-Homed Firewalls
If several interfaces exist in related Layer 2 environments, unexpected ARP behavior can complicate diagnosis.
Symptoms may look like:
wrong interface
unexpected MAC response
unstable reachability
even when nftables appears correct.
CorgiNetwork’s Linux ARP Flux with Multiple NICs on the Same Subnet covers this specific class of problem.
NAT and Connection Tracking
NAT in Netfilter is closely tied to connection tracking.
Once a connection has been classified and translated, subsequent packets belonging to that flow use the associated NAT state.
This is one reason troubleshooting should consider:
NAT
+
conntrack
+
filtering
+
routing
as interacting systems rather than completely independent mechanisms.
The upstream Netfilter project documents both stateful packet filtering and NAT as core Netfilter capabilities.
Optional Performance Layer: Netfilter Flowtables
On high-throughput Linux routers, Netfilter also provides flowtable infrastructure that can create a fast path for selected forwarded flows.
The official Linux kernel flowtable documentation explains that, after the first packet traverses the classic forwarding path, selected subsequent packets can be offloaded to a flowtable fast path. It also notes that flowtable entries retain the NAT configuration associated with the connection.
This is an advanced optimization.
Do not begin there.
First build:
Correct routing
↓
Correct firewall
↓
Correct NAT
↓
Correct observability
↓
Performance optimization
A fast incorrect firewall is still an incorrect firewall.
Do Not Forget IPv6
A common mistake is building:
Excellent IPv4 firewall
while leaving:
IPv6
with a completely different security posture.
IPv4 NAT is also not a substitute for IPv6 firewall policy.
If IPv6 is enabled and routed, define an explicit IPv6 security model.
The Linux kernel exposes separate forwarding controls for IPv4 and IPv6; its IP sysctl documentation describes global IPv6 forwarding separately from net.ipv4.ip_forward.
DMZ Is Not a Complete Security Strategy
A DMZ does not make a vulnerable server safe.
You still need:
Operating-system updates
Application patching
Minimal exposed services
TLS configuration
Authentication
Least privilege
Logging
Monitoring
Backups
Endpoint security
Vulnerability management
Network segmentation limits reachability.
It does not replace host and application security.
Production Hardening Checklist
Before deploying this architecture, verify:
✓ LAN, DMZ and management are separate zones
✓ Default forwarding policy is DROP
✓ Invalid traffic is handled
✓ Established/related traffic is handled statefully
✓ Internet cannot directly reach LAN
✓ Internet cannot directly reach management
✓ Only required services are DNATed
✓ DMZ-to-LAN access is denied by default
✓ Required DMZ-to-LAN exceptions are narrowly scoped
✓ Administrative access originates from management
✓ NAT rules are documented
✓ Firewall counters are available
✓ Logging is rate-limited
✓ IPv6 has an explicit policy
✓ Firewall rules persist across reboot
✓ Configuration backups exist
✓ Out-of-band recovery is available where possible
A Better Mental Model
Think of the firewall as several separate systems working together:
PACKET
|
v
+---------------+
| Layer 2/VLAN |
+---------------+
|
v
+---------------+
| Routing |
+---------------+
|
v
+---------------+
| Netfilter |
| Firewall |
+---------------+
|
+----+----+
| |
v v
FILTER NAT
| |
+----+----+
|
v
CONNTRACK
|
v
DESTINATION
When troubleshooting, identify which subsystem is responsible.
Frequently Asked Questions
What is a Linux DMZ?
A Linux DMZ is a separate network security zone containing systems that require controlled connectivity to less-trusted networks, while a Linux router/firewall regulates communication between the DMZ, Internet, and internal networks.
Can nftables build a DMZ firewall?
Yes.
nftables can provide stateful filtering, NAT, address/port translation, logging, sets, maps, and other packet-processing capabilities through the Linux Netfilter framework.
Do I need NAT between LAN and DMZ?
Not necessarily.
If both networks are internally routed and have valid return routes, they can communicate through normal Layer 3 routing.
NAT is normally introduced when address translation is actually required.
What is DNAT used for?
DNAT changes the destination address and optionally the destination port.
A common DMZ use is translating:
Public IP:443
to:
Private DMZ Web Server:443
Filtering should still explicitly decide whether that forwarded connection is allowed.
What is masquerading?
Masquerading is a form of source NAT commonly used when outbound private-network traffic shares an external address, particularly when that external address may change.
Should the DMZ be able to access the LAN?
The safest starting position is generally to deny unsolicited DMZ-to-LAN access and then create narrowly scoped exceptions for documented application requirements.
For example:
Web Server
|
TCP/5432
|
Database
is much safer than:
DMZ
|
ANY
|
LAN
Should SSH be exposed from the Internet?
Avoid exposing administrative services unless there is a clear operational requirement and an appropriate security architecture.
A dedicated management network, VPN, bastion design, or another controlled administrative path is usually preferable to broadly exposing management interfaces.
Does DNAT automatically open the firewall?
No.
Translation and filtering are separate concepts.
A DNAT rule can change the packet destination while the forwarding firewall still drops the packet.
How do I troubleshoot a Linux DMZ?
Start with:
ip -br addr
ip route
ip route get DESTINATION
sysctl net.ipv4.ip_forward
nft list ruleset
conntrack -L
tcpdump -ni any
Then follow the packet from source to destination and back.
Conclusion
A secure Linux DMZ is not created by one nftables rule.
It is an architecture:
INTERNET
|
v
WAN
|
v
Linux Router
|
+------------+------------+
| | |
v v v
LAN DMZ MANAGEMENT
Each component has a specific responsibility:
VLANs
↓
Layer 2 separation
Routing
↓
Layer 3 reachability
nftables filtering
↓
Security policy
DNAT
↓
Publishing selected services
SNAT/Masquerading
↓
Outbound address translation
Conntrack
↓
Stateful connection handling
The core security principle is straightforward:
Expose the minimum required service and the minimum required path.
Internet users do not need access to your LAN.
A public web server does not need unrestricted access to every internal system.
Ordinary LAN users do not need administrative access to the DMZ.
And an administrator should not have to weaken the entire network simply to manage one server.
This article builds directly on the Linux nftables Inter-VLAN Firewall, where we created the default-deny and stateful firewall foundation.
The Layer 3 foundation is covered in Linux Inter-VLAN Routing with Network Namespaces, while Linux Bridge VLAN Filtering Explained covers the Layer 2 VLAN architecture underneath it.
For complex multi-homed routers, Linux Policy Routing with ip rule and Multiple Routing Tables extends the design to source- and policy-dependent route selection.
If Linux sends packets through an unexpected NIC, use Linux Traffic Uses the Wrong Interface before modifying the firewall.
For asymmetric paths, Linux rp_filter and Asymmetric Routing explains another important Linux networking behavior that can appear in multi-router environments.
For the underlying technologies, the official Netfilter project documents stateful packet filtering, NAT and masquerading, while the Linux kernel IP sysctl documentation documents IP forwarding behavior. For advanced high-throughput routers, the kernel’s Netfilter flowtable documentation explains software fast-path and hardware-offload capabilities.
The CorgiNetwork cluster now progresses naturally through:
Network Namespaces
↓
Linux Bridge
↓
VLAN Filtering
↓
Inter-VLAN Routing
↓
Stateful nftables Firewall
↓
nftables Sets & Maps
↓
Linux DMZ + NAT
↓
Production Linux Firewall Architecture