Attack and defence flow: red team action, pfSense routing, Suricata/Wazuh detection, and the verdict on each row

PEAS SOC: Running Both Sides of the Fight — Attacking My Own Blue Team's Detection Matrix

Before our group’s live attack/defence rounds, I ran three untested rows of our Blue Team Playbook’s detection matrix solo — playing attacker and defender at once to find real detection gaps before they’d cost us marks.

6 November 2026 · 5 min
pfSense console with all nine zones assigned and addressed

TAFE SOC Network Group Project

The 9 zones behind the firewall pfSense sits at the centre as firewall and router. WAN, LAN, CORP (VLAN 10), Blue Team / SOC (VLAN 20), MGMT (VLAN 99), DMZ (VLAN 30), Red Team / Guest (VLAN 40), WiFi-CORP (VLAN 50), WiFi-Guest (VLAN 60). Red Team to Blue Team direct access is denied both ways and logged. Each box opens a panel below the map with what the zone does and what changed on it during the project. Remote access — Remote access runs through OpenVPN on pfSense with FreeRADIUS authentication against Active Directory, tunnelling clients onto a dedicated 10.200.0.0/24 network that reaches the SOC segment only, not CORP, not DMZ, with the option to layer TOTP or privacyIDEA for MFA. RADIUS group membership, not just a valid login, determines which internal segments a VPN client can actually reach. 🔐 Remote access OpenVPN · 10.200.0.0/24 reaches SOC only, not CORP or DMZ Detection — Two independent views of the same activity: Suricata IDS on the GUEST, DMZ and CORP segments, correlated against Wazuh on the SOC segment. 🛰 Detection Suricata on 3 zones · Wazuh two independent views of one round WAN — The firewall's uplink, taking its address by DHCP from the campus NetLab pod. WAN the uplink to the campus network vtnet0 · DHCP from NetLab POD pfSense — pfSense sits at the centre as firewall and router, segmenting nine zones: router-on-a-stick, with Proxmox's VLAN-aware bridge handling trunking. Explicit deny rules enforce isolation between zones that should never touch, and every block is logged. Segmentation was designed around aliases, not one-off rules, so the deny policy could be reasoned about and audited as a whole. 🧱 pfSense fw01 · router-on-a-stick routing · NAT · Suricata IDS/IPS OpenVPN · Squid proxy alias-based deny · every block logged 802.1Q trunk on vtnet1 SW-CORE (conceptual) — the Proxmox bridge vmbr1, VLAN-aware, no IP address; every VLAN is trunked through it. 🔀 SW-CORE (conceptual) the Proxmox bridge vmbr1 802.1Q · VLAN-aware · no IP Red Team to Blue Team direct access: denied both ways and logged. The rule the whole design hangs off. ⛔ Red ↔ Blue no direct traffic, either way: denied both ways and logged THE RULE LAN — The firewall's base internal interface on vtnet1, the 802.1Q trunk into the Proxmox bridge vmbr1, which is VLAN-aware and has no IP address. The seven VLANs from CORP to MGMT are tagged on it. LAN base interface · the 802.1Q trunk 192.168.1.1/24 · vtnet1 CORP — The corporate LAN: Active Directory, DNS and the certificate authority on DC01, plus domain workstations. CORP VLAN 10IDS internal users and directory services 10.10.10.0/24 DC01-AD-DS-CA·AD · DNS · CA PC-CORP2·domain workstation comms01·InspIRCd chat Blue Team / SOC — The Blue Team SOC segment, which receives logs, alerts and detection evidence. Red Team to Blue Team direct access is denied both ways and logged, and GUEST can never reach it. Blue Team / SOC VLAN 20BLUE blue team · detect and respond 10.20.20.0/24 · SOC soc01·Ubuntu VM SIEM-Wazuh·+ EDR agents SYSLOG-ng·log collection RADIUS-FreeRADIUS·AAA via AD MGMT — A locked-down management plane reserved purely for firewall administration during the build. GUEST can never reach it. MGMT VLAN 99LOCKED firewall administration only 10.99.99.0/24 Admin workstation·during build only DMZ — The public-facing DMZ, reachable only per firewall policy and published services, and where the targets of the exercise lived. DMZ VLAN 30IDS public-facing, and the target 172.16.30.0/24 dmz01·Debian VM WEB-Apache·+ Flask app DB-MariaDB·least privilege VM 100·Metasploitable target Red Team / Guest — Red Team operates from VLAN 40 in a controlled, lab-safe manner. Red Team to Blue Team direct access is denied both ways and logged, and GUEST can never reach SOC or MGMT. Red Team / Guest VLAN 40RED · IDS red team · lab-safe only 10.40.40.0/24 · GUEST KALI-RED·attack box WiFi-CORP — Corporate wifi, in the design as its own segment. A design placeholder, with no devices configured. WiFi-CORP VLAN 50 corporate wifi · placeholder 10.50.50.0/24 · WIFICORP WiFi-Guest — Guest wifi, in the design as its own segment. A design placeholder, with no devices configured. WiFi-Guest VLAN 60 guest wifi · placeholder 10.60.60.0/24 · WIFIGUEST 9 zones, 7 of them VLANs on one 802.1Q trunk · Red ↔ Blue: no direct traffic, denied both ways and logged · click any box for what it does and what changed Click a zone, the firewall, or either corner box. Its panel opens below with what it does, what runs there, and what changed during the project, with links to the pages that cover it. The project also has its own page, with more of the evidence, kept up to date. Click a box on the map. Each panel says what that part of the network does, what runs there, and what changed on it during the project. The URL updates, so a panel can be linked to. ...

