A containerized Security Operations Center (SOC) lab simulating a real-world brute-force attack and its detection, built entirely with Docker. This project demonstrates the full attack-to-detection lifecycle: an attacker container brute-forcing a target web server, an IDS inspecting and alerting on the traffic, and a SIEM centralizing and visualizing the evidence.
Built as the final capstone project for the Cybersecurity Certification from Netligent Technologies.
+------------+ +--------------------------------+ +-----------------------+
| Kali | Hydra | nginx-suricata | eve.json | Splunk Universal |
| (Attacker) |------->| Nginx (Basic Auth login) |---------> | Forwarder |
+------------+ | Suricata (IDS, eth0) |access.log +-----------+-----------+
+-------------------------------+ |
all connected via v
doc-prac-net (Docker bridge) +------------------+
| Splunk |
| (index: soc_lab) |
+------------------+
Flow: Kali runs a Hydra HTTP brute-force attack against Nginx → Nginx logs every request (success/fail) → Suricata inspects the same traffic on its container interface and fires a custom detection rule when it sees the burst pattern → both log sources are forwarded by the Splunk Universal Forwarder into a centralized, searchable Splunk index.
| Container | Base Image | Role |
|---|---|---|
nginx-suricata |
ubuntu:22.04 (custom Dockerfile) |
Target web server (Nginx + HTTP Basic Auth) and host-based IDS (Suricata) |
kali |
kalilinux/kali-rolling |
Attacker — runs Hydra brute-force |
splunk |
splunk/splunk:latest |
Central SIEM — indexing, search, dashboards |
splunk-forwarder |
splunk/universalforwarder:latest |
Ships Nginx and Suricata logs into Splunk |
All containers communicate over a user-defined Docker bridge network, doc-prac-net.
- Multi-container security lab design using Docker networking and bind mounts
- Configuring an IDS (Suricata) to monitor live HTTP traffic and parse application-layer data
- Writing a custom Suricata detection rule (threshold-based) to catch brute-force patterns rather than relying only on default signatures
- Forwarding and centralizing logs from multiple sources into a SIEM (Splunk) using the Universal Forwarder
- Building basic SIEM dashboards to visualize attack evidence
- Docker installed
- ~4GB free RAM (Splunk is resource-heavy)
git clone https://github.com/eli-cipher/soc-lab-docker.git
cd soc-lab-dockerdocker network create doc-prac-netdocker build -t nginx-suricata-image ./containers/nginx-suricata
mkdir -p bi-direc suricata-logs
docker run -d \
--network doc-prac-net \
-p 80:80 \
-v $(pwd)/bi-direc:/var/log/nginx/ \
-v $(pwd)/suricata-logs:/var/log/suricata/ \
--name nginx-suricata \
nginx-suricata-imagedocker run -dit --network doc-prac-net --name kali kalilinux/kali-rolling bash
docker exec -it kali bash -c "apt update && apt install -y hydra curl"docker run -d \
--network doc-prac-net \
-p 8000:8000 -p 9997:9997 \
-e SPLUNK_GENERAL_TERMS=--accept-sgt-current-at-splunk-com \
-e SPLUNK_START_ARGS=--accept-license \
-e SPLUNK_PASSWORD=Admin123! \
--name splunk splunk/splunk:latest
docker run -d \
--network doc-prac-net \
-e SPLUNK_START_ARGS=--accept-license \
-e SPLUNK_GENERAL_TERMS=--accept-sgt-current-at-splunk-com \
-e SPLUNK_PASSWORD=Admin123! \
-e SPLUNK_STANDALONE_URL=splunk \
-v $(pwd)/bi-direc:/var/log/nginx-monitor \
-v $(pwd)/suricata-logs:/var/log/suricata-monitor \
--name splunk-forwarder splunk/universalforwarder:latestConfigure monitors (once the forwarder is up):
docker exec -it splunk-forwarder bash
/opt/splunkforwarder/bin/splunk add monitor /var/log/nginx-monitor -index soc_lab -sourcetype nginx-monitor -auth admin:Admin123!
/opt/splunkforwarder/bin/splunk add monitor /var/log/suricata-monitor -index soc_lab -sourcetype suricata-eve -auth admin:Admin123!(Create the soc_lab index first via Splunk web UI: Settings → Indexes → New Index.)
docker exec -it kali bash
hydra -l admin -P /tmp/wordlist.txt http-get://nginx-suricata/1. Nginx access log captures every request — repeated 401 Unauthorized responses from the attacker's IP, followed by a 200 OK on the successful guess:
2. Suricata parses the traffic at the application layer, logging each HTTP request with method, status, and user-agent (Mozilla/4.0 (Hydra) — identifying the tool itself):
3. A custom Suricata rule (custom.rules) fires an active alert when it detects 5+ requests from the same source within 10 seconds:
alert http any any -> any 80 (msg:"Possible HTTP Brute Force Attack Detected"; flow:to_server,established; http.method; content:"GET"; threshold: type threshold, track by_src, count 5, seconds 10; classtype:attempted-recon; sid:1000001; rev:1;)
4. Splunk centralizes both log sources, making the attack fully searchable and correlated in one place:
- Suricata deployment scope: Suricata runs inside the
nginx-suricatacontainer, monitoring only that container's own interface (eth0). This is effectively a host-based deployment of a network-based tool — it sees traffic to/from this one container, not full network-wide traffic acrossdoc-prac-net. A true network-wide NIDS setup would require a span/mirror port or a dedicated sensor with tap access. - Detection is custom, not default: Suricata's out-of-the-box ruleset does not include a signature for this kind of application-layer brute-force. The alert relies on a custom threshold-based rule, which is a simplified version of real detection engineering logic (rate-based anomaly detection).
- Log ingestion, not built-in Splunk apps: logs are shipped as raw text via the Universal Forwarder monitor, not parsed with a dedicated Splunk technology add-on (e.g., a Suricata TA). Field extraction (
src_ip,alert.signature, etc.) works because Suricata emits JSON, which Splunk auto-extracts.
- Map detected alert to MITRE ATT&CK (T1110 — Brute Force)
- Add a Suricata rule for a second attack vector (e.g., directory brute-forcing)
- Configure Splunk alerting (trigger an email/webhook on new brute-force alert)
- Move from IDS (alert-only) to IPS (active block) mode for Suricata
Eli Cipher (@elicipher_) — LinkedIn · GitHub



