Add red blue security loop
This commit is contained in:
parent
0af1257d86
commit
7f537c4025
12
README.md
12
README.md
|
|
@ -244,6 +244,18 @@ is stale:
|
||||||
|
|
||||||
This keeps local AI answers grounded in repo files and runbooks.
|
This keeps local AI answers grounded in repo files and runbooks.
|
||||||
|
|
||||||
|
## Red/Blue Loop
|
||||||
|
|
||||||
|
Safe red-team/blue-team checks are grouped under one command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./jeannie red-blue plan
|
||||||
|
./jeannie red-blue run --local
|
||||||
|
```
|
||||||
|
|
||||||
|
Local mode runs deterministic prompt-injection and eval checks. Live scans are
|
||||||
|
listed but gated so they can run from the Debian host when intended.
|
||||||
|
|
||||||
## Deploying
|
## Deploying
|
||||||
|
|
||||||
From the Debian server:
|
From the Debian server:
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,18 @@ is stale:
|
||||||
|
|
||||||
This keeps local AI answers grounded in repo files and runbooks.
|
This keeps local AI answers grounded in repo files and runbooks.
|
||||||
|
|
||||||
|
## Red/Blue Loop
|
||||||
|
|
||||||
|
Safe red-team/blue-team checks are grouped under one command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./{{ main_script }} red-blue plan
|
||||||
|
./{{ main_script }} red-blue run --local
|
||||||
|
```
|
||||||
|
|
||||||
|
Local mode runs deterministic prompt-injection and eval checks. Live scans are
|
||||||
|
listed but gated so they can run from the Debian host when intended.
|
||||||
|
|
||||||
## Deploying
|
## Deploying
|
||||||
|
|
||||||
From the Debian server:
|
From the Debian server:
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,10 @@ the public website deployment.
|
||||||
`security/prompt-injection-lab`. Cases cover hostile runbooks, tool output,
|
`security/prompt-injection-lab`. Cases cover hostile runbooks, tool output,
|
||||||
fake dashboard mitigations, and unsafe command arguments.
|
fake dashboard mitigations, and unsafe command arguments.
|
||||||
|
|
||||||
|
`red-blue {plan|run|ledger}`
|
||||||
|
: Plan or run the homelab red-team/blue-team loop. `run --local` executes only
|
||||||
|
deterministic local checks; live security scans remain explicit.
|
||||||
|
|
||||||
Kyverno hardening policies
|
Kyverno hardening policies
|
||||||
: `apps/supply-chain-policy` includes audit-mode checks for privileged pods,
|
: `apps/supply-chain-policy` includes audit-mode checks for privileged pods,
|
||||||
privilege escalation, hostPath use, resource requests/limits, and mutable image
|
privilege escalation, hostPath use, resource requests/limits, and mutable image
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Red-Team / Blue-Team Loop
|
||||||
|
|
||||||
|
This directory defines the safe recurring security loop for the homelab. The
|
||||||
|
first implementation runs local deterministic checks and prints the gated live
|
||||||
|
checks that should run from the Debian host.
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./jeannie red-blue plan
|
||||||
|
./jeannie red-blue run --local
|
||||||
|
```
|
||||||
|
|
||||||
|
The loop is intentionally top-risk oriented: fix, accept, or defer findings in
|
||||||
|
`infra/red-blue-loop/ledger.tsv`.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
id mode command risk
|
||||||
|
prompt_injection local ./jeannie prompt-injection-lab run prompt injection regressions
|
||||||
|
ai_evals local ./jeannie ai-evals run Jeannie/RAG behavior regressions
|
||||||
|
secret_scan live ./jeannie security-secrets secret leakage
|
||||||
|
web_scan live ./jeannie security-web public web exposure
|
||||||
|
k8s_policy live ./jeannie security-k8s Kubernetes hardening drift
|
||||||
|
nuclei live ./jeannie security-nuclei public known-CVE exposure
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
date finding status owner notes
|
||||||
|
2026-06-30 initial-loop accepted jv Loop created; add real findings as fixed, accepted, or deferred.
|
||||||
|
|
||||||
|
8
jeannie
8
jeannie
|
|
@ -6346,6 +6346,10 @@ ai_memory() {
|
||||||
"${REPO_ROOT}/scripts/ai-memory" "${@:2}"
|
"${REPO_ROOT}/scripts/ai-memory" "${@:2}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
red_blue_loop() {
|
||||||
|
"${REPO_ROOT}/scripts/red-blue-loop" "${@:2}"
|
||||||
|
}
|
||||||
|
|
||||||
validate_homelab() {
|
validate_homelab() {
|
||||||
"${REPO_ROOT}/scripts/validate-homelab"
|
"${REPO_ROOT}/scripts/validate-homelab"
|
||||||
}
|
}
|
||||||
|
|
@ -6606,6 +6610,7 @@ Security Learning
|
||||||
security-runtime Check runtime security sensors.
|
security-runtime Check runtime security sensors.
|
||||||
security-attack-path Print prioritized attack-path findings.
|
security-attack-path Print prioritized attack-path findings.
|
||||||
prompt-injection-lab {list|run|show} Run local prompt-injection defense drills.
|
prompt-injection-lab {list|run|show} Run local prompt-injection defense drills.
|
||||||
|
red-blue {plan|run|ledger} Run or plan safe red-team/blue-team loop.
|
||||||
|
|
||||||
AI And Indexing
|
AI And Indexing
|
||||||
ai-index Build the local homelab RAG index.
|
ai-index Build the local homelab RAG index.
|
||||||
|
|
@ -6890,6 +6895,9 @@ case "${1:-}" in
|
||||||
prompt-injection-lab)
|
prompt-injection-lab)
|
||||||
prompt_injection_lab "$@"
|
prompt_injection_lab "$@"
|
||||||
;;
|
;;
|
||||||
|
red-blue)
|
||||||
|
red_blue_loop "$@"
|
||||||
|
;;
|
||||||
openwrt)
|
openwrt)
|
||||||
openwrt
|
openwrt
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ EOF
|
||||||
|
|
||||||
is_read_only_command() {
|
is_read_only_command() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
status|capacity|capacity-advisor|capacity-limits|recover-plan|incident|safety-case|agent-sandbox|scorecard|gitops-status|cert-check|backup-status|synthetic-checks|resource-budget|route-inventory|golden-ledger|workers|change-journal|map|validate|access-audit|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|secrets-check|tailnet-policy-check|ai-check|ask|ai-evals|ai-scheduler|ai-memory|impact|review-last-change|security-scan|security-host|security-trivy|security-secrets|security-nuclei|security-web|security-logs|security-attack-path|prompt-injection-lab|control-plane)
|
status|capacity|capacity-advisor|capacity-limits|recover-plan|incident|safety-case|agent-sandbox|scorecard|gitops-status|cert-check|backup-status|synthetic-checks|resource-budget|route-inventory|golden-ledger|workers|change-journal|map|validate|access-audit|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|secrets-check|tailnet-policy-check|ai-check|ask|ai-evals|ai-scheduler|ai-memory|impact|review-last-change|security-scan|security-host|security-trivy|security-secrets|security-nuclei|security-web|security-logs|security-attack-path|prompt-injection-lab|red-blue|control-plane)
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Run or plan the homelab red-team/blue-team loop."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import csv
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
CHECKS_FILE = REPO_ROOT / "infra" / "red-blue-loop" / "checks.tsv"
|
||||||
|
LEDGER_FILE = REPO_ROOT / "infra" / "red-blue-loop" / "ledger.tsv"
|
||||||
|
|
||||||
|
|
||||||
|
def checks() -> list[dict[str, str]]:
|
||||||
|
with CHECKS_FILE.open(encoding="utf-8", newline="") as handle:
|
||||||
|
return list(csv.DictReader(handle, delimiter="\t"))
|
||||||
|
|
||||||
|
|
||||||
|
def plan() -> int:
|
||||||
|
print("Red/Blue Loop")
|
||||||
|
print("=============")
|
||||||
|
for row in checks():
|
||||||
|
print(f"{row['mode']:5} {row['id']:18} {row['command']}")
|
||||||
|
print(f" risk: {row['risk']}")
|
||||||
|
print()
|
||||||
|
print(f"Ledger: {LEDGER_FILE.relative_to(REPO_ROOT)}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def run(local_only: bool) -> int:
|
||||||
|
failures = 0
|
||||||
|
selected = [row for row in checks() if row["mode"] == "local" or not local_only]
|
||||||
|
print("Red/Blue Loop Run")
|
||||||
|
print("=================")
|
||||||
|
for row in selected:
|
||||||
|
print(f"\n== {row['id']} ==")
|
||||||
|
if row["mode"] != "local" and local_only:
|
||||||
|
print("skip: live check gated")
|
||||||
|
continue
|
||||||
|
process = subprocess.run(
|
||||||
|
row["command"].split(),
|
||||||
|
cwd=REPO_ROOT,
|
||||||
|
text=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
print(process.stdout.rstrip())
|
||||||
|
if process.returncode != 0:
|
||||||
|
failures += 1
|
||||||
|
print()
|
||||||
|
print(f"failures={failures}")
|
||||||
|
print(f"ledger={LEDGER_FILE.relative_to(REPO_ROOT)}")
|
||||||
|
return 1 if failures else 0
|
||||||
|
|
||||||
|
|
||||||
|
def ledger() -> int:
|
||||||
|
print(LEDGER_FILE.read_text(encoding="utf-8").rstrip())
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||||
|
subparsers.add_parser("plan")
|
||||||
|
run_parser = subparsers.add_parser("run")
|
||||||
|
run_parser.add_argument("--local", action="store_true", help="Run only deterministic local checks.")
|
||||||
|
subparsers.add_parser("ledger")
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.command == "plan":
|
||||||
|
return plan()
|
||||||
|
if args.command == "run":
|
||||||
|
return run(local_only=args.local)
|
||||||
|
if args.command == "ledger":
|
||||||
|
return ledger()
|
||||||
|
return 2
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Loading…
Reference in New Issue