# FoodProject SOC Lab

This repository runs a combined SOC lab with:

- `wazuh-docker` (single-node) — SIEM, log ingestion, rule engine
- `iris-web` — case and alert management (DFIR-IRIS)
- `Shuffle` — SOAR workflow automation
- `pagerduty-stub` — mock PagerDuty escalation endpoint
- `soc-integrator` (FastAPI) — KPI enrichment, IOC analysis, orchestration
- `flask-openapi-shuffle` — OpenAPI demo for Shuffle integration

All services are connected through a shared Docker network (`soc_shared`).

## Prerequisites

- Docker + Docker Compose plugin
- Bash
- Python 3 (for test/seed scripts)

## Quick Start

Start all services (detached):

```bash
./run-combined-stack.sh up --all -d
```

Start all and stream logs:

```bash
./run-combined-stack.sh up --all
```

Stop all:

```bash
./run-combined-stack.sh down --all
```

Status overview:

```bash
./run-combined-stack.sh status
```

## Stack Management

```bash
./run-combined-stack.sh <command> [target] [options]
```

| Command | Description |
|---|---|
| `up [target] [-d]` | Start services (default: all, detached) |
| `down <target>` | Stop services (target required) |
| `recreate [target]` | Force-recreate containers (picks up bind-mount changes) |
| `logs [target] [-f]` | View logs |
| `status` | Show container and endpoint status |
| `dedup` | Remove duplicate OpenSearch index patterns in Wazuh dashboard |
| `cleanup [--with-volumes]` | Prune stopped containers, unused images, builder cache |
| `help` | Show full usage |

Targets: `all` / `--all`, `wazuh`, `iris`, `shuffle`, `pagerduty`, `integrator`, `flask-openapi-shuffle`

Examples:

```bash
./run-combined-stack.sh up iris -d
./run-combined-stack.sh recreate wazuh
./run-combined-stack.sh recreate --all
./run-combined-stack.sh down shuffle
./run-combined-stack.sh logs integrator -f
./run-combined-stack.sh dedup
./run-combined-stack.sh cleanup --with-volumes
```

> **macOS bind mount note**: After editing config files on the host, run `recreate` to ensure the
> container picks up the new inode. The `Edit`/`Write` tools create new inodes on macOS which Docker
> does not automatically detect.

## Service URLs

| Service | URL |
|---|---|
| Wazuh Dashboard | `https://localhost` |
| Wazuh API | `https://localhost:55000` |
| IRIS-web | `https://localhost:8443` |
| IRIS KPI Dashboard | `https://localhost:8443/kpi-dashboard` |
| Shuffle UI | `http://localhost:3001` |
| PagerDuty Stub | `http://localhost:18080` |
| SOC Integrator API | `http://localhost:8088` |
| SOC Integrator Swagger | `http://localhost:8088/docs` |

## SOC Integrator

Key env file: `soc-integrator/.env`

### IRIS KPI endpoints

These endpoints fetch IRIS data and enrich each record with live SLA/KPI metrics:

```
GET  /iris/alerts                 List alerts with KPI
GET  /iris/alerts/{id}            Single alert with KPI
POST /iris/alerts/{id}/assign     Assign alert
GET  /iris/alerts/export-csv      Export alerts as CSV
GET  /iris/cases                  List cases with KPI
GET  /iris/cases/{id}             Single case with KPI
GET  /iris/cases/export-csv       Export cases as CSV
```

KPI is computed per alert/case:

```
elapsed_pct = (now − created_at) / sla_seconds × 100
kpi_pct     = 100 − elapsed_pct  (clamped 0–100)

SLA by severity:   High → 4 h   Medium → 8 h   Low → 24 h

Status thresholds: On Track ≥ 80 | Watch ≥ 60 | Warning ≥ 40 | Urgent ≥ 20 | Critical > 0 | Breached
Resolved alerts:   elapsed frozen at resolution time, status = "Resolved"
```

### MVP orchestration endpoints

```
POST /mvp/incidents/ingest
POST /mvp/ioc/evaluate
POST /mvp/vpn/evaluate
GET  /mvp/config/policies
PUT  /mvp/config/policies
GET  /mvp/health/dependencies
```

Protected endpoints require header: `X-Internal-API-Key`
Key from: `SOC_INTEGRATOR_INTERNAL_KEY` in `soc-integrator/.env`

### Other endpoints