4 November 2026 · 16 min
The GitHub page for the blog repository after the leak gate landed. The file tree shows scripts, tests/leak-gate and .github/workflows, with recent commit messages about enforcing the site-pattern count and pinning the runner image. A banner offers to open a pull request for the feat/leak-gate branch.

A clean result that means nothing

I now hand finished write-ups to a second Claude that owns this repository, instead of publishing them myself. It turns a draft into a post and keeps the standing pages honest. Before any of that could run, something had to stop a draft carrying a credential or a real address into a public repo. That is this post. The gate works. Getting there was mostly me rebuilding, in the gate itself, the exact failure the gate exists to catch. ...

4 October 2026 · 7 min

A network digital twin: does my homelab still look the way I think it does?

With the SOC built and an AI analyst sitting on top of it, the obvious gap was that both of them only knew about things that happened. Neither knew what my lab was supposed to look like in the first place. So this project builds a baseline — a structured representation of the homelab, physical and logical — and then continuously checks reality against it. If a port opens that I know nothing about, I want to find out because it disagreed with the inventory, not because I happened to look. ...

10 September 2026 · 9 min

Integrating Kimi as a read-only AI security analyst for my homelab SOC

With the new SOC structure and dashboard built, I decided to take on a bigger project: integrating Kimi as a security AI analyst. Its role covers reporting on alerts, examining logs, judging whether something is a false positive, and auditing continuously. It is built in phases, and each phase needs extensive validation before the next one starts. The problem The SOC we built catches things. Seven log sources into Loki, 36 detection rules, alerts routed to my phone by severity, one Grafana dashboard over all of it. ...

8 September 2026 · 10 min

A SOC for the homelab, built in the right order

My homelab keeps growing — more containers, more services, a multi-LLM setup on top. Security is the priority for all of it: guard rails, hooks, and strict policies everywhere, to the point that my main Claude agent is audited by Codex, with Kimi as a third read-only reviewer I invoke manually through the council dashboard. What it didn’t have was a place to see any of that. So: a SOC. A centralised security view for the lab, and a genuinely useful thing to have built. ...

6 September 2026 · 10 min
The council dashboard showing a run's pipeline, all stages passed through the approval gate

The Council Gets a Front Door

The council worked. One request went to Claude, Codex and Kimi, they answered independently, and nothing touched the lab without a human approval bound to a hash of the exact plan. It also lived entirely in a terminal, which meant that to approve a run I had to type a run id like hc-20260822-6fbfa5 and then re-type the original 350-character request verbatim, because resume rebuilds the context pack from it. Get one character wrong and you have quietly changed the inputs the agents were judged on. ...

