Também disponível em português: README.pt-BR.md
A small Security Operations Center built from scratch on a dedicated VMware ESXi host. The lab pulls firewall, network, identity, and endpoint telemetry into one monitoring workflow. I wrote down every decision, limitation, and test result as the work happened.
Current stage: Chapter 3 complete. The lab detects, responds, and reports. Milestone-level status lives in the Roadmap.
I've spent my career in infrastructure and cloud operations, part of it in a NOC. Networking, system monitoring, virtualization, and troubleshooting are my day-to-day work. This lab points that experience at security operations: I build the monitoring pipeline myself, then investigate what comes out of it.
It's my first structured SOC project, so I kept the scope small on purpose. Nothing new gets added until I understand, test, and document what is already running.
The core workflow, end to end:
- Kali Linux generates controlled test activity from an isolated network.
- FortiGate routes, filters, and logs the traffic between the Attack and SOC networks.
- Suricata watches the monitored segment without touching it, while the endpoints produce system and identity telemetry.
- Wazuh centralizes everything. I run detection, investigation, and response there.
- High-severity alerts trigger a reversible block on the attacked host and a notification to a private channel.
- I close each test scenario with reviewed evidence and a written investigation report.
Four scenarios validated the workflow as it grew. Chapter 1 covered network discovery from Kali (UC-01) and authentication failures against Active Directory (UC-02). Chapter 2 added a PowerShell download cradle on the Windows endpoint (UC-03). Chapter 3 closed with the full detect-block-reverse-notify cycle (UC-04). Each chapter's boundaries and acceptance criteria live in its scope document: Chapter 1, Chapter 2, Chapter 3.
Skills exercised across the lab, each backed by a document with evidence:
- Network segmentation and firewall policy design under a hard constraint. The evaluation license allows exactly three policies, and I spend all three on purpose.
- SIEM deployment and telemetry pipelines: agent enrollment, syslog ingestion, and file-based collection converging on a single Wazuh node.
- Passive network monitoring: IDS placement, capture interface design, and the virtual switch settings that make sniffing possible.
- Detection engineering: I profiled the endpoint before writing a single rule, built custom Wazuh rules against controlled attacks, tuned them against the false positives they raised, and mapped them to MITRE ATT&CK.
- Automated response: alert enrichment that separates known hosts from unknown ones, a reversible host block triggered by the enriched alert, notifications out of the SIEM, and the operational risks written down before I armed the automation.
- Evidence-based validation: I close every milestone with a controlled test, expected-versus-observed results, and reviewed screenshots.
- Virtualization and infrastructure operations: ESXi networking, resource allocation, and the trade-offs of running a lab on one physical box.
The lab runs on a dedicated VMware ESXi 6.7 host: a 14-core Intel Xeon E5-2680 v4, 64 GB of RAM, an SSD, and a single physical network interface.
Only the external virtual switch has a physical uplink. The SOC and Attack networks are internal to the host, and the FortiGate is the only routed path between them.
Every path in the diagram is deployed and validated. Solid arrows carry telemetry into Wazuh; dashed arrows show the response and notification I added in Chapter 3.
flowchart LR
PHYSICAL["Physical Network and Internet"]
DISCORD["Discord<br/>private channel"]
subgraph ESXI["Dedicated VMware ESXi 6.7 Host"]
WAN["WAN Soc<br/>vSwitch0 with uplink"]
FG["FortiGate 7.4.12"]
SOCNET["SOC Network<br/>vSwitch-Lab"]
ATTACKNET["Attack Network<br/>vSwitch-Attack"]
KALI["Kali Linux"]
WAZUH["Wazuh All-in-One"]
SURICATA["Suricata IDS"]
AD["Active Directory"]
WIN10["Windows 10"]
DEBIAN["Debian Desktop"]
WAN -->|"port1"| FG
KALI --- ATTACKNET
ATTACKNET -->|"port3"| FG
FG -->|"port2"| SOCNET
SOCNET --- WAZUH
SOCNET --- SURICATA
SOCNET --- AD
SOCNET --- WIN10
SOCNET --- DEBIAN
end
PHYSICAL --> WAN
FG -->|"Syslog"| WAZUH
AD -->|"Wazuh Agent"| WAZUH
WIN10 -->|"Wazuh Agent"| WAZUH
DEBIAN -->|"Wazuh Agent"| WAZUH
SURICATA -->|"eve.json via Wazuh Agent"| WAZUH
WAZUH -.->|"Active Response<br/>firewall block"| AD
WAZUH -.->|"Alerts level 12+"| DISCORD
| Technology | Role in the lab |
|---|---|
| VMware ESXi 6.7 | Dedicated hypervisor. Keeps the internal networks isolated at the virtual switch level. |
| FortiGate 7.4.12 (Evaluation) | Routing, segmentation, and policy enforcement between the networks. The evaluation license caps the lab at three firewall policies, a real constraint the design works around. |
| Wazuh all-in-one (Ubuntu Server 24.04) | The central SIEM. Receives agent, syslog, and Suricata telemetry, and is the single place where I analyze events. |
| Suricata (Ubuntu Server 24.04) | Passive network IDS on the monitored segment, exporting structured events through eve.json. |
| Sysmon (Windows 10) | Process-level endpoint telemetry: command lines, hashes, and parent lineage feeding the detection work. |
| Active Directory + Windows 10 | Identity, authentication, and Windows endpoint telemetry. |
| Debian Desktop | Linux endpoint visibility. |
| Kali Linux | Source of controlled test traffic, kept inside the isolated Attack Network. |
| Discord (webhook) | Notification channel for high-severity alerts, reached by a custom integration on the manager. |
A few choices carry most of the lab's reasoning:
- The Attack-to-SOC policy allows everything, on purpose. In a detection lab, attack traffic has to cross the boundary and be seen rather than stop at the edge. I disabled NAT on that path so every log records the real source address. The segmentation baseline covers the trade-off.
- Three firewall policies is all the license gives, and I spend all three. The same baseline records which paths earned a policy slot and which fall back on the implicit deny.
- The IDS captures on an interface with no IP address. A promiscuous port group and a dedicated listen-only interface separate capture from management, which is the closest a virtual lab gets to a SPAN port. The sensor validation has the details.
- Kali carries no agent, on purpose. Its footprint in the SIEM is exactly what the defensive stack observes and nothing more. The agent onboarding explains the reasoning.
Each milestone produces one document, written as the work happens:
| Document | What it covers | Milestone |
|---|---|---|
| Chapter 1 Scope | Boundaries, telemetry sources, validation scenarios, and acceptance criteria: the chapter's contract | C1-01 |
| Infrastructure Baseline | ESXi host, virtual networking, VM inventory, IP plan, and traffic paths | C1-02 |
| FortiGate Segmentation Baseline | Interfaces, firewall policies, expected traffic matrix, and the allow/deny tests that prove it | C1-03 |
| Wazuh Agent Onboarding | Manager deployment, agent naming convention, enrollment, and verification | C1-04 |
| FortiGate Telemetry Integration | The syslog pipeline and the controlled event that validates decoding end to end | C1-05 |
| Suricata Sensor Validation | Capture design, sensor configuration, and the passive capture proof | C1-06 |
| Suricata and Wazuh Integration | Collecting eve.json through the host agent and the controlled alert that validates it |
C1-07 |
| UC-01: Network Discovery | Investigation report: a controlled scan from Kali traced across FortiGate, Suricata, and Wazuh | C1-08 |
| UC-02: Authentication Failures | Investigation report: a controlled SMB brute force against Active Directory, from Windows event to Wazuh alert | C1-09 |
| Chapter 1 Closure | Success-criteria review, consolidated versions and limitations, and lessons learned | C1-10 |
| Chapter 2 Scope | What detection engineering must deliver, the workflow it validates, and what stays out | C2-01 |
| Sysmon Deployment | Process-level telemetry on the Windows endpoint: configuration choice, collection, and the alerting gap it exposed | C2-02 |
| Sysmon Telemetry Baseline | The endpoint with nothing wrong: event distribution, a noisy updater, and an injection indicator investigated | C2-03 |
| UC-03: Suspicious PowerShell Execution | Investigation report: a controlled PowerShell download cradle, captured in Sysmon and traced to Wazuh, and the alerting gap it exposes | C2-04 |
| Custom Detection Rule | The first custom Wazuh rule: a PowerShell download-cradle detection, the rebuild on Script Block Logging, and the validation | C2-05 |
| Rule Tuning | Running the rule against normal PowerShell, the false positive it exposed, and the tuning that keeps the cradle while dropping the noise | C2-06 |
| Chapter 2 Closure | Success-criteria review, MITRE ATT&CK mapping and its gaps, consolidated versions and limitations, and lessons learned | C2-07, C2-08 |
| Chapter 3 Scope | What response and automation must deliver, how the two detection chains split roles, and what stays out | C3-01 |
| CDB List Enrichment | A known-hosts list and a rule that weights authentication failures by source, elevating unknown origins and leaving known ones alone | C3-02 |
| Active Response | A netsh block on the domain controller that fires on the enriched rule, contains a repeat attempt, and lifts itself on a timeout |
C3-03 |
| Discord Notifications | A custom integration forwarding high-severity alerts to a Discord channel, and the TLS and Cloudflare fixes it took to work | C3-04 |
| Response Safeguards | The operational risks automated response carries and the guardrails against them, written before the full-cycle test | C3-05 |
| UC-04: Full Response Cycle | Investigation report: the UC-02 brute force re-run against the hardened pipeline, traced from alert to block to reversal to notification | C3-06 |
| Chapter 3 Closure | Success-criteria review, ATT&CK coverage and its edges, consolidated versions and limitations, and lessons learned | C3-07 |
| Project Roadmap | Milestones, current focus, and status: the only place status lives | — |
.
├── README.md ← you are here
├── README.pt-BR.md ← Portuguese version
├── ROADMAP.md ← milestone status
├── LICENSE ← MIT
├── docs/
│ ├── NN-*.md ← one document per milestone (see the table above)
│ └── img/ ← sanitized evidence, one folder per document
├── detection/
│ ├── local_rules.xml ← custom Wazuh rules (100100 cradle, 100110 enrichment)
│ └── known-hosts ← CDB list of known lab hosts
├── integrations/
│ ├── custom-discord ← Wazuh → Discord notification wrapper
│ └── custom-discord.py
└── investigations/ ← STAR investigation reports, one folder per scenario
├── UC-01/ ← report.md + evidence/
├── UC-02/
├── UC-03/
└── UC-04/
Chapters 1 and 2 built the seeing half.
Chapter 1 delivered the telemetry pipeline and proved it worked: every endpoint reporting to Wazuh as an active agent, FortiGate logs arriving by syslog, and Suricata's eve.json reaching the SIEM through the host agent. It closed with two investigations traced from the attacker's action to the SIEM, network discovery in UC-01 and a credential brute force in UC-02.
Chapter 2 turned that visibility into detection. Sysmon went onto the Windows endpoint (deployment, baseline), a controlled PowerShell cradle showed up in UC-03, and a custom rule now alerts on it. I built the rule, tuned it against false positives, and mapped it to ATT&CK.
Chapter 3 built the acting half.
Enrichment came first. A known-hosts list and a rule that weights authentication failures by source (C3-02) raise a brute force from an unknown address to a high-severity alert, while an ordinary failed login from a domain host stays where it is.
Response followed: a small, reversible Wazuh Active Response (C3-03) that fires on that elevated alert, blocks the source on the domain controller, contains a repeat attempt, and lifts the block on a timeout. A custom integration (C3-04) then carries every high-severity alert to a Discord channel.
I wrote down the response safeguards and operational risks (C3-05) before validating any of it. UC-04 put the whole chain to the test, and the Chapter 3 Closure reviews the criteria, versions, limitations, and lessons.
Three chapters took the lab from an empty hypervisor to a pipeline that sees an attack, alerts on it, contains it, and reports it. The project ends there, and you should know its edges: the response covers one technique on one host, containment reaches no further than the domain controller's own firewall, and I maintain the known-hosts list by hand.
Each edge points at a next step: edge blocking through the FortiGate API, a second responding agent, a list generated from inventory, and notification aggregation that survives a burst. The per-chapter closures (1, 2, 3) record the limitations behind each of them in detail.
Every attack simulation targets systems the lab owns, inside the isolated environment. I expose nothing to the internet and run no real malware. Two connections do leave the lab, and neither is an attack: the sensor and endpoint validations fetch a benign page from testmynids.org, a public service built for that purpose, and the Chapter 3 notifications post to a private Discord channel through a webhook. Before I publish a screenshot, configuration, event, or log sample, I review it and strip credentials, personal data, and infrastructure details that don't need to be public.