Post

Designing a Segmented Active Directory Lab Behind a pfSense Firewall

How I designed the network for my Hybrid Identity Detection Lab, why a firewall has to sit between the attacker and the domain, and the design mistake I caught before building anything.

Designing a Segmented Active Directory Lab Behind a pfSense Firewall

Overview

This is the first post in my Hybrid Identity Detection Lab project. The goal is to build an Active Directory domain, harden it, attack it, detect the attacks, investigate the compromised domain controller, and then extend the lab into Microsoft Entra ID and Microsoft Sentinel.

Before installing anything I designed the network. This post covers the design and the reasoning behind it, including a mistake in my first version.

The full design document is in the project repository: hybrid-identity-detection-lab on GitHub.


The problem

The tutorial that started this project has the domain controller do the routing and NAT. That works for a demo, but real networks separate those jobs. I also had a specific requirement: a port scan from the attacker should be visible to a firewall, so I can write and test network-level detections later.

The network had to meet four requirements:

  1. Behave like a real Active Directory environment.
  2. Force attacker traffic through a firewall.
  3. Stay isolated, with internet access for updates only.
  4. Be easy to reset with snapshots.

Environment

  • Host: Arch Linux laptop, 16 GB RAM, VirtualBox 7.2
  • Firewall: pfSense CE (VM fw01)
  • Domain controller: Windows Server 2022 evaluation (VM DC01)
  • Client: Windows 11 Pro (VM win11)
  • Attacker: the Arch host itself

Desktop View The lab VMs in VirtualBox, each with a fresh-install snapshot


My first design, and why it was wrong

My first version put everything on one subnet, 10.0.10.0/24, with pfSense as the default gateway. It looked simple, and the firewall still sat in the path to the internet.

Then I rechecked it against the project goals and found the flaw.

A router or firewall only sees traffic that crosses a subnet boundary. Traffic between two hosts on the same subnet is switched at Layer 2 and never reaches the gateway.

If the attacker and the domain controller share a subnet, a port scan against the DC goes straight across the virtual switch. pfSense never sees it, so it can neither log nor block it. My simplification would have made one of the lab’s own objectives impossible.


The corrected design: two zones

I moved the attacker to its own subnet so that all attack traffic has to pass through the firewall.

ZoneVirtualBox modeSubnetMembers
AttackerHost-Only (vboxnet1)10.0.66.0/24Arch host (10.0.66.2)
LabInternal Network (adlab-net)10.0.10.0/24dc01, ws01
InternetNATfrom VirtualBoxpfSense WAN only

pfSense has three interfaces, one in each zone:

InterfaceAddressRole
WANDHCP (NAT)Internet for updates
LAN10.0.10.1Gateway for the lab zone
OPT110.0.66.1Gateway for the attacker zone and the sensor point

Desktop View Two zones with pfSense between them


Why each decision

Why Host-Only for the attacker

Host-Only is the only VirtualBox mode that puts the physical host on a virtual network with VMs. Since my Arch machine is the attacker, it has to sit on that wire.

Why Internal Network for the lab

An Internal Network is sealed from the host and the internet. The only way out of the domain controller and the client is through pfSense, so the firewall sees everything they send.

Why a static route on the host

The host is directly connected to 10.0.66.0/24 but knows nothing about 10.0.10.0/24. Without a route, traffic for the lab goes to my real router and leaves my machine. One route fixes that:

1
sudo ip route add 10.0.10.0/24 via 10.0.66.1

The replies come back by themselves. The lab machines use pfSense as their default gateway, and pfSense knows the attacker subnet is on OPT1.

Why DNS and DHCP live on the domain controller

Active Directory finds domain controllers through DNS records, so the client must use dc01 as its DNS server. DHCP on the DC hands out the gateway and the DNS server together, so DHCP on pfSense is turned off to avoid two servers answering.

The trade-off I chose

Servers and clients share one subnet. In production I would split them, but it adds routing and DHCP relay work without teaching more about identity attacks. The design extends cleanly later: add an interface and move the client.


Safety controls

  • No bridged adapters, so nothing reaches the real network
  • The Arch host must not forward lab traffic to my real network. IP forwarding is on, because Docker is installed. I checked the firewall: the IPv4 FORWARD policy is DROP and Docker’s chains only accept traffic from Docker’s own bridges, so nothing forwards traffic from the lab interface. IPv6 is blocked only by the kernel setting net.ipv6.conf.all.forwarding = 0, while its firewall policy is ACCEPT, which is a weaker control that I have noted as an open item
  • My existing VirtualBox networks are untouched: I created a new Host-Only adapter and a new internal network name
  • Windows Server evaluation media only, and Windows 11 installed without a product key
  • A snapshot before every major change and before every attack

Outcome

  • A written design with addressing, firewall policy and a verification checklist
  • A network that makes every attack path pass through a logging firewall
  • A host-side adapter, vboxnet1, configured as 10.0.66.2/24

What I would do differently

I should have tested the design against the objectives before writing it up. The single-subnet version looked finished and was wrong. Checking the design against what I needed to detect is now part of how I review a network.

Next

Installing pfSense and assigning its three interfaces. That is covered in the next post.

This post is licensed under CC BY 4.0 by the author.