23 August 2026 · 7 min
Pipeline diagram: one context pack fans out to Claude, Codex and Kimi in a blind first round, then a peer round, then a free model synthesises, then a human approval gate, then execute and verify

Three Agents, One Approval Gate — Making Claude, Codex and Kimi Review Each Other

I have three coding agents with access to this lab. Claude Code does most of the building. Codex reviews and runs the scans. Kimi audits. Until this week they had never exchanged a word — each got its own context, its own session, and its own chance to be confidently wrong with nobody checking. So I built a thing that makes them work one request together, and put a human approval gate in front of anything that changes the lab. ...

19 August 2026 · 11 min
Pipeline diagram: Codex scans 24 images and finds 2042 actionable findings, Claude Code fixes them one wave at a time behind a hard validation gate, then the same scanner measures again at 1497

A Vulnerability Scan From Codex, a Container Upgrade From Claude Code

I run two coding agents against this lab. Codex found the problem and Claude Code fixed it, and keeping those two jobs in different hands turned out to be the most useful part of the whole exercise. The short version: a full vulnerability scan reported 2042 actionable findings — 46 critical, 764 high — and 1751 of them were in container images. Ten waves of updates later, the same scanner reports 1497 actionable, 21 critical, 570 high. ...

18 August 2026 · 10 min
The agent auditor's local operator dashboard, showing observe-only mode and a list of recent Claude sessions with their recorded timelines

Auditing the AI Agent That Runs My Homelab

My homelab has picked up a lot of moving parts this year, and several of them are now LLMs. Claude Code runs natively on the box and does real work against real infrastructure — containers, reverse proxy config, monitoring, the lot. That’s genuinely useful. It also creates a trust problem I hadn’t had before. The problem is simple to state: the same agent that makes a change also writes the summary explaining why the change was safe. That’s convenient. It is not independent verification. If the agent quietly skips a validator and then reports “config validated”, I have no signal at all. The report is the evidence, and the thing that wrote the report is the thing being checked. ...

7 August 2026 · 9 min
The finished Release Watch workflow in n8n: schedule, state read, repo list, feed fetch, XML parse, decide, state write, and two ntfy branches

Patching a Homelab Has Two Halves — and Only One of Them Is apt

Two halves apt patches Debian. My homelab is not Debian — it’s about a dozen containers sitting on top of Debian. AdGuard, Authentik, Vaultwarden, Caddy, n8n, Greenbone, Homepage. apt will never once mention that Vaultwarden cut a release. So “keep the lab patched” is really two jobs: Debian packages — unattended-upgrades, already a solved problem, just needs scheduling and a report. Everything I actually run — nobody’s solved that for me. Containers only update when I decide to pull. This post is both halves. The first one taught me something uncomfortable about monitoring, so I’ll start there. ...

1 August 2026 · 13 min
Two rows: DNS resolution from host rig and phone through AdGuard to an upstream DoH resolver, and the admin UI reached only through Caddy and Authentik SSO

One DNS Filter, Three Clients That All Bypass It Differently

I wanted a DNS sinkhole in the lab — block ads, trackers, and adult content at the resolver instead of per-device. AdGuard Home is a single Go binary, so standing it up was maybe ten minutes. Then I spent the rest of the session learning that “point a device at the resolver” is a lie three times over. A Windows PC, an iPhone, and the lab host each route DNS somewhere I didn’t tell them to, for three completely different reasons. This is the write-up of finding all three. ...

1 August 2026 · 7 min
Before/after diagram: Tailscale reaching one machine, versus a subnet router advertising 10.10.0.0/24 so the phone reaches the whole lab

Two VPNs, One Phone: Consolidating Remote Access

I ended up with two VPNs into my homelab. WireGuard, running on my firewall, which I set up first. And Tailscale on my main server, which I added later so I could SSH in from a laptop without opening ports. Then I tried to use both from my phone and hit a wall. Phones only run one VPN at a time This isn’t a bug or a conflict between the two apps. iOS and Android both allow exactly one active VPN tunnel, full stop. It’s an OS-level restriction. ...

24 July 2026 · 10 min
OpenVAS results page showing the first scan's findings by severity

Running OpenVAS in My Home Lab — Install, the Three Things That Broke, and the First Scan

