Diana J. Hakim · Cybersecurity & IT Developer · October 30, 2025
The Problem With “It Works”
Most small businesses run on a network that was never designed — it was accumulated. A consumer router here, a switch there, a NAS that someone set up five years ago and port-forwarded so they could reach it from a hotel. One Wi-Fi password, shared with staff and guests alike. The result is a flat /24 where the accountant’s laptop, a compromised IoT thermostat, and a visiting client’s phone are all equally trusted.
This works fine — until it doesn’t. One weak device becomes every device. A guest who captures the Wi-Fi key can probe the NAS. A misconfigured IoT camera can scan the accounting workstation. And when something goes wrong, there’s no log to reconstruct what happened.
Over the past several weeks, I completed a consulting-style engagement for a fictional 15-person professional services firm called Apex Consulting Group. The project demonstrates how to move from that flat, over-trusted pattern to a default-deny, VLAN-segmented architecture — and, critically, how to prove the segmentation actually works through a reproducible Docker lab with executable allow/deny tests.
This is the complete case study: current-state assessment, segmentation design, firewall architecture, secure remote access, endpoint isolation, monitoring, logging, and incident-response testing.
Part 1 — Current-State Assessment
The fictional Apex started where most small businesses start:
- One consumer router providing routing and Wi-Fi
- A single shared credential for staff and guests
- A NAS reachable through an internet port forward
- No network-layer boundary between any two devices
Visually, it looked like this:
flowchart TB
Internet((Internet)) --> Router[Consumer router + Wi-Fi<br/>default admin credentials<br/>NAS port forward]
Router --> LAN["192.168.1.0/24 — flat LAN"]
LAN --> Emp[Employee]
LAN --> NAS[NAS]
LAN --> Print[Printer]
LAN --> IoT[IoT]
LAN --> Guest[Guest]
LAN --> Admin[Admin UI]
Emp -.->|"broad peer reachability"| NAS
IoT -.->|"broad peer reachability"| NAS
Guest -.->|"broad peer reachability"| Admin
classDef edge fill:#3b1e1e,stroke:#fb7185,color:#fff
classDef lan fill:#3b2f1e,stroke:#fbbf24,color:#fff
classDef host fill:#1e2f1e,stroke:#4ade80,color:#fff
class Internet,Router edge
class LAN lan
class Emp,NAS,Print,IoT,Guest,Admin host
The flat design offers no network-layer boundary between visitors, endpoints, infrastructure, and business data. Application permissions might exist on the NAS or wiki, but the network makes every unnecessary peer connection reachable. A single compromised workstation or IoT device can scan the entire subnet and attempt lateral movement.
The deeper problem is trust inheritance. In a flat network, every device inherits the trust of every other device. There’s no way to say “this zone can reach that service, but that zone cannot.”
Principal Risks
| Risk | Impact |
|---|---|
| Flat LAN | Any compromised device can scan and attempt connections to NAS and peers. |
| Shared staff/guest Wi-Fi | Guests become indistinguishable from staff and can probe internal devices. |
| NAS port forwarding | File service and admin interface reachable from the internet, inviting credential attacks. |
| Shared/default credentials | Easy takeover and no accountability. |
| No backups | Ransomware, accidental deletion, hardware failure, theft may be unrecoverable. |
| No logging | Incidents and misconfiguration are difficult to identify. |
The STRIDE Threat Model
Before touching a firewall, I applied the STRIDE threat model to the current state. STRIDE forces the analyst to consider six distinct categories of attack, each mapping to a specific control family:
flowchart LR
S[Spoofing] --> C1[Unique accounts<br/>revoke shared PSKs<br/>rotate credentials]
T[Tampering] --> C2[VLANs<br/>endpoint controls<br/>versioned backups]
R[Repudiation] --> C3[NTP<br/>central syslog<br/>retention controls]
I[Information disclosure] --> C4[Remove forward<br/>VPN access<br/>guest isolation]
D[Denial of service] --> C5[Supported edge<br/>UPS<br/>tested recovery]
E[Elevation of privilege] --> C6[Credential reset<br/>management VLAN<br/>MFA + patching]
classDef threat fill:#3b1e1e,stroke:#fb7185,color:#fff
classDef ctrl fill:#1e2f1e,stroke:#4ade80,color:#fff
class S,T,R,I,D,E threat
class C1,C2,C3,C4,C5,C6 ctrl
Each STRIDE category maps to a specific control family. Spoofing is mitigated by named accounts and rotated credentials; Tampering by VLANs and versioned backups; Repudiation by NTP and central logging; Information disclosure by removing the NAS port forward and isolating guests; Denial of service by supported hardware and tested recovery; and Elevation of privilege by management VLANs, MFA, and explicit inter-VLAN rules. The control list is deliberately narrow: each category has one or two primary controls, not a laundry list, because small businesses cannot maintain sprawling security stacks.
Part 2 — Network Segmentation
The modernization replaces the flat LAN with six purpose-defined zones, each on its own VLAN, connected by a stateful firewall with default-deny inter-zone policy:
| VLAN | Zone | Subnet | Trust Level |
|---|---|---|---|
| 10 | Staff | 10.20.10.0/24 | Managed endpoints |
| 20 | Servers | 10.20.20.0/24 | Business services |
| 30 | IoT | 10.20.30.0/24 | Untrusted |
| 40 | Guest | 10.20.40.0/24 | Internet only |
| 50 | Management | 10.20.50.0/24 | Admin-only |
| 60 | VPN / Contractors | 10.20.60.0/24 | Authenticated remote |
Each zone is defined by a purpose, a subnet, and a policy direction:
flowchart TB
Internet((Internet)) --> FW[Stateful firewall<br/>WAN inbound deny]
FW --> SW[Managed VLAN-aware switch + business APs]
SW --> V10["VLAN 10 — Staff<br/>10.20.10.0/24"]
SW --> V20["VLAN 20 — Servers<br/>10.20.20.0/24"]
SW --> V30["VLAN 30 — IoT<br/>10.20.30.0/24"]
SW --> V40["VLAN 40 — Guest<br/>10.20.40.0/24"]
SW --> V50["VLAN 50 — Management<br/>10.20.50.0/24"]
FW -. "Unique identities; scoped policy" .-> V60["VPN / Contractors<br/>10.20.60.0/24"]
V10 -->|"HTTPS to approved services"| Proxy[Internal wiki / reverse proxy]
V60 -->|"HTTPS to assigned resources"| Proxy
Proxy --> NAS[NAS / internal data services]
V50 -->|"Admin protocols from approved hosts"| Infra[Firewall, switch, AP management]
V30 -. "Only documented destinations" .-> IoTServices[Required IoT services]
V40 -. "Internal access denied" .-> V20
V40 -->|"Internet only"| FW
V30 -->|"Restricted egress"| FW
FW --> Logs[Central log collector / alerts]
V20 --> Backup[Protected backup target]
classDef edge fill:#3b1e1e,stroke:#fb7185,color:#fff
classDef zone fill:#1e3a5f,stroke:#38bdf8,color:#fff
classDef infra fill:#1e2f1e,stroke:#4ade80,color:#fff
classDef vpn fill:#2f1e3b,stroke:#a78bfa,color:#fff
class Internet,FW edge
class V10,V20,V30,V40,V50 zone
class SW,Proxy,NAS,Infra,IoTServices,Logs,Backup infra
class V60 vpn
- Staff reach approved internal services over HTTPS.
- Servers host the wiki, NAS, and log collector.
- IoT is limited to documented destinations.
- Guest is internet-only, with no access to internal address space.
- Management is admin-only, reachable only from approved admin hosts.
- Contractors reach only their assigned resources over VPN.
Why Six Zones?
The zone count is not arbitrary. Each zone represents a distinct trust level that needs its own policy. Staff need internal service access. Guests need internet-only. IoT needs restricted egress. Management needs admin protocols but no general access. Contractors need scoped remote access.
Collapsing any two zones would force a tradeoff between usability and security. Merging Staff and Servers would make the wiki reachable from every staff device, including any compromised one. Merging Guest and Staff would restore the original flat-network problem. Merging Management into Staff would eliminate the admin boundary entirely.
Segmentation is not about adding complexity — it’s about eliminating unnecessary trust.
IPv6 Parity
The most common small-business failure mode is to implement a strong IPv4 policy while leaving IPv6 auto-configured and unfiltered on every VLAN. Either design and filter IPv6 end-to-end in each zone — including router advertisements and guest isolation — or explicitly disable it at the edge and on internal networks until supported. Do not secure only IPv4 and leave an unfiltered IPv6 path.
Part 3 — Firewall Architecture
The single most important design decision in the firewall policy is the rule evaluation order:
flowchart TD
A[Inbound packet arrives on zone interface] --> B{Source zone<br/>and dest zone<br/>in policy matrix?}
B -->|No match| DENY[Deny + log]
B -->|Match found| C{Stateful<br/>session<br/>exists?}
C -->|Yes| ALLOW[Allow return traffic]
C -->|No| D{Source IP in<br/>allowed host set?}
D -->|No| DENY
D -->|Yes| E{Destination port<br/>in allowed set?}
E -->|No| DENY
E -->|Yes| F{Application-layer<br/>auth required?}
F -->|Yes| G[Proxy authenticates<br/>then forwards]
F -->|No| ALLOW
G --> ALLOW
classDef step fill:#1e3a5f,stroke:#38bdf8,color:#fff
classDef deny fill:#3b1e1e,stroke:#fb7185,color:#fff
classDef allow fill:#1e2f1e,stroke:#4ade80,color:#fff
class A,B,C,D,E,F,G step
class DENY deny
class ALLOW allow
Every packet first checks whether the source/destination zone pair has a policy entry at all; if not, it is denied and logged. If a policy entry exists, the firewall then evaluates the connection state, the source host, the destination port, and — where applicable — whether application-layer authentication is required.
The key insight is that the first decision is the zone pair, not the port. This is what makes the rule set maintainable as zones grow. Adding a new zone means adding new zone-pair entries, not rewriting every port-based rule.
Inter-VLAN Policy Matrix
| Source → destination | Policy |
|---|---|
| Staff VLAN 10 → firewall DHCP/DNS/NTP | Allow required local services only |
| Staff → NAS VLAN 20 | Allow SMB TCP/445 to NAS only for approved users/devices |
| Staff → wiki VLAN 20 | Allow HTTPS TCP/443 to wiki only |
| Staff → Internet | Allow outbound DNS, NTP, and required web access |
| Staff → IoT VLAN 30 | Deny by default |
| IoT VLAN 30 → Staff/Servers/Mgmt | Deny by default |
| IoT → Internet | Allow only required destinations/ports where practical |
| Guest VLAN 40 → APEX_NETS/RFC1918 | Block all IPv4 and IPv6 internal destinations |
| Guest → Internet | Allow DHCP/DNS and web access |
| Mgmt VLAN 50 → infrastructure | Allow admin protocols only from approved admin hosts |
| Any other zone → Mgmt VLAN 50 | Deny |
| VPN contractors → internal | Per-contractor groups: allow only the required resource |
Every unlisted traffic path is denied and logged.
WAN Hardening
- Remove all NAS/remote-admin port forwards; remove unused UPnP mappings.
- Deny unsolicited inbound traffic by default. Publish no management interfaces.
- Enable bogon/private-source filtering on WAN where valid.
- Disable firewall GUI/SSH from WAN; allow management only from approved VLAN 50 hosts.
- Keep firmware patched; back up configs encrypted and store a copy offline.
Part 4 — Secure Remote Access
The remote-access design gives each of the three contractors a unique, revocable identity with resource-scoped firewall rules:
sequenceDiagram
participant C as Contractor device
participant FW as Firewall WAN
participant ID as Identity / MFA
participant VPN as VPN zone (10.20.60.0/24)
participant Res as Assigned resource
C->>FW: VPN handshake (WireGuard peer / OpenVPN cert)
FW->>ID: Authenticate identity
ID-->>FW: MFA verified (where required)
FW->>VPN: Assign peer address 10.20.60.x/32
C->>Res: Request assigned resource (HTTPS or SMB)
FW->>FW: Check per-contractor firewall rule
alt Rule allows
FW->>Res: Forward request
Res-->>C: Response
else Rule denies
FW-->>C: Drop + log
end
Note over C,FW: Contract end revoke peer/cert, remove identity, log change
The final note is the operationally critical part: revoking access at contract end requires removing the peer identity, not just letting the peer expire. A contractor who was issued a key and never removed remains a valid peer indefinitely, which is why the lifecycle is documented as part of the design rather than as an operational afterthought.
The WireGuard Caveat
WireGuard is operationally simple, but a key alone is not MFA. If policy requires MFA, put VPN access behind an identity-aware MFA-capable solution or select an OpenVPN deployment with supported MFA integration. Do not claim MFA merely because a key is unique.
Identity Lifecycle Controls
- Named accounts; MFA for firewall admins and identity provider.
- Certificate lifecycle: private keys generated and stored securely; documented expiry; CA key offline; revoke promptly.
- Access request approved by the business owner and resource owner; record sponsor, resource, reason, start, and end dates.
- Log successful/failed authentication, peer identity, assigned address, connection times, and configuration changes.
Part 5 — Endpoint Isolation
Endpoint isolation means each client device is confined to its own zone and cannot reach peers within the same zone without going through a policy-controlled path. In practice this means:
- Guest-to-guest isolation. Wireless client isolation is enabled on the guest SSID, so two visitors on the same network cannot see each other.
- IoT peer isolation. IoT devices cannot reach one another unless a documented application requires it.
- Staff endpoint isolation is not enforced, because staff need to share files through the NAS rather than peer-to-peer. The NAS is the controlled path.
The Docker lab demonstrates this property for the guest and IoT zones: those clients share no Docker network with the wiki gateway, so they cannot reach it and they cannot reach each other’s application paths. This is the same isolation property a real wireless controller provides on a production guest SSID.
Part 6 — Monitoring, Logging & IDS
Every infrastructure device forwards security and administration events to a single collector in the server zone:
flowchart LR
FW[Firewall] --> Collector
SW[Switch / AP] --> Collector
NAS[NAS / hosts] --> Collector
VPN[VPN endpoint] --> Collector
Collector[Internal log collector<br/>VLAN 20] --> Restricted[Restricted admin access]
Collector --> Retain[Retention / rotation]
Collector --> Alert[Alerts to named on-call owner]
classDef source fill:#1e3a5f,stroke:#38bdf8,color:#fff
classDef hub fill:#2f1e3b,stroke:#a78bfa,color:#fff
classDef out fill:#1e2f1e,stroke:#4ade80,color:#fff
class FW,SW,NAS,VPN source
class Collector hub
class Restricted,Retain,Alert out
The collector is placed in VLAN 20, never exposed to the internet, and restricts ingestion to known senders. Log retention is 30–90 days searchable, with longer archives only if justified by incident, contractual, or privacy requirements.
Suricata Deployment
Suricata is deployed in stages, not all at once:
| Step | Action |
|---|---|
| 1 | Install the supported Suricata package on the firewall |
| 2 | Begin in IDS/alert-only mode on WAN and selected internal interfaces |
| 3 | Select maintained rule sources (e.g. ET Open where license and platform terms permit) |
| 4 | Set HOME_NET to all internal prefixes including VPN; EXTERNAL_NET appropriately |
| 5 | Verify alerts are logged and forwarded; tune false positives with documented rationale |
| 6 | Consider IPS/block mode only after observation, capacity testing, and a rollback plan |
| 7 | Test with benign, controlled lab traffic and official rule test indicators |
A signature alert does not prove compromise; absence of an alert does not prove safety. TLS encryption limits payload inspection, and malware callback detection is not guaranteed. This is why the design starts in IDS-only mode and only moves to IPS after the alert pipeline is validated.
Part 7 — Incident-Response Testing
A design that has never been tested is a hypothesis. The lab and production validation plan together cover the full incident-response lifecycle.
The Docker Lab
I built a Docker Compose lab that simulates the six trust zones with isolated bridge networks:
flowchart LR
subgraph STAFF["Docker network: Staff 10.20.10.0/24"]
S[client-staff<br/>10.20.10.10]
GW_S[wiki-gateway<br/>10.20.10.254:443]
end
subgraph CONTRACTOR["Docker network: Contractors 10.20.60.0/24"]
C[client-contractor<br/>10.20.60.10]
GW_C[wiki-gateway<br/>10.20.60.254:443]
end
subgraph SERVERS["Docker network: Servers 10.20.20.0/24"]
GW_V[wiki-gateway<br/>10.20.20.20]
W[mock-wiki<br/>10.20.20.21:80]
end
subgraph GUEST["Docker network: Guest 10.20.40.0/24"]
G[client-guest<br/>10.20.40.10]
end
subgraph IOT["Docker network: IoT 10.20.30.0/24"]
I[client-iot<br/>10.20.30.10]
end
subgraph MGMT["Docker network: Management 10.20.50.0/24"]
A[client-admin<br/>10.20.50.11]
M[management-console<br/>10.20.50.10:80]
end
S -->|"HTTPS allow"| GW_S
C -->|"HTTPS allow"| GW_C
GW_S --- GW_V
GW_C --- GW_V
GW_V -->|"HTTP upstream on server network"| W
G -. "No shared network: denied" .-> GW_S
I -. "No shared network: denied" .-> GW_S
A -->|"Management-zone allow"| M
classDef allow fill:#1e2f1e,stroke:#4ade80,color:#fff
classDef deny fill:#3b1e1e,stroke:#fb7185,color:#fff
classDef hub fill:#1e3a5f,stroke:#38bdf8,color:#fff
class S,C,A,GW_S,GW_C,GW_V,W,M hub
The wiki gateway is intentionally multi-homed on Staff, Contractors, and Servers. Guest and IoT clients share no Docker network with the gateway, so they cannot reach it. The management console is attached only to the Management network, so only the admin client can reach it.
Seven Executable Assertions
A Python acceptance-test runner exercises seven explicit allow/deny cases:
PASS: Staff can access the HTTPS wiki
PASS: Contractor can access the HTTPS wiki
PASS: Guest cannot access the wiki
PASS: IoT cannot access the wiki
PASS: Staff cannot access management
PASS: Guest cannot access management
PASS: Admin can access management
An allow is valid only when the command succeeds and the expected response body is present. A deny is valid when the request does not succeed. The runner returns exit code 0 if all assertions pass and 1 if any fail, which makes it CI-integratable. A GitHub Actions workflow validates the lab end-to-end on every push, keeping it from silently drifting away from the documented allow/deny matrix.
A passing negative test is evidence that the boundary exists, not evidence that something is broken.
Production Validation Plan
The lab is only the first stage. Production validation adds:
| Step | Action |
|---|---|
| 1 | Configuration review: firewall rules, switch VLAN assignments, AP-to-SSID mapping, DHCP scopes, IPv6 controls |
| 2 | Cross-zone tests: representative test devices in each VLAN |
| 3 | Authorized Nmap checks: only approved targets, during an agreed window |
| 4 | Packet capture: authorized SPAN/mirror point to validate isolation |
| 5 | Identity and remote access: MFA, per-contractor scope, revocation |
| 6 | Detection: Suricata alerts with benign indicators |
| 7 | Recovery: restore representative data from protected backups |
The pilot migration is the critical checkpoint: if applications, DNS, printing, and roaming do not work correctly on the pilot endpoints, no other endpoints are migrated until the issues are resolved.
What the Lab Is — and What It Isn’t
I want to be honest about the lab’s scope. The Docker lab demonstrates reachability isolation, not a full routed firewall deployment:
- Docker bridge networks stand in for zones; they are not physical 802.1Q VLANs.
- The proxy has interfaces on Staff, Contractor, and Servers. Its network membership allows those zones to reach the proxy, but Docker does not enforce the full stateful inter-VLAN ACL policy.
- Contractors are simulated as already attached clients — there is no WireGuard tunnel, identity provider, MFA, or certificate issuance.
- There is no DHCP, WAN/NAT, IPv6 policy, NAS, Suricata sensor, or centralized logging in the running lab.
- The Caddy internal CA is for the lab only; the test runner uses
curl --insecure.
This is intentional. A lab that overclaims what it demonstrates is worse than no lab at all. The lab proves one specific property — that the intended allow/deny pattern is expressible in code and testable — and it proves that property rigorously.
For production, the design maps to real hardware:
| Lab construct | Production counterpart |
|---|---|
| Docker bridge network | Switch VLAN, AP SSID mapping, firewall VLAN interface |
| Multi-homed Caddy container | Internal reverse proxy in server/DMZ with routed firewall rules |
| Contractor Docker network | VPN address pool with authenticated peer/user |
| Management-only Docker network | Dedicated management VLAN / true OOB path |
| Python curl acceptance suite | Firewall, service, and end-to-end acceptance tests |
The Five Architectural Decisions
The project rests on five decisions that distinguish a design that works in a lab from a design that works in production:
1. Segmentation Over Trust
Six purpose-defined zones replace the flat LAN, so a compromised device in one zone cannot inherit the trust of another. This is the foundational decision — everything else depends on it.
2. Default-Deny Inter-Zone Policy
Every zone pair has an explicit policy entry; nothing is allowed by assumption. The first decision in the firewall flow is the zone pair, not the port.
3. Named Identities and Lifecycle Controls
Shared credentials are eliminated. Every admin and contractor has a named, revocable identity. Access is removed at contract end, not left to expire.
4. Centralized Observability
Firewall, VPN, switch, AP, and server logs flow to a restricted collector with a named alert owner and a documented retention policy. The collector is never exposed to the internet.
5. Tested Recovery, Not Assumed Recovery
Backups are only considered a control once a restore has been demonstrated against the approved RPO/RTO. A backup that has never been restored is a file, not a capability.
Lessons Learned
Segmentation is only effective when the whole stack agrees. A switch, AP, firewall, endpoint, and service ACL all need to enforce the same policy. If any one of them disagrees, the boundary leaks.
A WireGuard key is not MFA. If policy requires MFA, VPN access needs to sit behind an identity-aware solution or use a VPN deployment with supported MFA integration.
IDS alerts are not proof of compromise, and no alert is not proof of safety. TLS encryption limits payload inspection. Start in IDS/alert-only mode and only move to IPS after capacity testing and rollback validation.
A backup is not a recovery capability until a restore has been tested. This is the single most commonly skipped step in small-business security.
The most common small-business failure mode is IPv4-only security. Strong IPv4 policy while leaving IPv6 auto-configured and unfiltered on every VLAN. Either design and filter IPv6 end-to-end, or explicitly disable it.
The Broader Portfolio Story
This project is the third in a series of portfolio-grade engineering case studies that together demonstrate different classes of systems-engineering discipline:
| Project | Discipline | Core Question |
|---|---|---|
| Harbourview Motel Management | Legacy modernization | How do we replace a paper-based process with a reliable offline-first application? |
| HVAC Integration Hub | Cross-system orchestration | How do multiple independent systems communicate reliably without sharing databases? |
| Apex Network Security | Infrastructure modernization | How do we segment a flat network into trust zones that are testable? |
Each project is validated by a reproducible, testable lab rather than by assertion. The Apex lab proves reachability isolation; the HVAC lab proves retry and idempotency; the Harbourview prototype proves offline synchronization and audit integrity.
Together they cover the three halves of the modernization problem:
- Replacing what no longer works (Harbourview)
- Orchestrating what needs to communicate (HVAC)
- Segmenting what needs boundaries (Apex)
What’s Next
The lab is complete, but the production migration is not. The next steps are:
- Real routed firewall lab — pfSense or OPNsense on Proxmox with isolated virtual switches
- Authenticated VPN — unique contractor identities with MFA and revocation tests
- Suricata in alert-only mode — measure capacity and tune before considering IPS blocking
- Central logging — forward firewall, AP/switch, server, and VPN logs with documented retention
- Backup and restore — encrypted/versioned backups with a scheduled restore test
- IPv6 parity — specific rules and tests, service health checks, image digest pinning
Final Thoughts
Small-business security is not a matter of buying more equipment. It is a matter of deciding what the boundaries are, enforcing them at every layer, and proving that the enforcement works.
The Apex engagement demonstrates that segmentation does not require enterprise-grade hardware or operational complexity that the business cannot sustain. Six zones, one stateful firewall, one managed switch, and a tested recovery process are enough to eliminate the most common small-business attack paths.
And critically — the design is provable. The seven passing allow/deny tests are not a marketing claim; they are executable evidence that the intended boundaries hold. That is the property that makes the design transferable to production hardware and defensible to a client.
Related reading: