tutorial

/etc/suricata/suricata.yaml

## Prerequisites - Suricata 6.x or 7.x running on Linux (bare metal, VM, or Docker) - `suricatasc` CLI available for socket interaction - A free account at [...

Prerequisites

  • Suricata 6.x or 7.x running on Linux (bare metal, VM, or Docker)
  • suricatasc CLI available for socket interaction
  • A free account at vigilmon.online

Part 1: Enable the Suricata Unix socket

Suricata exposes a management interface via a Unix socket. This is the primary way to query Suricata's internal state without reading log files.

Enable the socket in suricata.yaml

unix-command:
  enabled: yes
  filename: /var/run/suricata/suricata-command.socket

Restart Suricata after changing the config:

sudo systemctl restart suricata

Verify the socket is listening

sudo suricatasc -c /var/run/suricata/suricata-command.socket uptime

Expected response:

{"message": 3842, "return": "OK"}

The message field contains the process uptime in seconds.


Part 2: Build a Suricata HTTP health endpoint

Vigilmon polls HTTP(S) endpoints, so we need a thin wrapper around suricatasc.

Create suricata_health.py

# suricata_health.py
import subprocess
import json
import os
from fastapi import FastAPI, Response

app = FastAPI()

SOCKET_PATH = os.environ.get("SURICATA_SOCKET", "/var/run/suricata/suricata-command.socket")
MIN_RULES = int(os.environ.get("SURICATA_MIN_RULES", "1000"))


def run_suricatasc(command: str) -> dict:
    result = subprocess.run(
        ["suricatasc", "-c", SOCKET_PATH, command],
        capture_output=True,
        text=True,
        timeout=10,
    )
    if result.returncode != 0:
        raise RuntimeError(result.stderr.strip())
    return json.loads(result.stdout)


@app.get("/health")
def health(response: Response):
    checks = {}

    try:
        uptime_resp = run_suricatasc("uptime")
        if uptime_resp.get("return") == "OK":
            checks["suricata_process"] = "ok"
            checks["uptime_sec"] = uptime_resp.get("message", 0)
        else:
            checks["suricata_process"] = "error"
    except Exception as e:
        checks["suricata_process"] = f"error: {e}"

    try:
        counters_resp = run_suricatasc("dump-counters")
        if counters_resp.get("return") == "OK":
            stats = counters_resp.get("message", {})
            rules_loaded = stats.get("detect", {}).get("rules_loaded") or 0
            if isinstance(rules_loaded, (int, float)) and rules_loaded >= MIN_RULES:
                checks["rules_loaded"] = int(rules_loaded)
                checks["rules_status"] = "ok"
            else:
                checks["rules_loaded"] = rules_loaded
                checks["rules_status"] = f"warning: only {rules_loaded} rules loaded"
        else:
            checks["rules_status"] = "error"
    except Exception as e:
        checks["rules_status"] = f"error: {e}"

    try:
        iface_resp = run_suricatasc("iface-list")
        if iface_resp.get("return") == "OK":
            checks["capture_interfaces"] = len(iface_resp.get("message", {}).get("ifaces", []))
        else:
            checks["capture_interfaces"] = "unknown"
    except Exception as e:
        checks["capture_interfaces"] = f"error: {e}"

    process_ok = checks.get("suricata_process") == "ok"
    rules_ok = checks.get("rules_status") == "ok"
    healthy = process_ok and rules_ok

    response.status_code = 200 if healthy else 503
    return {"status": "ok" if healthy else "degraded", "checks": checks}

Run the health service

pip install fastapi uvicorn
sudo uvicorn suricata_health:app --host 0.0.0.0 --port 8093

Or add it as a systemd service:

# /etc/systemd/system/suricata-health.service
[Unit]
Description=Suricata health endpoint
After=suricata.service

[Service]
User=root
WorkingDirectory=/opt/suricata-health
ExecStart=/usr/local/bin/uvicorn suricata_health:app --host 0.0.0.0 --port 8093
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl enable suricata-health
sudo systemctl start suricata-health

Verify the endpoint

curl http://localhost:8093/health

Expected response:

{
  "status": "ok",
  "checks": {
    "suricata_process": "ok",
    "uptime_sec": 7203,
    "rules_loaded": 45892,
    "rules_status": "ok",
    "capture_interfaces": 2
  }
}

Part 3: Set up HTTP monitoring in Vigilmon

Monitor Suricata process health

  1. Log in to vigilmon.online and click Add Monitor.
  2. Choose HTTP(S) monitor.
  3. Enter: http://your-suricata-host:8093/health
  4. Set interval to 1 minute.
  5. Add a keyword check: must contain "status":"ok".
  6. Add your alert channel.
  7. Click Save.

Monitor the rule update source

Suricata rule updates typically come from sources like Emerging Threats Open or a private feed. Monitor the update endpoint separately:

  1. Add a new HTTP(S) monitor.
  2. Enter the rule feed URL, for example your internal rule distribution server: https://rules.internal.example.com/health
  3. Set interval to 15 minutes.
  4. Add your alert channel.

Part 4: Monitor rule freshness

Stale rules are nearly as bad as no rules. Track when suricata-update last ran:

#!/bin/bash
# check_rule_freshness.sh — run via cron

RULE_FILE="/var/lib/suricata/rules/suricata.rules"
MAX_AGE_HOURS=26
STATUS_API="${STATUS_API_URL}/api/rule-status"

if [ ! -f "$RULE_FILE" ]; then
  curl -s -X POST "$STATUS_API" \
    -H "Content-Type: application/json" \
    -d '{"status": "error", "detail": "rule file missing"}'
  exit 1
fi

MTIME=$(stat -c %Y "$RULE_FILE")
NOW=$(date +%s)
AGE_HOURS=$(( (NOW - MTIME) / 3600 ))

if [ "$AGE_HOURS" -gt "$MAX_AGE_HOURS" ]; then
  curl -s -X POST "$STATUS_API" \
    -H "Content-Type: application/json" \
    -d "{\"status\": \"stale\", \"age_hours\": $AGE_HOURS}"
else
  curl -s -X POST "$STATUS_API" \
    -H "Content-Type: application/json" \
    -d "{\"status\": \"ok\", \"age_hours\": $AGE_HOURS}"
fi

Add to cron to run every hour:

0 * * * * /opt/suricata/check_rule_freshness.sh

Expose the latest status via an HTTP endpoint and point Vigilmon at it.


Part 5: Docker deployment monitoring

If Suricata runs in Docker, the socket needs to be mounted and the health service needs host-network access:

# docker-compose.yml
version: "3.8"

services:
  suricata:
    image: jasonish/suricata:7-latest
    network_mode: host
    cap_add:
      - NET_ADMIN
      - SYS_NICE
    volumes:
      - /etc/suricata:/etc/suricata:ro
      - /var/log/suricata:/var/log/suricata
      - suricata-run:/var/run/suricata
    command: ["-i", "eth0", "--unix-socket=/var/run/suricata/suricata-command.socket"]

  suricata-health:
    image: python:3.12-slim
    network_mode: host
    command: >
      sh -c "pip install -q fastapi uvicorn &&
             uvicorn suricata_health:app --host 0.0.0.0 --port 8093"
    volumes:
      - ./suricata_health.py:/app/suricata_health.py
      - suricata-run:/var/run/suricata:ro
    working_dir: /app
    environment:
      SURICATA_SOCKET: /var/run/suricata/suricata-command.socket

volumes:
  suricata-run:

Part 6: Webhook alerts for IDS/IPS outages

An IDS/IPS outage is a security event, not just an availability event. Configure Vigilmon webhooks to trigger security-specific incident flows:

from fastapi import FastAPI, Request
import logging

app = FastAPI()
logger = logging.getLogger(__name__)


@app.post("/webhook/vigilmon")
async def vigilmon_alert(request: Request):
    payload = await request.json()
    status = payload.get("status")
    monitor_name = payload.get("monitor_name")
    checked_at = payload.get("checked_at")

    if status == "down":
        logger.critical(
            "SECURITY ALERT: Suricata IDS/IPS is DOWN — network threat detection offline",
            extra={"monitor": monitor_name, "at": checked_at},
        )
        await create_security_incident(
            title=f"Suricata DOWN: {monitor_name}",
            severity="critical",
            detail="Network intrusion detection is offline. Threats will not be detected.",
        )
        await notify_soc(monitor_name)

    elif status == "up":
        logger.info("Suricata monitor recovered", extra={"monitor": monitor_name})
        await resolve_security_incident(monitor_name)

    return {"received": True}

Vigilmon sends this payload on status transitions:

{
  "monitor_id": "mon_suricata01",
  "monitor_name": "Suricata IDS Health",
  "status": "down",
  "url": "http://ids-host.internal:8093/health",
  "checked_at": "2026-07-02T14:00:00Z",
  "response_code": 503,
  "response_time_ms": 10001
}

Part 7: Multi-sensor fleet monitoring

If you run Suricata on multiple network sensors, add one Vigilmon monitor per host:

| Monitor name | URL | Keyword | |-------------|-----|---------| | Suricata — DMZ sensor | http://dmz-ids:8093/health | "status":"ok" | | Suricata — Core network sensor | http://core-ids:8093/health | "status":"ok" | | Suricata — Cloud egress sensor | http://cloud-ids:8093/health | "status":"ok" |

Group them in Vigilmon under a "Network Security" project so you have one dashboard for the entire IDS fleet.


Summary

Your Suricata deployment now has layered monitoring:

  1. Process health endpoint — confirms Suricata is running, has rules loaded, and the capture interface is active, polled every 60 seconds by Vigilmon.
  2. Rule update source monitor — catches failures in the feed delivery pipeline before rules go stale.
  3. Rule freshness check — alerts when suricata-update has not run within the expected window.
  4. Security-aware webhooks — DOWN events trigger P1 security incidents, not just standard availability alerts.
  5. Fleet monitoring — one monitor per sensor so you know exactly which network segment lost IDS coverage.

Vigilmon handles check scheduling, multi-region polling, and alert routing. When Suricata goes down, your SOC team knows within 60 seconds — before attackers figure it out.


Monitor your Suricata IDS/IPS fleet free at vigilmon.online

#suricata #ids #networksecurity #infosec #monitoring

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →