Back to blog

Cisco / IOS  ·  Tutorial  ·  July 2026

Cisco IOS
Zone-Based Firewall

ACLs judge every packet alone. A real firewall remembers conversations — let a reply back in because you allowed the request out. The Zone-Based Firewall turns an IOS router into exactly that: group interfaces into zones, and write stateful policy for traffic between them.

Cisco IOS ZBF Firewall Stateful Inspection Security Zone-Pair

LAN trusted DMZ public server WAN untrusted LAN→WAN: inspect (stateful, allowed) WAN→LAN: drop (default — replies still return via state) Traffic between two zones is denied until a zone-pair policy permits it. Inspected sessions auto-allow their return traffic — no mirror rule needed.

The ACL post ended honestly: classic ACLs are stateless. Each packet is judged in isolation, so to let web replies back in you either open a wide inbound hole or lean on the crude established keyword — TCP-only, and blind to the actual conversation. A real firewall is stateful: it remembers that an inside host opened a connection outward, and automatically permits that connection's return traffic while blocking everything unsolicited. No mirror rules, every protocol.

Cisco's Zone-Based Firewall (ZBF) brings stateful inspection to any IOS/IOS-XE router — no separate appliance. The model is clean once it clicks: put interfaces into security zones (LAN, WAN, DMZ), and traffic between any two zones is denied by default until you attach a zone-pair policy that says otherwise. The policy is built from the same modular CLI (class-maps and policy-maps) that QoS uses. This post builds a working three-zone edge — internet access out, nothing unsolicited in, a published DMZ server — and the model scales straight to guest isolation.

Prerequisites

01

The Model — Zones, Pairs, and Default-Deny

Four moving parts, always in the same order: define zones, match traffic (class-map), decide what to do with it (policy-map), and bind the policy to a direction between two zones (zone-pair).

PieceRole
zone securityA named group of interfaces (LAN, WAN, DMZ).
class-map type inspectMatches which traffic — usually via an ACL. "What are we talking about?"
policy-map type inspectSays what to do with each class: inspect / pass / drop. "What do we do with it?"
zone-pair securityBinds a policy to traffic FROM one zone TO another. Direction matters.

Two rules make the whole system predictable. Traffic within the same zone flows freely (two LAN interfaces talk with no policy). Traffic between different zones is dropped unless a zone-pair explicitly allows it — and a zone-pair is unidirectional, so LAN→WAN and WAN→LAN are separate policies. An interface with no zone can't talk to any interface that has one, which is a classic "why did everything stop" surprise.

02

Step 1 — Define the Zones

Name the trust domains, then assign each interface to exactly one. This is the moment the firewall starts dropping inter-zone traffic, so plan before you assign.

IOS — create zones and place interfaces
R1(config)# zone security LAN
R1(config)# zone security WAN
R1(config)# zone security DMZ

R1(config)# interface GigabitEthernet0/1
R1(config-if)# zone-member security LAN
R1(config-if)# exit
R1(config)# interface GigabitEthernet0/0
R1(config-if)# zone-member security WAN
R1(config-if)# exit
R1(config)# interface GigabitEthernet0/2
R1(config-if)# zone-member security DMZ
R1(config-if)# end
⚠ Gotcha — Assigning a Zone to Your Management Interface Locks You Out

The instant an interface joins a zone, all traffic to/from other zones — including your own SSH session if it crosses a zone boundary — is dropped until a zone-pair permits it. Assign the WAN or management interface to a zone with no matching self-zone policy and you'll lose the box. Two defences: configure over the console for the initial cutover, and remember the special self zone (next gotcha) governs traffic to the router itself. Build the zone-pairs first, assign interfaces last, or do it on the console.

03

Step 2 — Match & Police: Class-Map + Policy-Map

Define what traffic you care about (an ACL wrapped in a class-map), then a policy that inspects it. "Inspect" is the magic word — it means stateful.

