Automated security monitoring, threat intelligence enrichment, and incident management, deployed as code.
A lightweight Security Operations Center pipeline built around Wazuh (SIEM), n8n (SOAR/workflow automation), and DFIR-IRIS (case management). Alerts are detected, enriched with threat intelligence, scored, and turned into structured incident cases automatically. All services run in Docker and are provisioned with Ansible.
Built during a summer 2026 engineering internship at EY Tunisia (Security Consulting).
Monitored host (Linux / Windows)
│
Wazuh Agent
│
Wazuh Manager ── detection rules, MITRE ATT&CK mapping
│ │
│ └── Active Response (firewall-drop on brute force)
▼
Custom integration script (custom-n8n)
│ HTTP POST (JSON)
▼
n8n webhook workflow
┌────────┼────────┐
VirusTotal AbuseIPDB OTX (queried in parallel)
└────────┼────────┘
▼
Merge → Score verdict
│
Worth a case? ── no ──▶ no action
│ yes
▼
DFIR-IRIS case (via API)
- Detection: Wazuh agents on Linux and Windows hosts, with built-in rules for SSH brute force, file integrity monitoring, and system logs.
- Custom integration: a Wazuh
<integration>script forwards alerts to an n8n webhook as structured JSON. - Threat intel enrichment: VirusTotal, AbuseIPDB, and AlienVault OTX are queried in parallel and merged into one verdict.
- Automated case creation: alerts above a score threshold become DFIR-IRIS cases with rule, severity, agent, source IP, and enrichment data filled in.
- Active Response: repeated failed logins (rule 2502, MITRE T1110) trigger
firewall-dropto block the source IP for 600 s. - Resilient workflow: alerts without a source IP take a fallback branch instead of failing the pipeline.
- Infrastructure as code: Docker Compose services, idempotent Ansible playbooks, and Ansible Vault for secrets.
| Role | Tool |
|---|---|
| SIEM / endpoint detection | Wazuh |
| SOAR / workflow automation | n8n |
| Incident management | DFIR-IRIS |
| Threat intelligence | VirusTotal, AbuseIPDB, AlienVault OTX |
| Containers | Docker, Docker Compose |
| Provisioning | Ansible (Terraform was evaluated, not used for deployment) |
| Version control | Git |
The target was a single 8 GB RAM VM, so the stack is deliberately light:
- IRIS instead of TheHive + Cortex: smaller footprint with comparable case management.
- n8n instead of Shuffle: Shuffle needs its own dedicated OpenSearch instance.
- No MISP / IntelOwl: both need a full self-hosted platform. Reputation lookups go through public APIs instead.
- Ansible instead of Terraform for services: the services run on a single VM, so VM provisioning added little.
soc-as-code/
├── ansible/
│ └── playbooks/
│ ├── deploy-iris.yml
│ ├── deploy-n8n.yml
│ └── vars/
│ └── n8n_secrets.vault.yml # encrypted with Ansible Vault
├── wazuh-docker/
│ └── single-node/
│ ├── docker-compose.yml
│ ├── custom-n8n # integration wrapper script
│ ├── custom/n8n.py # integration script (forwards alerts to n8n)
│ └── config/wazuh_cluster/
│ └── wazuh_manager.conf # integration + active-response config
└── iris-web/ # DFIR-IRIS deployment
- Ubuntu Server VM with at least 8 GB RAM
- Docker and Docker Compose
- Ansible
- API keys for VirusTotal, AbuseIPDB, and AlienVault OTX
git clone https://github.com/d0ok/soc-as-code.git
cd soc-as-code- Wazuh: from
wazuh-docker/single-node/, generate the indexer certificates with Wazuh's cert generator, thendocker compose up -d. - IRIS:
ansible-playbook ansible/playbooks/deploy-iris.yml - n8n: create your own vault file from the template, then run
ansible-playbook ansible/playbooks/deploy-n8n.yml --ask-vault-pass - n8n workflow: import the workflow, add your API keys as credentials, and publish it. Wazuh must call the Production webhook URL (
/webhook/...), not the Test URL. - Wazuh integration: set the n8n
hook_urlinwazuh_manager.conf, then recreate the manager container.
- Apply Wazuh config changes with
docker compose down && up. The config is bind-mounted and copied at container start, sorestartdoes not pick it up. - Integration scripts need mode
755. Wazuh runs them as a different UID than the host user, so750fails silently withCouldn't execute commandinossec.log. - Active Response needs
NET_ADMINandiptables. The manager service addscap_add: [NET_ADMIN, SYS_PTRACE], andiptablesis installed inside the container (dnf, Amazon Linux 2023 base). - Use a stable VM IP. The VM runs on VMware NAT networking. Bridged mode broke the Wazuh
hook_url, the n8n IRIS node URL, and the agent manager addresses on every network change. - Never suspend the VM. Suspend corrupts Docker's bridge networking. Shut it down cleanly.
| Path | Status |
|---|---|
| Wazuh → n8n webhook delivery | Verified |
| n8n → VirusTotal / AbuseIPDB / OTX | Verified |
| n8n → IRIS case creation (correct rule, level, verdict) | Verified |
| SSH brute-force detection in Wazuh (rule 2502, T1110) | Verified with a real attack |
| Active Response block following rule 2502 | Verified in live alert logs (rule 651 within ~1 s) |
| Single uninterrupted live attack through the entire chain to an IRIS case | Not demonstrated |
Components were validated individually and through controlled test events. A single continuous live attack traversing the whole pipeline in one run was not demonstrated.
- No secrets are committed.
.gitignoreexcludes.env, TLS certificates, and key material. - Ansible Vault encrypts the n8n secrets file.
- Redact API keys, tokens, and credentials from any screenshot before publishing.
- Single-node deployment, no high availability
- Three threat intel sources
- Basic threshold-based scoring with manual tuning
- No multi-tenant support
- Alert correlation engine
- More SOAR playbooks (account disable, endpoint isolation)
- Production hardening and HA
- Performance monitoring
- Additional threat intel integrations
- Automated resilience testing