Lab infrastructure

Cowrie honeypot + Grafana + Wazuh: attacker intelligence pipeline in a Docker Compose stack

A minimal, auditable deployment that runs Cowrie SSH/Telnet honeypot, feeds attacker session data to Wazuh Indexer, and visualizes it in a Grafana dashboard provisioned from code. LAN-only binding by default. No credentials written to disk. CI validates the compose file on every push.

Context

Components:

  • Cowrie — low-interaction SSH/Telnet honeypot. Records attacker commands, credentials attempted, session duration, and payload downloads. Writes structured JSON logs.
  • Grafana OSS — dashboard provisioned from code (grafana/dashboards/honeypot_ops.json). Queries Wazuh Indexer directly via OpenSearch datasource.
  • Wazuh Indexer — already in the lab infrastructure. Cowrie JSON logs ingested via Wazuh agent localfile config → decoded → indexed under rule.groups:cowrie.

Data flow:

  1. Attacker hits Cowrie on SSH port (2222) or Telnet port (2223)
  2. Cowrie logs session to /var/log/cowrie/cowrie.json
  3. Wazuh agent reads JSON log via localfile config
  4. Wazuh manager decodes and indexes to Wazuh Indexer
  5. Grafana queries rule.groups:cowrie → dashboard panels update

Problem

services:
  cowrie:
    image: cowrie/cowrie:latest
    ports:
      - "${LAN_BIND_IP:-localhost}:${COWRIE_SSH_PORT:-2222}:2222"
      - "${LAN_BIND_IP:-localhost}:${COWRIE_TELNET_PORT:-2223}:2223"
    volumes:
      - ./cowrie/var:/cowrie/cowrie-git/var
    # Logs write to cowrie/var/log/cowrie/cowrie.json

  grafana:
    image: grafana/grafana-oss:latest
    ports:
      - "${LAN_BIND_IP:-localhost}:${GRAFANA_PORT:-3000}:3000"
    environment:
      - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER}
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASS}
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/dashboards:/var/lib/grafana/dashboards

All secrets live in .env (not committed). LAN_BIND_IP defaults to localhost — services are LAN-only unless explicitly overridden.

Approach

Dashboard JSON lives in grafana/dashboards/honeypot_ops.json and is provisioned automatically on container start. Three panels:

  • Sessions Over Time — time series of Cowrie session events (rule.groups:cowrie). Shows attacker activity volume across the monitoring window.
  • Top Usernames — aggregated credential spray patterns. Shows which usernames attackers try most.
  • Top Commands — what attackers type after gaining access. Shows adversary objectives and tooling.

No manual dashboard config — dashboard state is version-controlled and reproduced identically on every deploy.

Evidence

Wazuh agent localfile config on the honeypot VM or host:

<localfile>
  <log_format>json</log_format>
  <location>/var/log/cowrie/cowrie.json</location>
</localfile>

OpenSearch queries to verify ingestion:

rule.groups:cowrie
data.cowrie.session:*
data.username:* AND rule.groups:cowrie

Outcome

# Copy env template and fill in values
cp .env.example .env

# Bring up the stack
docker compose up -d

# Health check script
./scripts/verify_stack.sh

# Expected:
# - cowrie: Up
# - grafana: Up
# - Grafana HTTP check: 302 Found (redirect to login)

Lessons + next hardening step

  • LAN-only by defaultLAN_BIND_IP defaults to localhost. Public-facing deployment requires explicit .env change.
  • No credentials on disk — all secrets via environment variables from .env. No hardcoded values in compose file or scripts.
  • Proxmox DMZ placement — docs specify isolating the honeypot VM on a DMZ bridge segment; DMZ cannot initiate connections to LAN.
  • CI validation — GitHub Actions validates docker-compose.yml syntax and shellchecks scripts on every push.

Why this matters to a hiring team

A honeypot generates attacker intelligence — real credential spray patterns, post-auth command sequences, and tooling fingerprints from actual threat actors (or automated scanners). That data feeds directly into detection rule refinement: if the top username spray list from the honeypot shows admin, root, and ubuntu, the Sigma and Wazuh authentication failure rules should be tuned to weight those patterns. The Grafana dashboard makes that feedback loop visible. The Wazuh integration means attacker session data is indexed alongside endpoint telemetry — enabling correlation between honeypot hits and lateral movement patterns on monitored systems.