Simulating Enterprise Network Infrastructures with KVM/QEMU & virt-manager
Created: 2026-11-05 Β· Last updated: 2026-11-06 Β· Nejat Hakan
Designing, hardening, and troubleshooting multi-zone network architecturesβcomplete with perimeter firewalls, isolated DMZs, internal LANs, authoritative DNS servers, and reverse proxiesβrequires a realistic testing ground. Building such a lab with physical switches, routers, and rack servers is expensive, noisy, and rigid.
By combining the Linux Kernel-based Virtual Machine (KVM), QEMU, libvirt, and virt-manager, you can transform a single Linux workstation or server into a Type-1 enterprise virtualization lab capable of running complete multi-subnet network topologies at near-native hardware speed.
1. The Linux Virtualization Architecture (KVM + QEMU + libvirt)
A frequent point of confusion for newcomers is how KVM, QEMU, libvirt, and virt-manager relate to each other. Rather than competing tools, they form a clean 4-layer virtualization stack:
%%{init: {'flowchart': {'wrappingWidth': 600}}}%%
flowchart TB
L4["π₯οΈ Layer 4: Management Clients & UIs<br/>virt-manager (GUI) Β· virsh / virt-install (CLI) Β· Cockpit (Web-UI)"]
L3["βοΈ Layer 3: libvirtd Orchestration Daemon<br/>qemu:///system Β· XML Domains Β· Storage Pools Β· virbr0 Linux Bridges"]
L2["π¦ Layer 2: QEMU Machine Emulator (User Space)<br/>qemu-system-x86_64 Processes Β· VirtIO Paravirtualization Β· UEFI (OVMF)"]
L1["π§ Layer 1: KVM Linux Kernel Hypervisor (/dev/kvm)<br/>kvm-intel / kvm-amd Β· Direct Hardware Virtualization of vCPU & vRAM"]
HW["π© Bare-Metal Host Hardware<br/>Intel VT-x / AMD-V CPU Extensions & NVMe / SSD Storage"]
L4 ==>|"UNIX RPC Socket: /run/libvirt/libvirt-sock"| L3
L3 ==>|"Process Lifecycle & XML Generation"| L2
L2 <==>|"ioctl(/dev/kvm) System Calls"| L1
L1 <==>|"Ring -1 Root-Mode CPU Execution"| HW
The Four Core Components Explained
-
1. KVM (
kvm.ko) β The Hypervisor Core
A mainline Linux kernel module that turns the Linux kernel itself into a Type-1 Bare-Metal Hypervisor. It exposes
/dev/kvmto execute guest CPU instructions directly on physical Intel VT-x or AMD AMD-V silicon. -
2. QEMU β The Hardware & VirtIO Emulator
While KVM handles CPU and RAM virtualization, QEMU runs as an unprivileged user-space process per VM, emulating motherboards, UEFI (
OVMF), PCI buses, and high-speed paravirtualized VirtIO disks and network cards. -
3.
libvirtd&virshβ The Control Plane
A system daemon that manages VM lifecycles, declarative XML configurations, virtual Layer-2 switches (
virbr*),dnsmasqDHCP/DNS instances, and storage pools. -
4.
virt-managerβ The Graphical Console
A feature-rich desktop GUI client (with built-in SPICE / VNC graphical consoles, snapshot managers, and live CPU/RAM/Disk/Network telemetry graphs) talking to
libvirtdlocally or remotely over SSH.
2. Host Setup & Hardware Verification
Step 1: Pre-Flight Hardware Virtualization Check
Verify that Intel VT-x (vmx) or AMD AMD-V (svm) extensions are enabled in your BIOS/UEFI and that the /dev/kvm device node is active:
# 1. Check CPU virtualization flags (look for VT-x or AMD-V)
lscpu | grep -i virtualization
# 2. Verify that kvm and kvm_intel / kvm_amd kernel modules are loaded
lsmod | grep kvm
# 3. Verify /dev/kvm device node permissions
ls -la /dev/kvm
Step 2: Install the Complete Virtualization Stack
Select your host Linux distribution below to install KVM, QEMU, libvirt, virt-manager, bridge-utils, and UEFI (OVMF) firmware support:
sudo apt update
sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients \
bridge-utils virt-manager virtinst ovmf swtpm spice-vdagent cpu-checker
# Grant your user permission to manage system VMs without sudo
sudo usermod -aG libvirt,kvm $(whoami)
# Verify KVM hardware acceleration readiness
kvm-ok
Log Out or Run newgrp libvirt After Adding Groups!
Linux only evaluates group memberships at login. After running sudo usermod -aG libvirt,kvm $(whoami), either log out and back into your desktop session or run newgrp libvirt in your current shell before testing virsh list --all.
Step 3: Verify libvirtd & Default Network Status
# 1. Verify daemon status
sudo systemctl is-active libvirtd
# 2. Test unprivileged virsh access
virsh list --all
# 3. Verify the 'default' NAT network and storage pool are active
virsh net-list --all
virsh pool-list --all
3. Virtual Disk Architectures, Storage Pools & Snapshots
Virtual machines store their operating systems and data inside Storage Volumes carved out of Storage Pools (by default located at /var/lib/libvirt/images/).
Disk Image Formats: qcow2 vs. raw
| Feature / Dimension | qcow2 (QEMU Copy-On-Write v2) |
raw (Bit-for-Bit Image) |
|---|---|---|
| Provisioning Default | Thin Provisioned (Grows dynamically on demand) | Thick Provisioned (Reserves full size immediately) |
| Internal Snapshots | β Yes (Supports multi-branch disk & live RAM snapshots) | β No (Requires external LVM/ZFS/Btrfs snapshots) |
| Backing Files (Clones) | β Yes (Instant Copy-on-Write linked clones from a base image) | β No |
| Compression & AES Crypto | β
Yes (zlib / zstd compression & LUKS encryption) |
β No |
| I/O Performance | Near-native (especially with VirtIO bus + preallocation=metadata) |
Maximum raw block throughput (zero metadata overhead) |
| Recommended Use Case | Default for 99% of Lab & Server VMs | High-IOPS Databases or LVM/Ceph block volumes |
Engineering Deep-Dive: Two Real-World virt-manager Storage Quirks Explained
When creating custom storage pools and volumes in virt-manager, you will encounter two subtle behaviors:
- Why Relative Paths (
~/kvm_storage_pool) Fail invirt-manager:
When connected toQEMU/KVM(qemu:///system), thelibvirtddaemon runs asroot, not as your desktop user. If you type~/kvm_storage_poolas a storage pool target path,libvirtddoes not expand your user's tilde (~), resulting in phantom volumes that do not appear in your home directory. Always specify the full absolute path (e.g.,/home/nejat/kvm_storage_pool)! - Why
virt-managerGUIqcow2Volumes Sometimes Allocate Full Space vs.qemu-imgCLI:
When you runqemu-img create -f qcow2 disk.qcow2 5Gin the terminal, QEMU creates a pure sparse file consuming only~200 KiBon disk. By contrast, when creating a volume via thevirt-managerGUI inside certain directory pools,libvirtinvokesposix_fallocate()unless sparse allocation is explicitly enforced, immediately reserving the blocks on disk.
Hands-On Lab 1: Mastering qemu-img Creation, Inspection & Format Conversion
Use the qemu-img CLI tool to create thin-provisioned qcow2 disks, thick raw disks, and convert between formats (including VMware .vmdk or VirtualBox .vdi images):
# 1. Create a temporary lab directory
mkdir -p ~/kvm_disk_lab && cd ~/kvm_disk_lab
# 2. Create a 5 GiB thin-provisioned (sparse) qcow2 image
qemu-img create -f qcow2 thin_disk.qcow2 5G
# 3. Inspect virtual size (5 GiB) vs. actual physical disk usage (~196 KiB!)
qemu-img info thin_disk.qcow2
# 4. Create a 1 GiB raw image and convert it into a compressed qcow2 image
qemu-img create -f raw thick_disk.raw 1G
qemu-img convert -c -f raw -O qcow2 thick_disk.raw converted_disk.qcow2
qemu-img info converted_disk.qcow2
# 5. Clean up lab files
cd ~ && rm -rf ~/kvm_disk_lab
4. Virtual Networking Modes in libvirt & KVM
To simulate realistic enterprise infrastructures, libvirt provides three core virtual networking modes using Linux kernel bridges (virbr*) and tap interfaces (vnet*):
%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
subgraph TopLevel ["π₯οΈ Virtual Machines (KVM / QEMU Guest Tier)"]
VM_NAT["π₯οΈ NAT VM<br/>(192.168.122.x)"]
VM_ISO1["π₯οΈ Isolated VM A<br/>(10.0.1.10)"]
VM_ISO2["π₯οΈ Isolated VM B<br/>(10.0.1.50)"]
VM_BR["π₯οΈ LAN Bridge VM<br/>(192.168.1.x)"]
end
subgraph Bridges ["π§ Linux KVM Host: libvirt Bridges & Kernel Switches"]
direction TB
B_NAT["1οΈβ£ virbr0 (NAT Bridge)<br/>192.168.122.1 / dnsmasq"]
B_ISO["2οΈβ£ virbr1 (Isolated L2 Switch)<br/>Pure Bridge (Zero Routing)"]
B_BR["3οΈβ£ br0 (Physical Host Bridge)<br/>Enslaved eth0 on LAN"]
end
VM_NAT <==>|"vnet0"| B_NAT
VM_ISO1 <==>|"vnet1"| B_ISO
VM_ISO2 <==>|"vnet2"| B_ISO
VM_BR <==>|"vnet3"| B_BR
NIC["π Host Physical NIC (eth0)"]
UPLINK(("π Physical LAN Router & Internet<br/>(192.168.1.1)"))
B_NAT ==>|"Host NAT / MASQUERADE"| NIC
B_BR ==>|"Layer-2 Trunk"| NIC
NIC <==> UPLINK
Comparing the 4 Virtual Network Topologies
| Network Mode | Outbound Internet? | Inbound from Physical LAN? | VM-to-VM & Host Access? | Primary Lab Use Case |
|---|---|---|---|---|
1. NAT (default / virbr0) |
β Yes (via Host NAT) | β No (Unless Port-Forwarded) | β
Yes (192.168.122.1) |
WAN Uplink for your virtual pfSense firewall or quick standalone VMs |
2. Isolated (lan1-net, dmz1-net) |
β No (Strictly Air-Gapped) | β No | β Yes (Same switch only) | Internal LAN & DMZ segments routed exclusively through a virtual firewall |
3. Host Bridge (br0) |
β Yes (Direct via LAN) | β Yes (Gets LAN DHCP IP) | β Yes | Exposing a production VM directly to your home/office physical network |
| 4. Routed Mode | β Yes (With static routes) | β Yes (With static routes) | β Yes | Advanced BGP/OSPF routing labs without NAT |
5. Architecting Our Multi-Zone Enterprise Lab Topology
Now we combine everything to build a complete, realistic 3-Zone Enterprise Network Infrastructure inside KVM/QEMU.
Complete Network & IP Architecture Diagram
%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
Internet(("π Internet & Host NAT<br/>virbr0 (192.168.122.0/24)<br/>Gateway: 192.168.122.1"))
Internet <==>|"WAN (vtnet0)<br/>DHCP: 192.168.122.x"| FW
subgraph Firewall ["π‘οΈ Perimeter Router & Stateful Firewall"]
FW["π₯ fw01 (pfSense CE)<br/>WAN: vtnet0 (DHCP)<br/>LAN: vtnet1 (10.0.1.1/24)<br/>DMZ: vtnet2 (10.0.2.1/24)"]
end
FW <==>|"LAN Interface (vtnet1)<br/>Gateway: 10.0.1.1"| SwitchLAN["π§ Isolated Switch: lan1-net<br/>Subnet: 10.0.1.0/24"]
FW <==>|"DMZ Interface (vtnet2)<br/>Gateway: 10.0.2.1"| SwitchDMZ["π§ Isolated Switch: dmz1-net<br/>Subnet: 10.0.2.0/24"]
subgraph ZoneLAN ["π’ Internal Trusted LAN Zone (10.0.1.0/24)"]
SRV01["ποΈ srv01.yourlab.lan<br/>Ubuntu Server (BIND9 DNS)<br/>Static IP: 10.0.1.10/24"]
CLIENT01["π» client01.yourlab.lan<br/>Linux Client + Wireshark<br/>Static/DHCP IP: 10.0.1.50/24"]
end
subgraph ZoneDMZ ["π Demilitarized Zone / DMZ (10.0.2.0/24)"]
DMZ01["π dmz01.yourlab.lan<br/>Ubuntu Server (Nginx VHosts)<br/>Static IP: 10.0.2.20/24"]
end
SwitchLAN === SRV01 & CLIENT01
SwitchDMZ === DMZ01
Master IP & VM Allocation Table
VM Name (libvirt) |
Hostname (FQDN) | Role & Services | vCPUs / RAM / Disk | Network Interfaces & IP Addresses |
|---|---|---|---|---|
fw01 |
fw01.yourlab.lan |
pfSense Stateful Firewall, NAT & DHCP | 1 vCPU / 1 GB / 10 GB |
β’ WAN (vtnet0): default (DHCP 192.168.122.x)β’ LAN ( vtnet1): lan1-net (10.0.1.1/24)β’ DMZ ( vtnet2): dmz1-net (10.0.2.1/24) |
ubuntu-server-01 |
srv01.yourlab.lanns1.yourlab.lan |
BIND9 Authoritative & Recursive DNS | 2 vCPU / 2 GB / 20 GB |
β’ LAN (enp1s0): lan1-net (10.0.1.10/24, GW 10.0.1.1) |
client-vm-01 |
client01.yourlab.lan |
Admin Workstation, Browser & Wireshark | 2 vCPU / 2 GB / 15 GB |
β’ LAN (enp1s0): lan1-net (10.0.1.50/24, GW 10.0.1.1) |
dmz-server-01 |
dmz01.yourlab.lanapp.yourlab.lan |
Nginx Web Server (Multi-VHost) | 1 vCPU / 1 GB / 15 GB |
β’ DMZ (enp1s0): dmz1-net (10.0.2.20/24, GW 10.0.2.1) |
Step 1: Create the Isolated Virtual Switches (lan1-net & dmz1-net)
Because pfSense (fw01) will act as the gateway (10.0.1.1 and 10.0.2.1) and DHCP server for both internal segments, we create lan1-net and dmz1-net as pure Layer-2 isolated switches without libvirt's own dnsmasq DHCP server (so libvirt doesn't steal the .1 gateway IPs from pfSense!):
Run these commands on your KVM host to create, start, and autostart both isolated virtual switches in 5 seconds:
# 1. Define lan1-net (Pure Layer-2 virtual switch virbr1)
cat << 'EOF' > /tmp/lan1-net.xml
<network>
<name>lan1-net</name>
<bridge name='virbr1' stp='on' delay='0'/>
</network>
EOF
# 2. Define dmz1-net (Pure Layer-2 virtual switch virbr2)
cat << 'EOF' > /tmp/dmz1-net.xml
<network>
<name>dmz1-net</name>
<bridge name='virbr2' stp='on' delay='0'/>
</network>
EOF
# 3. Register, start, and enable autostart for both networks
virsh net-define /tmp/lan1-net.xml
virsh net-start lan1-net
virsh net-autostart lan1-net
virsh net-define /tmp/dmz1-net.xml
virsh net-start dmz1-net
virsh net-autostart dmz1-net
rm -f /tmp/lan1-net.xml /tmp/dmz1-net.xml
virsh net-list --all
- Open
virt-managerβEditβConnection DetailsβVirtual Networkstab. - Click
+(Add Network): - Name:
lan1-net - Mode:
Isolated network - (If pfSense will hold
10.0.1.1and run DHCP, uncheck IPv4 DHCP or let pfSense manage the subnet). - Repeat for
dmz1-net.
Step 2: Configure Static IPs on Ubuntu Server VMs (Modern Netplan Syntax)
Deprecation Notice: Netplan gateway4 β routes
On Ubuntu 22.04 LTS and 24.04 LTS, the old gateway4: 10.0.1.1 key is deprecated. Always use the modern routes: - to: default via: ... block shown below!
6. Deploying the Perimeter Firewall (pfSense on fw01)
Step 1: Create the 3-NIC fw01 VM in virt-manager
- Download the pfSense CE AMD64 ISO (
pfSense-CE-*-RELEASE-amd64.iso.gz), extract it withgunzip, and create a new VM namedfw01(OS type:FreeBSD 14.x,1024 MBRAM,1 vCPU,10 GBVirtIOqcow2disk). - Check "Customize configuration before install" and configure three VirtIO NICs:
- NIC 1 (
vtnet0β WAN):Virtual network 'default': NAT - NIC 2 (
vtnet1β LAN):Virtual network 'lan1-net': Isolated - NIC 3 (
vtnet2β OPT1/DMZ):Virtual network 'dmz1-net': Isolated - Complete the UFS/ZFS guided installer, disconnect the ISO image, and reboot into the pfSense console menu.
Step 2: Console Interface & IP Assignment
From the pfSense text console menu, select Option 1 (Assign Interfaces):
* WAN: vtnet0
* LAN: vtnet1
* OPT1: vtnet2
Then select Option 2 (Set interface(s) IP address):
1. LAN (vtnet1): Static IPv4 10.0.1.1/24, enable DHCP range 10.0.1.100 β 10.0.1.200.
2. OPT1 (vtnet2): Static IPv4 10.0.2.1/24, enable DHCP range 10.0.2.100 β 10.0.2.200.
Crucial Lab Setting: Uncheck 'Block RFC1918 Private Networks' on WAN!
Because fw01's WAN interface (vtnet0) connects to libvirt's default NAT network (192.168.122.0/24βwhich is itself an RFC 1918 private subnet!), you must uncheck "Block private networks and loopback addresses" under Interfaces β WAN in the pfSense WebGUI (https://10.0.1.1).
Step 3: Zero-Trust Firewall Policy Matrix (LAN vs. DMZ)
In pfSense (Firewall β Rules), rules are evaluated top-to-bottom on the ingress interface (first match wins; implicit deny at the bottom):
| Interface Tab | Action | Protocol | Source | Destination | Port(s) | Purpose & Security Logic |
|---|---|---|---|---|---|---|
LAN |
π’ Pass |
Any |
LAN net |
Any |
Any |
Allows trusted LAN admins/clients to reach DMZ, WAN, and internal services. |
DMZ |
π’ Pass |
TCP/UDP |
DMZ net |
10.0.1.10 (srv01) |
53 (DNS) |
Allows DMZ servers to query the internal BIND9 DNS server only. |
DMZ |
π΄ Block |
Any |
DMZ net |
LAN net (10.0.1.0/24) |
Any |
Zero-Trust Isolation: Prevents a compromised DMZ server from pivoting into the LAN! |
DMZ |
π’ Pass |
TCP/UDP |
DMZ net |
Any (Internet) |
80, 443, 123 |
Allows DMZ servers to pull apt OS updates and sync NTP time. |
7. Authoritative & Recursive Internal DNS Server (BIND9 on srv01)
To resolve internal FQDNs (srv01.yourlab.lan, dmz01.yourlab.lan, app.yourlab.lan) and reverse PTR lookups (10.0.1.x and 10.0.2.x), we deploy BIND9 on srv01 (10.0.1.10).
Step 1: Install BIND9 & Configure Options
sudo apt update
sudo apt install -y bind9 bind9utils bind9-doc dnsutils
Configure /etc/bind/named.conf.options with strict Access Control Lists (internal-nets) so only LAN1 and DMZ1 can query your resolver:
acl "internal-nets" {
127.0.0.0/8;
10.0.1.0/24;
10.0.2.0/24;
};
options {
directory "/var/cache/bind";
listen-on port 53 { 127.0.0.1; 10.0.1.10; };
listen-on-v6 { none; };
allow-query { internal-nets; };
allow-recursion { internal-nets; };
recursion yes;
forwarders {
10.0.1.1;
1.1.1.1;
};
forward first;
dnssec-validation auto;
};
Step 2: Declare Forward & Reverse Zones (named.conf.local)
zone "yourlab.lan" {
type master;
file "/etc/bind/zones/db.yourlab.lan";
allow-update { none; };
};
zone "1.0.10.in-addr.arpa" {
type master;
file "/etc/bind/zones/db.10.0.1";
allow-update { none; };
};
zone "2.0.10.in-addr.arpa" {
type master;
file "/etc/bind/zones/db.10.0.2";
allow-update { none; };
};
Step 3: Populate Zone Files & Validate Syntax
$TTL 3600
@ IN SOA ns1.yourlab.lan. admin.yourlab.lan. (
2026110501 ; Serial (YYYYMMDDSS)
7200 ; Refresh
3600 ; Retry
1209600 ; Expire
3600 ) ; Negative Cache TTL
;
@ IN NS ns1.yourlab.lan.
; --- LAN1 Hosts (10.0.1.0/24) ---
ns1 IN A 10.0.1.10
srv01 IN A 10.0.1.10
fw01 IN A 10.0.1.1
client01 IN A 10.0.1.50
; --- DMZ1 Hosts (10.0.2.0/24) ---
fw01-dmz IN A 10.0.2.1
dmz01 IN A 10.0.2.20
app IN A 10.0.2.20
; --- Aliases (CNAME) ---
www IN CNAME dmz01.yourlab.lan.
firewall IN CNAME fw01.yourlab.lan.
Validate all syntax before restarting bind9:
sudo mkdir -p /etc/bind/zones
sudo chown -R bind:bind /etc/bind/zones
sudo chmod 755 /etc/bind/zones && sudo chmod 644 /etc/bind/zones/db.*
# Syntax validation (must return 0 errors!)
sudo named-checkconf
sudo named-checkzone yourlab.lan /etc/bind/zones/db.yourlab.lan
sudo named-checkzone 1.0.10.in-addr.arpa /etc/bind/zones/db.10.0.1
sudo named-checkzone 2.0.10.in-addr.arpa /etc/bind/zones/db.10.0.2
sudo systemctl restart bind9
8. Deploying Name-Based Virtual Hosts with Nginx in the DMZ (dmz01)
On dmz01 (10.0.2.20), we deploy Nginx and configure two separate Virtual Hosts (dmz01.yourlab.lan and app.yourlab.lan) sharing the single IP 10.0.2.20 via HTTP Host header routing:
# 1. Install Nginx
sudo apt update && sudo apt install -y nginx
# 2. Create document roots and landing pages
sudo mkdir -p /var/www/dmz01_yourlab_lan/html /var/www/app_yourlab_lan/html
echo "<h1>Welcome to dmz01.yourlab.lan (10.0.2.20)</h1>" | sudo tee /var/www/dmz01_yourlab_lan/html/index.html
echo "<h1>Welcome to app.yourlab.lan (10.0.2.20)</h1>" | sudo tee /var/www/app_yourlab_lan/html/index.html
sudo chown -R www-data:www-data /var/www/dmz01_yourlab_lan /var/www/app_yourlab_lan
# 3. Create Virtual Host configs
cat << 'EOF' | sudo tee /etc/nginx/sites-available/dmz01.yourlab.lan
server {
listen 80 default_server;
server_name dmz01.yourlab.lan www.yourlab.lan;
root /var/www/dmz01_yourlab_lan/html;
index index.html;
location / { try_files $uri $uri/ =404; }
}
EOF
cat << 'EOF' | sudo tee /etc/nginx/sites-available/app.yourlab.lan
server {
listen 80;
server_name app.yourlab.lan;
root /var/www/app_yourlab_lan/html;
index index.html;
location / { try_files $uri $uri/ =404; }
}
EOF
# 4. Enable sites, remove default, test config, and reload
sudo ln -sf /etc/nginx/sites-available/dmz01.yourlab.lan /etc/nginx/sites-enabled/
sudo ln -sf /etc/nginx/sites-available/app.yourlab.lan /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
9. Packet Inspection & Protocol Analysis with Wireshark
Because lan1-net (virbr1) and dmz1-net (virbr2) are Linux bridge interfaces directly on your KVM Host, you can launch Wireshark on your host machine and capture every single packet traversing your virtual network without installing a GUI inside your server VMs!
End-to-End Packet Flow: DNS Lookup + TCP 3-Way Handshake + HTTP GET
When client01 (10.0.1.50) runs curl http://app.yourlab.lan, Wireshark captures the exact multi-layer sequence across virbr1 and virbr2:
%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
subgraph Phase1 ["1οΈβ£ Phase 1: UDP Port 53 β DNS Resolution (on virbr1)"]
D1["client01 (10.0.1.50) β srv01<br/>DNS Query: A app.yourlab.lan"]
D2["srv01 (10.0.1.10) β client01<br/>DNS Response: A 10.0.2.20"]
D1 ==> D2
end
subgraph Phase2 ["2οΈβ£ Phase 2: TCP Port 80 β Three-Way Handshake (Routed via fw01)"]
T1["client01 β dmz01:80<br/>TCP Flags: [SYN] Seq=0"]
T2["dmz01:80 β client01<br/>TCP Flags: [SYN, ACK] Seq=0 Ack=1"]
T3["client01 β dmz01:80<br/>TCP Flags: [ACK] Seq=1 Ack=1"]
T1 ==> T2 ==> T3
end
subgraph Phase3 ["3οΈβ£ Phase 3: HTTP Layer-7 Payload & Graceful Teardown"]
H1["client01 β dmz01<br/>HTTP GET / (Host: app.yourlab.lan)"]
H2["dmz01 β client01<br/>HTTP/1.1 200 OK (Welcome Page)"]
H3["[FIN, ACK] β [FIN, ACK]<br/>(Connection Closed)"]
H1 ==> H2 ==> H3
end
Phase1 ==> Phase2 ==> Phase3
Essential Wireshark Display Filters for Lab Debugging
| Display Filter Expression | What It Isolates |
|---|---|
dns |
All DNS queries and responses (verify A and PTR lookups). |
icmp |
All ping Echo Requests, Echo Replies, and ICMP Unreachable errors. |
ip.addr == 10.0.1.50 && ip.addr == 10.0.2.20 |
Only packets flowing between client01 (LAN) and dmz01 (DMZ). |
tcp.flags.syn == 1 && tcp.flags.ack == 0 |
Initial TCP [SYN] connection attempts (spot blocked firewall ports!). |
http.host == "app.yourlab.lan" |
HTTP requests specifically targeting the app.yourlab.lan virtual host. |
tcp.analysis.retransmission |
Highlights packet loss or MTU mismatches across virtual bridges. |
Hands-On Lab 2: Live Verification & DNS Cache Flush from client01
Run these commands on client01 (10.0.1.50) while watching Wireshark on your KVM host's virbr1 interface:
# 1. Flush systemd-resolved DNS cache (Modern Debian 12 / Ubuntu 24.04 command)
sudo resolvectl flush-caches
# 2. Test Forward & Reverse DNS against srv01 (10.0.1.10)
dig app.yourlab.lan +short
dig -x 10.0.2.20 +short
# 3. Test HTTP Virtual Host Routing across the pfSense Firewall into DMZ1
curl -i http://dmz01.yourlab.lan
curl -i http://app.yourlab.lan
10. Continue Your Linux Networking Journey
Explore the companion guides in the Networking series to deepen your command-line and routing mastery:
-
Networking Basics
Master IPv4/IPv6 addressing, subnet masks, gateways, DNS fundamentals, and essential CLI tools (
ip,ping,ss). -
Intermediate Configuration & Services
Configure static interfaces, DHCP, SSH hardening, firewall rules (
ufw/nftables), and network services. -
Advanced Virtualization & Routing
Dive into Linux kernel routing tables, VLAN tagging (
802.1Q), network namespaces, policy routing, and traffic control (tc). -
Container Network Isolation
See how Linux network namespaces and Netavark power zero-trust microservice segmentation in Rootless Podman.