I wanted a real vulnerability scanner in the lab — something that port-scans a host, throws a big pile of network vulnerability tests at it, and tells me what to fix by severity. OpenVAS (now Greenbone Community Edition) is the obvious choice, and it ships as a Docker stack. This is the honest write-up: the install, the three separate things that broke, one of which was my own fault, and what the scanner actually found when I pointed it at my own box. Nothing here is polished — I got a couple of things wrong along the way and I’m leaving those in, because that’s where the useful bits are. ...

22 July 2026 · 11 min
RAINBOW MATRIX cover — the post title in rainbow monospace over falling matrix rain on black

I Hardened This VM by Hand. Lynis Scored It 68/100.

A while back I hardened this Debian VM by hand — patched it, cut the attack surface, put a default-deny host firewall in front of it, sandboxed the custom services. I audited my own box and fixed the drift I found. The problem with auditing your own box is that you grade your own homework. You check the things you already think to check. So this time I handed the job to something that has no idea what I meant to do and only reports what’s actually there: Lynis, the system-auditing tool from CISOfy. It ran 274 tests and gave the VM a hardening index of 68 / 100. ...

20 July 2026 · 7 min
Grafana Explore showing pfSense firewall logs flowing through Loki, with a log-volume chart above

Building a Homelab SIEM with Loki — Reading My Logs Instead of Hoping

I had dashboards showing me numbers — CPU, memory, network throughput. What I didn’t have was any way to answer “what actually happened?” When did someone try to log in? What did the firewall block? Those answers live in logs, and my logs were scattered across machines, unread. This post is how I pulled them into one searchable place. That’s a SIEM — Security Information and Event Management — and you can build a simple one at home. ...

19 July 2026 · 7 min
Logged into pfSense as an identity-provider account over LDAP

Making the Firewall Authenticate Against My Identity Provider — and Proving the Bug Wasn't Mine

Bringing pfSense’s admin login into Authentik with MFA — via RADIUS, which turned out to be broken upstream, and then via LDAP, which wasn’t. This is as much about how you prove a bug isn’t yours as it is about the build. Stack: Authentik · pfSense · Docker · LDAP · RADIUS 1. Purpose With an identity provider already running and Grafana logging in through it, the obvious next question was: how far does this go? ...

16 July 2026 · 13 min
Grafana's login page with a Sign in with authentik button

One Login, MFA Everywhere: Adding an Identity Provider to the Homelab

Deploying Authentik as a self-hosted identity provider, and wiring Grafana into it with OIDC — so a service that has never heard of MFA suddenly requires it. Stack: Authentik · Caddy · Grafana · Docker · Debian 1. Purpose Every self-hosted service arrives with its own login. Ten services means ten accounts, ten passwords, and ten places to forget to enable MFA. Worse, some services have no authentication at all and just quietly assume nobody hostile is on your network. ...

15 July 2026 · 9 min
The self-hosted Vaultwarden vault, logged in and showing its security reports

Self-Hosting a Password Manager the Hard Way: Vaultwarden, Caddy, and Three Firewalls Fighting

Building a self-hosted password vault with a genuinely trusted TLS certificate and zero inbound ports open to the internet — and the four-layer debugging session that stood between me and a working container. Stack: Debian 13 · Docker · Caddy (custom build) · Vaultwarden · pfSense · WireGuard · DuckDNS 1. Purpose Self-hosted services are easy to do badly. It’s trivial to run a container, forward a port, click through a certificate warning, and call it done — and end up with something less secure than the cloud service you replaced. ...

15 July 2026 · 11 min
Before/after: GitHub repo → Cloudflare Pages → billalrehmani.pages.dev, versus the same build now served at the registered domain billsblog.dev

Registering My Own Domain: From a Pages Subdomain to billsblog.dev

The blog you’re reading just moved house. Same content, same build, the same Cloudflare Pages underneath — but the address on the door changed from billalrehmani.pages.dev to a domain that’s actually mine: billsblog.dev. Why bother — it already worked billalrehmani.pages.dev was fine. It served over HTTPS, it was fast, it was free. But pages.dev is Cloudflare’s namespace, not mine. For a portfolio I link from LinkedIn and want people to remember, “billalrehmani-dot-pages-dot-dev” is a mouthful and a borrowed address. Owning the domain means owning the identity — and, as it turned out, a bit more control over the security posture too. ...

