Getting Started
Install the stack, run your first scenario and verify that routing works
The system ships as two Docker Compose stacks. ids/ contains the reverse proxy, shop,
honeypot pools, Redis, Suricata and Vector. siem/ contains Elasticsearch, Kibana and the
Vector aggregator. You can run both on one host or split them across two. The PyQt6 dashboard in
dashboard/ is the recommended way to configure, deploy and operate either shape.
Choose a Path
Prerequisites
Docker and system packages
Every host that runs a stack needs Docker with the Compose plugin. The machine that runs the dashboard
also needs python3 with the venv module. For a remote target you also need an SSH key
that can log in, and rsync on both machines. The dashboard checks for docker, Compose, ssh and
rsync at startup and shows the matching command below in a banner if one is missing.
| Distribution | Install command |
|---|---|
| Arch / Manjaro | sudo pacman -S --needed docker docker-compose openssh rsync |
| Ubuntu / Debian / Mint / Pop!_OS | sudo apt install docker.io docker-compose-v2 openssh-client rsync |
| Fedora / RHEL / CentOS / Rocky / Alma | sudo dnf install docker docker-compose-plugin openssh-clients rsync |
| openSUSE | sudo zypper install docker docker-compose openssh rsync |
| Other | Follow docs.docker.com/engine/install. |
The scenario scripts and the commands on this page also use git, curl and
jq. Then let your user run Docker:
sudo systemctl enable --now docker.service
sudo usermod -aG docker "$USER" # log out and back in afterwards
docker compose version # confirms the Compose plugin
Kernel settings
Without the first setting, Redis logs a memory-overcommit warning on every start. Elasticsearch needs the larger mmap limit on the SIEM host. No container can set these itself, so set them on each host. The dashboard's host-settings banner checks every host in use, including remote ones over SSH.
echo 'vm.overcommit_memory = 1' | sudo tee /etc/sysctl.d/99-honeypot.conf
echo 'vm.max_map_count = 262144' | sudo tee -a /etc/sysctl.d/99-honeypot.conf
sudo sysctl --system
Python environment for the dashboard
You normally don't need to do anything here. On its first run, ./run.sh creates
dashboard/venv/, installs PyQt6, and shows a progress window while it installs the rest of
requirements.txt. To set the environment up by hand instead (for example on a headless build
machine):
cd dashboard
python3 -m venv venv
./venv/bin/python3 -m pip install -r requirements.txt
The dashboard uses PyQt6 WebEngine, which needs the usual Qt/Chromium libraries (on Arch: nss, alsa-lib, mesa, libxkbcommon-x11, libxcomposite, libxdamage, libxrandr, libxtst, xcb-util-cursor).
Dashboard Setup
cd dashboard
./run.sh
The first launch creates the venv (see above) and then opens the setup wizard. The wizard's steps are Welcome → Remote hosts (optional) → Edge .env → SIEM .env → Certificates (optional) → Done.
- Welcome: shows a warning if docker, Compose, ssh or rsync is missing.
- Remote hosts: mark
idsand/orsiemas remote and enter each one's SSH host, port, user, key and remote path. Leave both unticked to run everything on this machine. - Edge .env / SIEM .env: fill in each project's
.env. A local file starts from.env.example. A remote file is fetched over SSH and saved back to the remote. Use Generate for the secrets (see the minimum values). - Certificates: Generate all certificates now creates the SIEM CA and service certificates and the edge nginx certificate. Do this before the first start.
You can run the wizard again from Connectors & Config → Re-run setup wizard. It keeps the values
already in the .env files. For remote targets, copy the code to the host once from
Connectors & Config: run Test connection, then Upload entire project…
(rsync, which skips backups, logs and volumes). After that, Upload .env to remote… pushes
configuration changes alone.
First Run Walkthrough
- Clear the banners. Apply any commands shown in the dependency or host-settings banner above the pages.
- Start the SIEM, then the edge. On Workloads, start
siemand thenids. The progress bar counts one-shot jobs that exit with 0 as up. The firstidsstart seeds the shop and builds the initial pools, which takes a few minutes. The Honeypot layer dropdown on this page chooses between the two deception layers; keep the default for a first run. - Check the topology. On Global Visualization, every node should be green (healthy, running or exited-ok). A red !N badge shows error lines in that container's log.
- Check log shipping. Log Pipeline should report that the edge shipper is
delivering to the SIEM's
vector_inbound. - Send a clean request, then an attack. On Attack Simulation, browse the shop (it should route to production), then pick the CVE-2024-27956 preset and press Run pattern (curl). The Pattern analyzer tab shows each signal and the HONEYPOT decision. Live Events shows the same decision without needing the SIEM.
- Follow the session. Adversary Sessions and Pool State Store show its score and pool. SIEM Analytics opens Kibana, logged in automatically, at the Attack Patterns dashboard.
- Clean up. Unpoison host IP clears the reputation your own test runs built up for your host, so later visits are not diverted.
Press F1 on any page for its guide, or Ctrl+K to jump to a page by name. The Knowledge Base page has eight guided labs that cover the steps above and more. The Dashboard Guide describes every page.
Manual Setup
The edge stack is enough for a first run. The shop is published at https://localhost/ with a self-signed certificate, so expect a browser warning. The command below brings up the default reverse-proxy deception layer (a separate decoy backend per session); see Honeypot Layers for the lighter database-proxy alternative and how to choose between them.
cd ids
cp .env.example .env
$EDITOR .env # set real passwords first
docker compose up -d --build
docker compose ps
docker compose logs -f reverse_proxy
Minimum .env values to change
| Variable | Purpose |
|---|---|
MYSQL_ROOT_PASSWORD | Root password for the production and honeypot databases. |
MYSQL_PASSWORD | Application user password for the production database. |
MYSQL_PASSWORD_POOL_2, MYSQL_PASSWORD_POOL_3 | Per-pool database passwords. They must differ from the production password and from each other; pool isolation depends on this. |
REDIS_PASSWORD | Password for the Redis session store. |
SESSION_SIGNING_KEY | HMAC key for the signed SERVERID session cookie. |
INTERNAL_TEST_SECRET | Gates the diagnostic X-Route-Target and X-Risk-Score headers. Leave unset in production so they fail closed. |
Never commit a filled .env. The uploaded copy on a remote host contains database, Redis and signing credentials, so restrict its permissions and rotate values if a host is rebuilt or shared.
Optional SIEM stack
cd siem/certs/root-ca && ./gen_elk_certs.sh
cd ../../docker
cp .env.example .env
docker compose up -d
Running both stacks on one host is supported for evaluation. The two-host and single-host pages describe the differences, and SIEM & log pipeline covers the data path.
Verify the Stack
Work outward from the containers to the detection pipeline:
- Containers are healthy.
docker compose psshould show the proxy, production shop, pool manager, Redis, Suricata, Vector and the database services running. The first start also seeds the shop and builds the initial pools, which takes a few minutes. - The shop responds. Open
https://localhost/and browse a product. A clean request should stay on production. - A scenario is detected. Run one of the scenario scripts, for example
bash testing/scenario_02_owasp_wstg.sh. With the internal test secret configured, the response headers show the route and risk score. - A pool was assigned. Inspect Redis for the session and pool keys, or use the dashboard's Pool State Store, Adversary Sessions and Pool Scaling pages.
- Events reached the SIEM. With the SIEM stack running, refresh the Kibana dashboards. Security events arrive through Vector; Suricata alerts arrive through
eve.jsoningestion.
Set INTERNAL_TEST_SECRET and send a matching X-Internal-Test-Auth header to receive X-Route-Target and X-Risk-Score. Without the secret, the headers are never emitted — a blind pentest must not get the routing oracle for free.
Command Cheat Sheet
| Task | Command |
|---|---|
| Start the edge stack | cd ids && docker compose up -d |
| Rebuild after changes | docker compose up -d --build |
| Follow proxy logs | docker compose logs -f reverse_proxy |
| Open a Redis shell | docker compose exec session_store redis-cli -a "$(grep ^REDIS_PASSWORD= .env | cut -d= -f2-)" --no-auth-warning |
| Run all scenarios | bash testing/run_all_scenarios.sh |
| Run Lua unit tests | reverse_proxy_enhanced/lua/tests/run_all.sh |
| Run dashboard tests | cd dashboard && python3 -m unittest discover -s tests -v |
| Regenerate Kibana objects | python3 siem/kibana/build_dashboards.py --ndjson |
| Export live dashboards | python3 siem/kibana/export_dashboards.py |