From ee28a9739e0414d478b491e49027f3dd048063eb Mon Sep 17 00:00:00 2001 From: Thibault Koechlin Date: Fri, 22 May 2026 15:43:43 +0200 Subject: [PATCH 1/2] add timestamps for verified instructions --- .github/workflows/validate.yml | 6 + CLAUDE.md | 37 +++- skills/crowdsec/references/appsec/deploy.md | 8 + .../references/configure/allowlists.md | 8 + .../configure/bouncers/web-servers.md | 8 + .../crowdsec/references/install/bare-metal.md | 8 + skills/crowdsec/scripts/check-verification.py | 195 ++++++++++++++++++ 7 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 skills/crowdsec/scripts/check-verification.py diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index db5e65b..7c514db 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -30,3 +30,9 @@ jobs: run: | npm install -g @anthropic-ai/claude-code claude plugin validate . + + # Reports per-doc verification coverage (date / version / env) and flags + # stale or missing records. Gaps and staleness are informational and do + # not fail the build; a malformed `verified:` block does. + - name: Verification coverage report + run: python3 skills/crowdsec/scripts/check-verification.py diff --git a/CLAUDE.md b/CLAUDE.md index ddfacce..5c893b3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,9 +10,9 @@ Conventions for authoring this skill. This governs how skill content is **writte - **Write only the final, correct information.** Never record self-corrections, dead ends, or "actually, it turned out…" narration discovered while authoring. The reader gets the conclusion, not the journey. This applies equally to inline expected-output hints: state the correct - outcome, never the wrong-then-fixed version. Do not annotate content as *verified* (no - "(verified)", "Verified on…", "verified gotcha") — verification is guaranteed by this file, - not restated per-doc. + outcome, never the wrong-then-fixed version. Do not annotate verification *inline in prose* (no + "(verified)", "Verified on…", "verified gotcha"). Record verification in the per-file `verified:` + frontmatter block instead — see **Verification tracking** below. - **Keep `SKILL.md` a router.** It is an index and decision layer that points to `references/`. Depth and recipes belong in the reference docs, not in `SKILL.md`. - **Cover every environment.** Each command or recipe carries its systemd / docker / k8s variant, @@ -34,4 +34,35 @@ Conventions for authoring this skill. This governs how skill content is **writte behavior as confirmed. - **Avoid duplicates** by checking in the existing skill documentation if the information is already present. If it is, ensure it's properly linked/routed, but don't duplicate instruction, code blocks or configurations. +## Verification tracking + +Verification is recorded **per reference doc**, in a `verified:` YAML frontmatter block, so freshness +is queryable and drift is visible. A doc with no `verified:` block has never been confirmed against a +real environment. + +```yaml +--- +verified: + - date: 2026-05-22 # ISO 8601 (YYYY-MM-DD); the day the recipes were run + version: "1.6.5" # CrowdSec engine version, from `cscli version` + env: docker # systemd | docker | k8s (free-form allowed, e.g. opnsense) + notes: "deploy + reload path only" # optional, short scope note +--- +``` + +Rules: + +- **One entry per env.** Re-verifying the same env updates that entry in place (bump `date` and + `version`); verifying a new env appends a new entry. This keeps "cover every environment" + auditable. +- **Stamp it when you verify it.** When you run a doc's commands against the real test environment + (see Testing) and observe them work, add or update that doc's `verified:` entry in the same change: + `date` = today, `version` = the output of `cscli version`, `env` = the detected environment. The + record and the verification happen together — never stamp from memory or inference. +- Use ISO 8601 dates so the checker can sort and age them. +- Don't pre-seed unverified docs with empty blocks; absence *is* the "never verified" signal. + +`skills/crowdsec/scripts/check-verification.py` (stdlib-only, safe to run locally) reports coverage, +lists docs with no block, flags entries older than 180 days, and fails on a malformed block. + diff --git a/skills/crowdsec/references/appsec/deploy.md b/skills/crowdsec/references/appsec/deploy.md index c4a0274..d662950 100644 --- a/skills/crowdsec/references/appsec/deploy.md +++ b/skills/crowdsec/references/appsec/deploy.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-21 + version: "1.7.8" + env: systemd + notes: "nginx acquisition + AppSec deploy" +--- + # AppSec — Deploy Canonical docs: · quickstart · rules deploy · advanced deployments diff --git a/skills/crowdsec/references/configure/allowlists.md b/skills/crowdsec/references/configure/allowlists.md index 69ed5df..abe888e 100644 --- a/skills/crowdsec/references/configure/allowlists.md +++ b/skills/crowdsec/references/configure/allowlists.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-20 + version: "1.7.8" + env: systemd + notes: "allowlists add/check/list" +--- + # Configure — Allowlists, whitelist parsers, and postoverflows Canonical docs: diff --git a/skills/crowdsec/references/configure/bouncers/web-servers.md b/skills/crowdsec/references/configure/bouncers/web-servers.md index 8d2c639..a08fd87 100644 --- a/skills/crowdsec/references/configure/bouncers/web-servers.md +++ b/skills/crowdsec/references/configure/bouncers/web-servers.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-21 + version: "1.7.8" + env: systemd + notes: "nginx bouncer path only" +--- + # Bouncers — Web servers (nginx, haproxy, apache, Traefik, Caddy) Canonical docs: (per-bouncer pages: nginx, haproxy, apache, traefik, caddy) diff --git a/skills/crowdsec/references/install/bare-metal.md b/skills/crowdsec/references/install/bare-metal.md index 8bc8cba..a871584 100644 --- a/skills/crowdsec/references/install/bare-metal.md +++ b/skills/crowdsec/references/install/bare-metal.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-21 + version: "1.7.8" + env: systemd + notes: "apt + systemd install path" +--- + # Install — bare metal (apt/dnf + systemd) Canonical docs: · post-install diff --git a/skills/crowdsec/scripts/check-verification.py b/skills/crowdsec/scripts/check-verification.py new file mode 100644 index 0000000..5703d81 --- /dev/null +++ b/skills/crowdsec/scripts/check-verification.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 +"""Report verification coverage across the CrowdSec skill's reference docs. + +A reference doc records when its recipes were last exercised against a real +environment in a YAML `verified:` frontmatter block (see CLAUDE.md § +"Verification tracking"). This static checker — stdlib only, safe to run on the +local working copy — walks the docs and reports: + + * a coverage table (doc · last-verified date · version · env · age in days), + * docs with no `verified:` block (coverage gaps), + * entries older than the staleness threshold (default 180 days). + +It exits non-zero only when a present `verified:` block is malformed, so it can +run as a non-blocking CI report while backfill is still in progress. +""" + +from __future__ import annotations + +import argparse +import datetime as dt +import sys +from pathlib import Path + +# skills/crowdsec/scripts/check-verification.py -> skills/crowdsec +SKILL_ROOT = Path(__file__).resolve().parent.parent +REFERENCES = SKILL_ROOT / "references" +SKILL_MD = SKILL_ROOT / "SKILL.md" + +CANONICAL_ENVS = {"systemd", "docker", "k8s"} +REQUIRED_KEYS = ("date", "version", "env") +OPTIONAL_KEYS = ("notes",) +ALLOWED_KEYS = set(REQUIRED_KEYS) | set(OPTIONAL_KEYS) + + +class SchemaError(Exception): + """A present `verified:` block does not match the expected schema.""" + + +def split_frontmatter(text: str) -> str | None: + """Return the YAML frontmatter body, or None if the file has no frontmatter. + + Frontmatter is the block between a leading `---` line and the next `---`. + """ + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return None + for i in range(1, len(lines)): + if lines[i].strip() == "---": + return "\n".join(lines[1:i]) + raise SchemaError("frontmatter opened with '---' but never closed") + + +def parse_verified(frontmatter: str) -> list[dict] | None: + """Parse the `verified:` list out of a frontmatter body. + + Targeted parser for the documented schema (a top-level `verified:` key whose + value is a list of flat mappings) — avoids a PyYAML dependency. Returns None + if there is no `verified:` key, or a list of entry dicts otherwise. + """ + lines = frontmatter.splitlines() + start = None + for idx, line in enumerate(lines): + if line.rstrip() == "verified:" or line.rstrip() == "verified: []": + if line.rstrip().endswith("[]"): + return [] + start = idx + 1 + break + if start is None: + return None + + entries: list[dict] = [] + current: dict | None = None + for raw in lines[start:]: + if raw.strip() == "": + continue + # A new top-level key (no leading space) ends the verified block. + if not raw.startswith(" ") and not raw.startswith("\t"): + break + stripped = raw.strip() + if stripped.startswith("- "): + current = {} + entries.append(current) + stripped = stripped[2:].strip() + if not stripped: + continue + if current is None: + raise SchemaError("verified entry data found before any '-' list item") + if ":" not in stripped: + raise SchemaError(f"malformed line in verified entry: {raw!r}") + key, _, value = stripped.partition(":") + current[key.strip()] = value.strip().strip('"').strip("'") + return entries + + +def validate_entry(entry: dict, doc: Path) -> dt.date: + """Validate one verified entry; return its parsed date.""" + unknown = set(entry) - ALLOWED_KEYS + if unknown: + raise SchemaError(f"unknown key(s) {sorted(unknown)} in a verified entry") + missing = [k for k in REQUIRED_KEYS if not entry.get(k)] + if missing: + raise SchemaError(f"verified entry missing required key(s): {missing}") + try: + return dt.date.fromisoformat(entry["date"]) + except ValueError as exc: + raise SchemaError(f"date {entry['date']!r} is not ISO 8601 (YYYY-MM-DD)") from exc + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--stale-days", + type=int, + default=180, + help="flag verified entries older than this many days (default: 180)", + ) + args = parser.parse_args() + + today = dt.date.today() + docs = sorted(REFERENCES.rglob("*.md")) + if SKILL_MD.exists(): + docs.append(SKILL_MD) + if not docs: + print(f"No reference docs found under {REFERENCES}", file=sys.stderr) + return 1 + + rows: list[tuple[str, str, str, str, str]] = [] + gaps: list[str] = [] + stale: list[str] = [] + errors: list[str] = [] + + for doc in docs: + rel = doc.relative_to(SKILL_ROOT).as_posix() + try: + frontmatter = split_frontmatter(doc.read_text(encoding="utf-8")) + entries = parse_verified(frontmatter) if frontmatter is not None else None + except SchemaError as exc: + errors.append(f"{rel}: {exc}") + continue + + if not entries: + gaps.append(rel) + continue + + for entry in entries: + try: + date = validate_entry(entry, doc) + except SchemaError as exc: + errors.append(f"{rel}: {exc}") + continue + age = (today - date).days + env = entry["env"] + env_note = "" if env in CANONICAL_ENVS else " (non-canonical env)" + rows.append((rel, entry["date"], entry["version"], env + env_note, f"{age}d")) + if age > args.stale_days: + stale.append(f"{rel} [{env}] verified {entry['date']} ({age}d ago)") + + headers = ("doc", "last verified", "version", "env", "age") + widths = [len(h) for h in headers] + for row in rows: + widths = [max(w, len(c)) for w, c in zip(widths, row)] + + def fmt(cols: tuple[str, ...]) -> str: + return " ".join(c.ljust(w) for c, w in zip(cols, widths)) + + print("== Verification coverage ==") + if rows: + print(fmt(headers)) + print(fmt(tuple("-" * w for w in widths))) + for row in sorted(rows): + print(fmt(row)) + else: + print("(no docs carry a verified: block yet)") + + print(f"\nVerified: {len(rows)} record(s) across {len(docs) - len(gaps) - len(errors)} doc(s)") + print(f"Coverage gaps (no verified: block): {len(gaps)}") + for g in gaps: + print(f" - {g}") + + if stale: + print(f"\nStale (> {args.stale_days} days): {len(stale)}") + for s in stale: + print(f" ! {s}") + + if errors: + print(f"\nMalformed verified: block(s): {len(errors)}", file=sys.stderr) + for e in errors: + print(f" x {e}", file=sys.stderr) + return 1 + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 8ced041594c3f8458facf86c74ce8b7fbd83dc76 Mon Sep 17 00:00:00 2001 From: Thibault Koechlin Date: Fri, 22 May 2026 17:11:19 +0200 Subject: [PATCH 2/2] add further verification steps with headers --- skills/crowdsec/references/appsec/configure.md | 10 +++++++++- skills/crowdsec/references/appsec/overview.md | 8 ++++++++ .../crowdsec/references/appsec/troubleshoot.md | 10 +++++++++- .../references/configure/acquisition.md | 8 ++++++++ .../references/configure/bouncers/firewall.md | 17 +++++++++++++++++ skills/crowdsec/references/configure/hub.md | 16 +++++++++++++--- .../crowdsec/references/configure/profiles.md | 8 ++++++++ .../references/debug/bouncer-not-blocking.md | 8 ++++++++ .../crowdsec/references/debug/common-errors.md | 8 ++++++++ skills/crowdsec/references/debug/no-alerts.md | 8 ++++++++ skills/crowdsec/references/debug/parsing.md | 8 ++++++++ skills/crowdsec/references/debug/triage.md | 8 ++++++++ skills/crowdsec/references/install/docker.md | 8 ++++++++ .../crowdsec/references/operate/health-check.md | 12 ++++++++++-- skills/crowdsec/references/operate/upgrades.md | 8 ++++++++ 15 files changed, 138 insertions(+), 7 deletions(-) diff --git a/skills/crowdsec/references/appsec/configure.md b/skills/crowdsec/references/appsec/configure.md index c1f39af..91d92bb 100644 --- a/skills/crowdsec/references/appsec/configure.md +++ b/skills/crowdsec/references/appsec/configure.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "appsec-configs/rules list+inspect, metrics rules table; fixed eval-time claim" +--- + # AppSec — Configure Canonical docs: · rule management · hooks · alerts & scenarios · API validation @@ -132,7 +140,7 @@ For captcha responses, configuration lives on the **bouncer** (captcha provider ## Performance levers - `request_body_limit` (engine config) caps how much of the request body AppSec processes — defaults are usually fine; raise for APIs with large legitimate payloads, lower for static-only fronts. -- Rule load order is automatic; per-rule eval time appears in `cscli metrics show appsec` once you generate traffic. +- Rule load order is automatic; per-rule **trigger counts** appear in `cscli metrics show appsec` once you generate traffic (the Rules Metrics table) — useful for spotting hot rules, though `cscli` does not report per-rule eval time. - Inband evaluation adds latency on the request path. Out-of-band is asynchronous and does not. - Move expensive rules (large regex, body inspection) to out-of-band if latency matters more than per-request blocking. diff --git a/skills/crowdsec/references/appsec/overview.md b/skills/crowdsec/references/appsec/overview.md index f8c1217..223927d 100644 --- a/skills/crowdsec/references/appsec/overview.md +++ b/skills/crowdsec/references/appsec/overview.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "listener 7422, configs/rules paths, out-of-band scenario→ban path; conceptual doc" +--- + # AppSec — Overview Canonical docs: · request lifecycle · protocol · FAQ diff --git a/skills/crowdsec/references/appsec/troubleshoot.md b/skills/crowdsec/references/appsec/troubleshoot.md index 4fa7e4e..b8da0a1 100644 --- a/skills/crowdsec/references/appsec/troubleshoot.md +++ b/skills/crowdsec/references/appsec/troubleshoot.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "metrics show appsec, listener on 7422, appsec-rules/configs inspect; fixed eval-time claim" +--- + # AppSec — Troubleshoot Canonical docs: · FAQ · benchmark @@ -159,7 +167,7 @@ Inspect the bouncer's own log for `captcha` lines; it should announce when it's sudo cscli metrics show appsec ``` -Per-rule eval time shows up in the rules table. Suspects: +The Rules Metrics table shows a per-rule **Triggered** count (how often each rule matched), not timing — use it to spot a hot rule worth moving out-of-band. `cscli` does not expose per-rule eval time; for actual latency numbers use the benchmark methodology below. Suspects: - **Expensive regex** in a rule body — move to out-of-band, or replace with a cheaper match. - **Large request bodies** — raise / lower `request_body_limit` in the engine config; the default is conservative. diff --git a/skills/crowdsec/references/configure/acquisition.md b/skills/crowdsec/references/configure/acquisition.md index 499580b..668303f 100644 --- a/skills/crowdsec/references/configure/acquisition.md +++ b/skills/crowdsec/references/configure/acquisition.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "config keys, crowdsec -t (clean + FATAL bad-source), appsec listener" +--- + # Configure — Acquisition (log sources) Canonical docs: · datasources index diff --git a/skills/crowdsec/references/configure/bouncers/firewall.md b/skills/crowdsec/references/configure/bouncers/firewall.md index bfc7ad1..00f5bef 100644 --- a/skills/crowdsec/references/configure/bouncers/firewall.md +++ b/skills/crowdsec/references/configure/bouncers/firewall.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "nftables bouncer v0.0.34 install+register, nft sets/chains (priority filter-10), ban→cscli set→drop; added stale-key recovery note" +--- + # Bouncers — Firewall (nftables / iptables / ipset) Canonical docs: @@ -49,6 +57,15 @@ If you *also* run `cscli bouncers add` you create a second, unused key — skip Only register manually when the bouncer runs on a **different host** than LAPI (then set `api_url` to the remote LAPI and paste the manual key into the yaml). +> **Gotcha — reinstall over a stale key.** If a previous +> `/etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml` survives a purge/reinstall, +> the postinst may not rewrite the `api_key`, and the service then fails to start with +> `API error: access forbidden` / `bouncer stream halted` in +> `/var/log/crowdsec-firewall-bouncer.log` (and the dpkg `--configure` step errors). +> Re-register: `cscli bouncers delete `, `KEY=$(cscli bouncers add fw-local -o raw)`, +> write it into the yaml's `api_key:`, `systemctl restart crowdsec-firewall-bouncer`. +> See [../../debug/bouncer-not-blocking.md](../../debug/bouncer-not-blocking.md) § 3. + ## 3 — What it creates in nftables The bouncer builds its **own** tables, isolated from your existing ruleset: diff --git a/skills/crowdsec/references/configure/hub.md b/skills/crowdsec/references/configure/hub.md index facb9a4..374eba4 100644 --- a/skills/crowdsec/references/configure/hub.md +++ b/skills/crowdsec/references/configure/hub.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "update/upgrade, list -o raw, collections install+remove, inspect tainted fields" +--- + # Configure — Hub management Canonical docs: · `cscli hub` reference @@ -13,9 +21,11 @@ Install a **collection** and it pulls every item it depends on. Installing ```bash sudo cscli collections install crowdsecurity/wordpress -# scenarios: crowdsecurity/http-bf-wordpress_bf, crowdsecurity/http-wordpress_user-enum, -# crowdsecurity/http-wordpress_wpconfig -# collections: crowdsecurity/wordpress +# enabling scenarios:crowdsecurity/http-bf-wordpress_bf +# enabling scenarios:crowdsecurity/http-wordpress_wpconfig +# enabling scenarios:crowdsecurity/http-wordpress_user-enum +# enabling collections:crowdsecurity/wordpress +# # Run 'sudo systemctl reload crowdsec' for the new configuration to be effective. ``` diff --git a/skills/crowdsec/references/configure/profiles.md b/skills/crowdsec/references/configure/profiles.md index 5827e0e..fa21f82 100644 --- a/skills/crowdsec/references/configure/profiles.md +++ b/skills/crowdsec/references/configure/profiles.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "profiles.yaml structure, no cscli profiles cmd, simulation status, decision add/list/delete" +--- + # Configure — Profiles (decisions, durations, simulation) Canonical docs: · post-install profiles diff --git a/skills/crowdsec/references/debug/bouncer-not-blocking.md b/skills/crowdsec/references/debug/bouncer-not-blocking.md index d8b2bb5..a8c4e93 100644 --- a/skills/crowdsec/references/debug/bouncer-not-blocking.md +++ b/skills/crowdsec/references/debug/bouncer-not-blocking.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "allowlists/decisions/bouncers ladder, LAPI curl 200, firewall nft sets/chains/counter" +--- + # Debug — Decisions exist but bouncer not blocking Canonical docs: · bouncers index diff --git a/skills/crowdsec/references/debug/common-errors.md b/skills/crowdsec/references/debug/common-errors.md index 0e8d00f..fc590b6 100644 --- a/skills/crowdsec/references/debug/common-errors.md +++ b/skills/crowdsec/references/debug/common-errors.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "diagnostic commands + ExecStartPre/-t, machines, geoip path; error strings are catalog" +--- + # Debug — Common errors (string → cause catalog) Canonical docs: diff --git a/skills/crowdsec/references/debug/no-alerts.md b/skills/crowdsec/references/debug/no-alerts.md index 9bfe81d..8ee0407 100644 --- a/skills/crowdsec/references/debug/no-alerts.md +++ b/skills/crowdsec/references/debug/no-alerts.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "metrics show scenarios, whitelist RFC1918 ranges, simulation status, explain replay" +--- + # Debug — Scenarios not firing (no alerts) Canonical docs: · `cscli metrics` diff --git a/skills/crowdsec/references/debug/parsing.md b/skills/crowdsec/references/debug/parsing.md index 68bc329..081714d 100644 --- a/skills/crowdsec/references/debug/parsing.md +++ b/skills/crowdsec/references/debug/parsing.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "metrics show acquisition + explain --log/--file type matching" +--- + # Debug — Logs not being parsed Canonical docs: · `cscli metrics` diff --git a/skills/crowdsec/references/debug/triage.md b/skills/crowdsec/references/debug/triage.md index 975858e..86c59f5 100644 --- a/skills/crowdsec/references/debug/triage.md +++ b/skills/crowdsec/references/debug/triage.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "diagnose.sh support dump + triage funnel commands" +--- + # Debug — First-look triage Use this when the user reports a CrowdSec problem and you don't yet know the shape of it. diff --git a/skills/crowdsec/references/install/docker.md b/skills/crowdsec/references/install/docker.md index 08e463c..0fc3705 100644 --- a/skills/crowdsec/references/install/docker.md +++ b/skills/crowdsec/references/install/docker.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: docker + notes: "compose up, COLLECTIONS install on boot, /logs/auth.log container-path acquisition, 8081:8080 coexistence, bouncers add; teardown -v" +--- + # Install — Docker / docker-compose Canonical docs: · image reference diff --git a/skills/crowdsec/references/operate/health-check.md b/skills/crowdsec/references/operate/health-check.md index cb193ae..1745096 100644 --- a/skills/crowdsec/references/operate/health-check.md +++ b/skills/crowdsec/references/operate/health-check.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "all 3 probes fire (whitelist removed), self-block→403, capi status; fixed remediation:false drift" +--- + # Operate — Health-check Canonical docs: @@ -37,7 +45,7 @@ curl -I https:///crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnl sudo cscli alerts list -s crowdsecurity/http-generic-test ``` -Expected: one row with `kind: crowdsec`, scope `Ip:`. Then a decision appears in `cscli decisions list` (the default profile bans on this alert, since `Remediation: true`). +Expected: one row with `kind: crowdsec`, scope `Ip:`. The test scenarios are deliberately `remediation: false` (`type: trigger`), so they produce an **alert but no ban decision** — the alert itself is the proof the detection chain works, and the probe won't lock you out. End-to-end bouncer enforcement is proven separately in § 5. **Common failure paths** (in order to check): 1. *No row, no parser hit* → the web server's logs aren't being read. `cscli metrics show acquisition` — does your access log show non-zero "Lines read"? If not, see [../configure/acquisition.md](../configure/acquisition.md). @@ -57,7 +65,7 @@ ssh crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnl@ sudo cscli alerts list -s crowdsecurity/ssh-generic-test ``` -Expected: one row with `kind: crowdsec`, ban decision shortly after. +Expected: one row with `kind: crowdsec`. Like the HTTP probe, `ssh-generic-test` is `remediation: false` — an alert appears, but no ban (by design). **Common failure paths:** 1. *No row* → check `cscli metrics show acquisition` for `/var/log/auth.log` (or wherever sshd logs land). On systems using journald-only logging, the file source may be empty — switch to a journald acquisition. diff --git a/skills/crowdsec/references/operate/upgrades.md b/skills/crowdsec/references/operate/upgrades.md index fa5993d..00aa6ad 100644 --- a/skills/crowdsec/references/operate/upgrades.md +++ b/skills/crowdsec/references/operate/upgrades.md @@ -1,3 +1,11 @@ +--- +verified: + - date: 2026-05-22 + version: "1.7.8" + env: systemd + notes: "apt-cache policy (no-op at latest, packagecloud repo, rollback table), hub upgrade, backup paths; non-destructive" +--- + # Operate — Upgrades, backup, rollback Canonical docs: · `cscli` reference