13 July 2026 · 5 min
Before/after diagram: GitHub repo → GitHub Actions → github.io, versus GitHub repo → Cloudflare Pages → pages.dev with security headers

Moving the Blog off GitHub Pages — for Real Security Headers

The blog you’re reading changed builders. The git push that publishes it is identical; what runs on the other end isn’t. It used to be a GitHub Actions job that built the site and served it from yourlocalunemployed.github.io. Now Cloudflare Pages builds from the same repo and serves it at billalrehmani.pages.dev. I didn’t move for speed or for a nicer dashboard. I moved because GitHub Pages won’t let me set HTTP response headers, and for a blog that’s meant to be a security portfolio, that was the one limit I couldn’t design around. ...

12 July 2026 · 5 min
All three VLAN interfaces online at their gateway IPs on pfSense

Virtual VLAN Segmentation on pfSense — Three Isolated Zones, No Managed Switch

A flat network trusts every device on it equally. A smart plug, a guest’s phone, and the machine holding my important data all share one space — so if any one of them is compromised, the attacker can reach the rest. Network segmentation breaks that flat space into separate zones and controls what may cross between them, shrinking the blast radius of any single compromise. I wanted to build that properly — VLANs and a firewall — on VMware Workstation with pfSense CE, and design three zones at deliberately different trust levels: ...

12 July 2026 · 8 min
WireGuard handshake established over mobile data

Self-Hosted WireGuard Through a Nested Firewall — and the Four-Layer Debug to Make It Work

My lab is deliberately isolated — an automation VM (CLAUDDEB) sits behind a virtual pfSense firewall on a segment (10.10.0.0/24) that my home network can’t reach. That isolation is great until you’re out of the house and want to check your Grafana dashboards, which only listen inside that segment. I already use Tailscale for casual remote access, and I’ll be honest up front: for pure convenience, Tailscale wins — it punches through NAT automatically with zero firewall work. But this project wasn’t about convenience. It was about building the thing Tailscale is made of. Tailscale is WireGuard under the hood; hand-rolling raw WireGuard on pfSense teaches you how VPNs actually work — keys, peers, routing, firewall rules, NAT — at a level the managed tool deliberately hides. So I built it from scratch, kept Tailscale as my daily driver, and got a genuinely brutal debugging lesson in the process. ...

11 July 2026 · 7 min
Terminal summary of the VM hardening result

Hardening My Debian Home-Lab VM — Even Behind pfSense

My Debian automation VM already sits behind a pfSense firewall with egress containment — it can reach the internet but not my home network. So why harden the VM itself? Because “behind a firewall” is doing less work than it sounds. Two paths reach into the VM without ever crossing pfSense, and an honest audit of my own box turned up drift I didn’t expect. This is the write-up: what the audit found, what I changed, and the systemd sandbox mistake that quietly broke a service. ...

7 July 2026 · 6 min
pfSense dashboard after the rebuild

Debugging a Dead VMware NAT and Hardening My pfSense Containment Lab

I run my Claude Code work inside a Debian 13 VM (CLAUDDEB) on VMware Workstation Pro 17.6.4, with a pfSense 2.8.1 VM in front of it as a virtual router and firewall. pfSense exists in this setup for containment: if something on the Debian VM misbehaves — a prompt injection, a compromised dependency — it must not be able to reach my PC, my router’s admin page, or anything else on the home network. ...

2 July 2026 · 7 min
Arcadyan HWG2025 router

Hardening and Segmenting My Home Network on an Arcadyan HWG2025

This is a small home network — one router, a handful of devices. The point wasn’t complexity; it was applying the same discipline you would to a small office or lab environment. Treated that way, it doubles as practical study for Network+ and Security+. The router is an Arcadyan HWG2025 — the NBN-issued unit, Wi-Fi 7 with MLO, around 500 Mb down. An ISP router doesn’t give you much room to move, but it gives you enough to do this properly. ...

29 June 2026 · 3 min