```
GET  /health
GET  /wazuh/alerts
GET  /wazuh/agents
POST /wazuh/sync-to-mvp
GET  /wazuh/auto-sync/status
POST /ingest/wazuh-alert
GET  /ioc/enrich
POST /ioc/evaluate
GET  /geoip/{ip}
POST /action/create-incident
POST /action/create-iris-case
POST /action/trigger-shuffle
GET/POST /shuffle/workflows
GET  /sim/logs/runs
POST /sim/logs/start
```

### Example: MVP ingest

```bash
curl -X POST http://localhost:8088/mvp/incidents/ingest \
  -H 'Content-Type: application/json' \
  -H 'X-Internal-API-Key: dev-internal-key' \
  -d '{
    "source":"manual",
    "event_type":"ioc_ips",
    "event_id":"evt-1",
    "timestamp":"2026-02-12T16:00:00Z",
    "severity":"high",
    "title":"Test IOC",
    "description":"MVP test",
    "asset":{"hostname":"labhost","user":"analyst"},
    "network":{"src_ip":"203.0.113.10","country":"US"},
    "tags":["mvp","test"],
    "risk_context":{"admin_account":true},
    "raw":{},
    "payload":{}
  }'
```

## Sending Test Events to Wazuh

### Appendix A/B/C simulation logs

Replay production-style sample logs via syslog UDP 514:

```bash
scripts/send-wazuh-sim-logs.sh all 1 0.2
scripts/send-wazuh-sim-logs.sh a2 1 0
scripts/send-wazuh-sim-logs.sh B3-06 1 0
scripts/send-wazuh-sim-logs.sh all 1 0 --dry-run
```

See `scripts/README.md` for full selector/flag reference.

### FortiGate firewall syslog test

Send FortiGate-style syslog messages directly to Wazuh port 514/UDP:

```bash
python3 scripts/test-firewall-syslog.py --via-docker
python3 scripts/test-firewall-syslog.py --scenario rdp --via-docker
```

The `--via-docker` flag sends from inside the container to preserve the firewall source IP
through Docker NAT. Source IP must be in the `allowed-ips` list in `wazuh_manager.conf`.

### Sync Wazuh alerts into MVP pipeline

```bash
curl -X POST "http://localhost:8088/wazuh/sync-to-mvp?limit=50&minutes=120&q=*" \
  -H 'X-Internal-API-Key: dev-internal-key'
```

Notes:

- Reads from `wazuh-alerts-*` in Wazuh indexer.
- Re-running is safe — dedupe applied by `source + event_id`.
- Wazuh must fire rules before alerts appear (check `archives.log` first).

### Enable automatic sync worker

```bash
sed -i 's/^WAZUH_AUTO_SYNC_ENABLED=.*/WAZUH_AUTO_SYNC_ENABLED=true/' soc-integrator/.env
./run-combined-stack.sh up integrator --build -d
```

Auto-sync settings in `soc-integrator/.env`:

- `WAZUH_AUTO_SYNC_ENABLED` (`true|false`)
- `WAZUH_AUTO_SYNC_INTERVAL_SECONDS` (default `60`)
- `WAZUH_AUTO_SYNC_QUERY` (default `*`)
- `WAZUH_AUTO_SYNC_LIMIT` (default `50`)
- `WAZUH_AUTO_SYNC_MINUTES` (default `120`)

## KPI Dashboard

The KPI dashboard is embedded inside IRIS at `/kpi-dashboard`.

It shows alerts and cases with live SLA progress (colour-coded gauge), status, owner, and severity.
The page auto-refreshes every 60 seconds.

To seed test data covering every KPI state:

```bash
IRIS_API_KEY=<key> python3 scripts/seed-kpi-test-data.py
```

Find your API key in IRIS → My Profile.

## Logs

All logs (non-follow):

```bash
./run-combined-stack.sh logs --all --tail 200
```

Follow one stack:

```bash
./run-combined-stack.sh logs integrator -f
./run-combined-stack.sh logs wazuh -f
```

## Notes

- MVP escalation is wired to `pagerduty-stub` (not real PagerDuty).
- IRIS-web is used as case management backend.
- After `recreate wazuh`, run `./run-combined-stack.sh dedup` to remove duplicate index patterns
  created by the Wazuh dashboard post-init script.
- Wazuh archive logging requires `<logall>yes</logall>` in `wazuh_manager.conf`
  (already enabled); archives appear in `/var/ossec/logs/archives/archives.log`.