IOS — allow the LAN out to the internet, statefully
# What traffic? An ACL, referenced by a class-map (the ACL habit pays off)
R1(config)# ip access-list extended LAN-OUT
R1(config-ext-nacl)# permit ip 192.168.1.0 0.0.0.255 any
R1(config-ext-nacl)# exit

R1(config)# class-map type inspect match-any LAN-TRAFFIC
R1(config-cmap)# match access-group name LAN-OUT
R1(config-cmap)# exit

# What do we do? Inspect it — this is the STATEFUL action
R1(config)# policy-map type inspect LAN-TO-WAN-POLICY
R1(config-pmap)# class type inspect LAN-TRAFFIC
R1(config-pmap-c)# inspect
R1(config-pmap-c)# exit
R1(config-pmap)# class class-default
R1(config-pmap-c)# drop log       # everything else out this pair: drop + log
R1(config-pmap-c)# end
💡 inspect vs pass — Know the Difference

A policy class has three possible actions. inspect is stateful: it permits the traffic and builds a session so the return traffic is auto-allowed — this is what you want for TCP/UDP/ICMP flows initiated by a trusted zone. pass permits statelessly in one direction only (no return session) — used for stateless protocols like some IPsec/ESP where the router shouldn't track state. drop denies. The class class-default at the bottom of every policy is your explicit default-deny — always set it to drop (add log to see what you're refusing), the ZBF equivalent of an ACL's implicit deny made visible.

04

Step 3 — Bind It: the Zone-Pair

Nothing happens until a zone-pair connects the policy to a direction. This is the line that actually turns traffic on between two zones.

IOS — apply the policy from LAN to WAN
R1(config)# zone-pair security LAN-TO-WAN source LAN destination WAN
R1(config-sec-zone-pair)# service-policy type inspect LAN-TO-WAN-POLICY
R1(config-sec-zone-pair)# end

# That's it. LAN hosts now reach the internet, and because the sessions
# are INSPECTED, all their return traffic is auto-permitted. There is NO
# WAN-TO-LAN zone-pair, so unsolicited inbound is dropped by default —
# exactly the stateful behaviour ACLs made you fake.

R1# show policy-map type inspect zone-pair sessions
  Zone-pair: LAN-TO-WAN
    Number of established sessions = 42
    Established Sessions
     Session 192.168.1.20:51344=>93.184.216.34:443 tcp SIS_OPEN

05

Publishing a DMZ Server

The one case where you deliberately allow unsolicited inbound: a public web server in the DMZ. A WAN→DMZ zone-pair, scoped to exactly one port, paired with the static NAT from the NAT post.

IOS — allow only HTTPS from WAN into the DMZ server
# Match ONLY tcp/443 to the DMZ server
R1(config)# ip access-list extended WEB-IN
R1(config-ext-nacl)# permit tcp any host 192.168.50.10 eq 443
R1(config-ext-nacl)# exit
R1(config)# class-map type inspect match-any WEB
R1(config-cmap)# match access-group name WEB-IN
R1(config-cmap)# exit
R1(config)# policy-map type inspect WAN-TO-DMZ-POLICY
R1(config-pmap)# class type inspect WEB
R1(config-pmap-c)# inspect
R1(config-pmap)# class class-default
R1(config-pmap-c)# drop log
R1(config-pmap-c)# exit
R1(config)# zone-pair security WAN-TO-DMZ source WAN destination DMZ
R1(config-sec-zone-pair)# service-policy type inspect WAN-TO-DMZ-POLICY
R1(config-sec-zone-pair)# end
# Pair with: ip nat inside source static tcp 192.168.50.10 443 interface g0/0 443
⚠ Gotcha — NAT and ZBF Order: Match the Real (Inside) Address

On inbound WAN→DMZ traffic, NAT untranslates the destination before the firewall inspects it, so your ZBF ACL must match the server's private address (192.168.50.10), not its public one. Get this backwards and the class never matches, the traffic hits class-default, and it's silently dropped — a very common "port-forward + firewall = nothing works" cause. When in doubt, show policy-map type inspect zone-pair shows per-class counters: if WEB shows zero and class-default is climbing, your match address is wrong.

