fix(annuaire): threatmesh-bridge reads cscli itself (heredoc/stdin clash) — 0.3.2 (ref #768)
Some checks are pending
License Headers / check (push) Waiting to run

This commit is contained in:
CyberMind-FR 2026-07-01 08:37:43 +02:00
parent dfa0a778c2
commit fd47aa99b6
2 changed files with 12 additions and 13 deletions

View File

@ -1,4 +1,4 @@
secubox-annuaire (0.3.1-1~bookworm1) bookworm; urgency=medium
secubox-annuaire (0.3.2-1~bookworm1) bookworm; urgency=medium
* feat(#768): threatmesh WAF hook — crowdsec -> mesh bridge.
/usr/sbin/sbx-threatmesh-bridge reads local `cscli decisions list` and

View File

@ -2,16 +2,14 @@
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
# SecuBox-Deb :: sbx-threatmesh-bridge (#768)
# Federate this node's LOCAL crowdsec ban decisions into the gondwana threatmesh
# so every peer enforces them too. Reads `cscli decisions list`, publishes each
# NEW ban via `annuairectl ban` (TTL = the crowdsec duration, so it auto-expires
# in the mesh in step). Idempotent via a small state file; safe no-op without
# crowdsec or annuairectl. This is the WAF -> mesh hook: detect once, block fleet.
# so every peer enforces them too. Publishes each NEW ban via `annuairectl ban`
# (TTL = the crowdsec duration; reason = the scenario), idempotent via a state
# file. Safe no-op without crowdsec/annuairectl. WAF hook: detect once, block fleet.
set -euo pipefail
command -v cscli >/dev/null 2>&1 || exit 0
command -v annuairectl >/dev/null 2>&1 || exit 0
STATE=/var/lib/secubox/annuaire/threatmesh-bridge.json
cscli decisions list -o json 2>/dev/null | python3 - "$STATE" <<'PY'
python3 - /var/lib/secubox/annuaire/threatmesh-bridge.json <<'PY'
import json, os, re, subprocess, sys, time
state_path = sys.argv[1]
try:
@ -26,8 +24,10 @@ def dur_to_s(d):
return sum(int(n) * mult[u] for n, u in re.findall(r"(\d+)([hms])", d or "")) or 3600
try:
data = json.load(sys.stdin) or []
except Exception:
out = subprocess.check_output(["cscli", "decisions", "list", "-o", "json"], timeout=15)
data = json.loads(out or b"[]") or []
except Exception as e:
print("threatmesh-bridge: cscli read failed:", e)
sys.exit(0)
published = 0
@ -39,12 +39,11 @@ for alert in data:
if not ip or ip in state:
continue
ttl = dur_to_s(dec.get("duration"))
reason = "crowdsec:" + (dec.get("scenario") or "").split("/")[-1]
reason = ("crowdsec:" + (dec.get("scenario") or "").split("/")[-1])[:64]
r = subprocess.run(["annuairectl", "ban", ip, "--ttl", str(ttl),
"--reason", reason[:64], "--severity", "high"],
capture_output=True)
"--reason", reason, "--severity", "high"], capture_output=True)
if r.returncode == 0:
state[ip] = now + ttl * 0.9 # refresh a little before expiry
state[ip] = now + ttl * 0.9
published += 1
os.makedirs(os.path.dirname(state_path), exist_ok=True)