OPNsenseXIV · VLANs and SegmentationInter-VLAN routing and east-west filtering
East-west firewalling — VLAN-to-VLAN rules and least privilege
What you'll learn
- Write VLAN-to-VLAN firewall rules that enforce least privilege
- Enumerate the service dependencies between VLANs
- Apply the source-group, destination-group, and port-group patterns to inter-VLAN rules
- Audit an inter-VLAN rule set for over-permissiveness
Prerequisites
Verified against OPNsense 25.x · FreeBSD 14.x · PF (FreeBSD packet filter) FreeBSD 14.x · Unbound 1.20+ · Kea DHCP OPNsense 25.x plugin · WireGuard in-kernel + OPNsense plugin · strongSwan (IPsec plugin) OPNsense 25.x plugin · OpenVPN 2.6.x · Suricata 7.x · 2026-08-14
East-west traffic is the traffic that crosses between VLANs inside the firewall. It is the dominant traffic class in most production estates — far more than north-south traffic (LAN to Internet, Internet to LAN via port forwarding). East-west is also the traffic class where segmentation fails when it fails: a flat VLAN where any host can talk to any other host is not segmented, regardless of how many VLANs the operator has created.
This lesson covers how to write VLAN-to-VLAN rules that enforce least privilege, how to enumerate the service dependencies that justify each rule, and how to keep the rule set auditable over time.
Why east-west rules are different from north-south rules
North-south rules (LAN ↔ Internet, Internet ↔ DMZ) are coarse: permit or block categories of traffic. The Internet does not need to reach internal VLANs; the LAN needs to reach most of the Internet; the DMZ needs to be reachable from the Internet but not from the LAN.
East-west rules are fine-grained: which service on VLAN 20 needs to be reachable from which host on VLAN 10. The granularity is the same granularity the application has — and applications have a long tail of dependencies (databases, caches, message queues, identity providers, monitoring agents).
The discipline of east-west firewalling is to enumerate those dependencies and write a rule for each one. The operator who writes “any to any” between VLANs has not done the work.
The service-dependency enumeration
Before writing an east-west rule, the operator must answer: what service on the destination VLAN does the source VLAN actually need?
The answer comes from the application’s network requirements:
- A user VLAN needs DNS (UDP 53 to the DNS server) and probably NTP (UDP 123 to the NTP server).
- A user VLAN typically does not need direct database access. The application uses the database via a server in a server VLAN.
- A server VLAN that runs a web application needs database access (TCP 3306 for MySQL, TCP 5432 for PostgreSQL) to the database server VLAN.
- A server VLAN that runs an internal API needs the API to be reachable from the user VLAN (TCP 443 to the API server).
- A management VLAN needs to reach the firewall IP for GUI/SSH, but no other VLAN needs to reach the management VLAN.
The list is per-application. The operator who writes the list once and shares it with the application team has the foundation for an auditable rule set.
A worked example: the user VLAN (10) needs to reach the web application (HTTPS) on the servers VLAN (20), and the web application needs to reach the database (TCP 5432) on the database VLAN (30).
| Source VLAN | Source | Destination VLAN | Destination | Port | Reason |
|---|---|---|---|---|---|
| Users (10) | 192.0.2.0/24 | Servers (20) | 203.0.113.50 | 443 | Web application access |
| Users (10) | 192.0.2.0/24 | DNS (20) | 203.0.113.10 | 53 | DNS resolution |
| Servers (20) | 203.0.113.50 | Database (30) | 198.51.100.20 | 5432 | Web app to PostgreSQL |
| Servers (20) | 203.0.113.50 | DNS (20) | 203.0.113.10 | 53 | Web app DNS |
Each rule has a reason. The audit answer to “why does this rule exist?” is in the table.
The rule pattern
For each service dependency, write one rule on the source VLAN. The rule has:
- Action: pass.
- Source: the source subnet or specific host.
- Destination: the specific destination IP (or alias).
- Destination port: the specific port (or alias).
- Description: the reason. Owner, ticket, review date.
The discipline: avoid “any” in any field. A rule with destination “any” is a permit-all to the destination subnet. A rule with port “any” is a permit-all on every port. The narrower the rule, the smaller the blast radius if the rule is wrong.
$ pfctl -s rules | grep -E 'igb1_vlan10|igb1_vlan20' | head -10@10 pass in quick on igb1_vlan10 inet proto tcp from 192.0.2.0/24 to 203.0.113.50 port = https
[ Permit users to web application on servers VLAN. Owner: NetOps. Ticket NET-2401. Reviewed 2026-08-01. ]
@11 pass in quick on igb1_vlan10 inet proto tcp from 192.0.2.0/24 to 203.0.113.10 port = 53
[ Permit users to DNS server. Owner: NetOps. Ticket NET-2402. Reviewed 2026-08-01. ]
@50 pass in quick on igb1_vlan20 inet proto tcp from 203.0.113.50 to 198.51.100.20 port = 5432
[ Permit web app to PostgreSQL database. Owner: AppTeam. Ticket APP-1102. Reviewed 2026-08-01. ]
@99 block drop in quick on igb1_vlan10 inet from any to any
[ Default deny on VLAN 10. Catches all traffic not explicitly permitted above. ]Illustrative output
The four rules shown implement the worked example. Each rule has a description; the default deny is the safety net.
Aliases for source groups, destination groups, port groups
Aliases are how OPNsense rulesets stay readable. The operator who writes “192.0.2.0/24 to 203.0.113.50 port = https” is fine; the operator who writes the same thing ten times for ten different sources creates maintenance debt.
Common alias patterns for east-west rules:
vlan10_hosts: an alias of specific hosts on VLAN 10 that need a particular service.db_servers: an alias of database server IPs across VLANs.web_app_ports: an alias of TCP ports 80, 443, 8443 used by a web application.monitoring_agents: an alias of monitoring agent IPs that need to reach services across VLANs.
Aliases are referenced in rules by name. When a host is added to an alias, every rule that uses the alias picks up the new host without a rule change.
Auditing an east-west ruleset
Three audit questions to ask of an existing inter-VLAN ruleset.
Audit 1: Is there a rule with “any” anywhere?
A rule with source any, destination any, or port any is almost certainly over-permissive. The exception is the default-deny rule at the bottom of the rule set, which is intentionally broad.
pfctl -s rules | grep -E 'from any to any|to any port = any|from any to .* port = any'
Any rule that matches is a candidate for narrowing.
Audit 2: Is the rule description up to date?
A rule without a description, or with a description that does not match the rule’s actual behaviour, is an audit finding. The operator reviewing the rule cannot tell what it is for.
The fix: every rule has a description; every description references an owner, a ticket, and a review date.
Audit 3: Is there a rule that is no longer needed?
Rules outlive the services that justified them. A server is decommissioned; the rule that permitted traffic to the server stays. A service is moved to a new VLAN; the rule that permitted traffic to the old VLAN IP stays.
The fix: quarterly review of every inter-VLAN rule. Remove rules that no longer correspond to a live service dependency.
$ pfctl -s rules | grep -E 'any' | head -512 pass in quick on igb1_vlan10 inet proto tcp from 192.0.2.0/24 to any port = https
47 pass in quick on igb1_vlan20 inet proto tcp from any to 198.51.100.20 port = 5432
89 pass in quick on igb1_vlan10 inet from any to any
104 block drop in quick on igb1_vlan10 inet from any to anyIllustrative output
The audit output distinguishes between the rules that intentionally use “any” (the default deny) and the rules that use “any” but should not (the over-permissive pass rules).
The “permit a group” anti-pattern
A common anti-pattern: the operator creates an alias called trusted_vlans that contains every VLAN the operator considers safe, and writes a single rule “permit trusted_vlans to any”. The rule is wide; the alias name is aspirational.
The blast radius of a misconfigured trusted-vlans alias is enormous. A host compromised on any “trusted” VLAN can reach any other VLAN. The segmentation is cosmetic.
The discipline: do not alias VLANs together. Each VLAN gets its own rule set, even if the rule sets look similar. The cost of duplicating rule sets is small; the cost of an over-permissive trusted-vlans alias is large.
A worked inter-VLAN ruleset
The minimal viable inter-VLAN ruleset for a three-VLAN estate (users, servers, database):
VLAN 10 (Users) rules
| Order | Action | Source | Destination | Port | Description |
|---|---|---|---|---|---|
| 1 | pass | vlan10_subnet | dns_server | 53 | Permit DNS to internal resolver |
| 2 | pass | vlan10_subnet | web_app | 443 | Permit web application |
| 3 | block | any | any | any | Default deny |
VLAN 20 (Servers) rules
| Order | Action | Source | Destination | Port | Description |
|---|---|---|---|---|---|
| 1 | pass | dns_server | any | 53 | Permit DNS server outbound (recursive resolution) |
| 2 | pass | web_app | postgres_db | 5432 | Permit web app to PostgreSQL |
| 3 | pass | monitoring_agent | any | any | Permit monitoring agent to all servers |
| 4 | block | any | any | any | Default deny |
VLAN 30 (Database) rules
| Order | Action | Source | Destination | Port | Description |
|---|---|---|---|---|---|
| 1 | pass | web_app | postgres_db | 5432 | Permit web app database queries |
| 2 | pass | monitoring_agent | postgres_db | 5432 | Permit monitoring queries to database |
| 3 | block | any | any | any | Default deny |
The three rule sets implement the dependencies between the VLANs. Each rule has a description. The default deny at the bottom of each rule set is the safety net.
Summary
- East-west firewalling is fine-grained. The granularity is the application’s network requirements.
- Every inter-VLAN rule has a reason. Enumerate the service dependencies before writing the rule.
- The default deny is the foundation. Every VLAN is born isolated; rules enable what is needed.
- Audit the ruleset for “any” usage, missing descriptions, and unused rules.
- Avoid the “permit trusted VLANs” anti-pattern. Each VLAN gets its own rule set.
Knowledge check · 4 questions
Q1. You inherit an inter-VLAN ruleset with a rule "permit any to any" between VLAN 10 and VLAN 20. The rule description says "allow all east-west traffic". What is the most appropriate first action?
Q2. OPNsense evaluates inter-VLAN rules on both the source VLAN interface and the destination VLAN interface for the same packet.
Q3. Which of the following are valid audit questions for an inter-VLAN ruleset? Select all that apply.
Q4. You add a new VLAN 40 (storage). The application team tells you the storage array at 198.51.100.100 needs to be reachable from the database server at 198.51.100.20 on TCP 2049 (NFS). You write a single rule on VLAN 30 permitting 198.51.100.20 to 198.51.100.100 port 2049. What is the most appropriate additional step?
Passing score: 75%. Answers are checked in this browser.