06

The Self Zone — Protecting the Router

Traffic TO the router itself (SSH, SNMP, routing protocols, your IP SLA pings) lives in a special built-in zone called "self". By default self-traffic is allowed — tighten it deliberately.

IOS — allow SSH to the router only from the LAN
# Match management protocols destined to the router
R1(config)# ip access-list extended TO-ROUTER
R1(config-ext-nacl)# permit tcp 192.168.1.0 0.0.0.255 any eq 22
R1(config-ext-nacl)# permit icmp any any
R1(config-ext-nacl)# exit
R1(config)# class-map type inspect match-any MGMT
R1(config-cmap)# match access-group name TO-ROUTER
R1(config)# policy-map type inspect TO-SELF
R1(config-pmap)# class type inspect MGMT
R1(config-pmap-c)# inspect
R1(config-pmap)# class class-default
R1(config-pmap-c)# drop
R1(config-pmap-c)# exit
R1(config)# zone-pair security WAN-TO-SELF source WAN destination self
R1(config-sec-zone-pair)# service-policy type inspect TO-SELF
R1(config-sec-zone-pair)# end
# This complements the VTY access-class from the ACL post — belt and braces.

07

Verify & Troubleshoot Safe to Run

ZBF fails in predictable ways — an unassigned interface, a missing zone-pair, or a class that never matches. These commands localize all three.

CommandShows
show zone securityEvery zone and its member interfaces — spot the unassigned one.
show zone-pair securityAll zone-pairs and the policy attached — spot the missing direction.
show policy-map type inspect zone-pairPer-class packet counters — the "which class is eating my traffic" view.
show policy-map type inspect zone-pair sessionsThe live stateful session table.
show class-map type inspectWhat each class-map matches.
💡 ACL or Zone-Based Firewall — When to Use Which

Not everything needs ZBF. For simple L3 segmentation and management-plane control — guest can't reach the LAN, only mgmt subnet may SSH — ACLs are lighter and perfectly adequate, and that's most SMB switch/router work. Reach for ZBF when you need true statefulness at an internet edge: return-traffic handling for every protocol, a DMZ, protocol inspection, or per-zone policy that would be a nightmare of mirrored ACLs. On sites that need a serious stateful edge, a dedicated firewall (or IOS-XE's newer policies) may be the better call — ZBF is the "real firewall without buying an appliance" middle ground, and it's plenty for most Cretan SMBs.

Takeaways

  1. Stateful beats stateless. ACLs judge packets alone; ZBF remembers conversations, so inspected outbound sessions auto-permit their return traffic — for every protocol, not just TCP established.
  2. Four pieces, always in order: zones (group interfaces) → class-map (match traffic, usually an ACL) → policy-map (inspect/pass/drop) → zone-pair (bind to a direction).
  3. Different zones are deny-by-default; same zone is open. A zone-pair is one-directional — LAN→WAN and WAN→LAN are separate — and an unzoned interface can't talk to any zoned one.
  4. inspect = stateful, pass = stateless one-way, drop = deny. End every policy with class class-default drop log as your visible default-deny.
  5. Publish servers with a scoped WAN→DMZ pair, matching the server's private address (NAT untranslates before inspection) and exactly one port — paired with a static NAT.
  6. The self zone protects the router. Lock management traffic to the box with a to-self policy; it complements the VTY access-class.
  7. Assign zones last (or from the console). The moment an interface joins a zone, inter-zone traffic — possibly your own session — drops until a pair permits it.

Need a real firewall at the edge without buying an appliance?

NOCTIS builds stateful Zone-Based Firewall policies on Cisco routers — internet edges, DMZs, guest isolation — for hotels and SMBs in Crete. Locked down by default, documented zone by zone, and pressure-tested from the outside.

Book a Discovery Call →