From b87993e23daec247a38faed298d0997831391765 Mon Sep 17 00:00:00 2001 From: kaushik-kumaran Date: Sat, 18 Jul 2026 01:06:03 -0500 Subject: [PATCH 1/2] feat(cluster): add guarded one-command threat demo Add exact-context confirmation, read-only preflight, dependency readiness gates, isolated bounded workloads, evidence collection, scoped cleanup, and safety regression tests for production-grade Kubernetes demos.\n\nCloses #98 --- Makefile | 22 ++++- README.md | 14 +++- cluster/test-diverse-threats.sh | 32 ++++---- docs/GETTING_STARTED.md | 35 ++++++-- scripts/demo-cluster.sh | 125 +++++++++++++++++++++++++++++ scripts/tests/test-demo-cluster.sh | 26 ++++++ 6 files changed, 228 insertions(+), 26 deletions(-) create mode 100644 scripts/demo-cluster.sh create mode 100644 scripts/tests/test-demo-cluster.sh diff --git a/Makefile b/Makefile index 8006a19..3541ef8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: help cluster-up cluster-down cluster-status deploy-cilium deploy-falco \ deploy-kyverno deploy-observability deploy-agent deploy-ui \ - setup-local dev-agent dev-ui demo-local hubble-ui grafana-ui k9s \ - test test-agent test-ui simulate-threats clean + setup-local dev-agent dev-ui demo-local demo-cluster demo-cluster-dry-run hubble-ui grafana-ui k9s \ + test test-agent test-ui test-cluster-demo simulate-threats clean THREAT_COUNT ?= 10 THREAT_SCENARIO ?= mixed @@ -16,6 +16,8 @@ help: @echo " make cluster-up Provision VMs, install k3s, Cilium, namespaces" @echo " make cluster-down Stop all OrbStack VMs" @echo " make cluster-status Show node and pod status" + @echo " make demo-cluster Run guarded real-cluster threat demo" + @echo " make demo-cluster-dry-run Validate cluster prerequisites without changes" @echo "" @echo " Security" @echo " make deploy-falco Install Falco via Helm" @@ -34,6 +36,7 @@ help: @echo "" @echo " Utilities" @echo " make test Run agent tests and build the UI" + @echo " make test-cluster-demo Test cluster-demo safety guards" @echo " make simulate-threats Generate randomized demo incidents" @echo " make hubble-ui Open Hubble network flow UI" @echo " make grafana-ui Port-forward Grafana to localhost:3000" @@ -109,6 +112,18 @@ dev-ui: demo-local: @bash scripts/demo-local.sh "$(THREAT_COUNT)" "$(THREAT_SCENARIO)" "$(THREAT_SEED)" +demo-cluster: + @DEMO_CLUSTER_CONTEXT="$(DEMO_CLUSTER_CONTEXT)" \ + DEMO_NAMESPACE="$(or $(DEMO_NAMESPACE),argus-demo)" \ + DEMO_WAIT_SECONDS="$(or $(DEMO_WAIT_SECONDS),30)" \ + DEMO_KEEP_RESOURCES="$(or $(DEMO_KEEP_RESOURCES),false)" \ + bash scripts/demo-cluster.sh + +demo-cluster-dry-run: + @DEMO_NAMESPACE="$(or $(DEMO_NAMESPACE),argus-demo)" \ + DEMO_WAIT_SECONDS="$(or $(DEMO_WAIT_SECONDS),30)" \ + bash scripts/demo-cluster.sh --dry-run + test: test-agent test-ui test-agent: @@ -117,6 +132,9 @@ test-agent: test-ui: @npm --prefix ui run build +test-cluster-demo: + @bash scripts/tests/test-demo-cluster.sh + simulate-threats: @curl --fail --silent http://localhost:8000/health >/dev/null || \ (echo "Argus backend is not running on localhost:8000."; \ diff --git a/README.md b/README.md index fe56a84..d32af50 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,19 @@ command, expected result, testing mode, and troubleshooting path, follow the | Mode | Command | Cluster required? | Evidence | |---|---|---:|---| | Local synthetic | `make demo-local` | No | Generated incidents for console and workflow testing | -| Full Kubernetes | `bash cluster/test-diverse-threats.sh` | Yes | Real Falco, Cilium, and Kyverno signals | +| Full Kubernetes | `make demo-cluster` | Yes | Real Falco, Cilium, Kyverno, and Argus evidence | + +For a guarded, one-command run against an existing cluster with Cilium, Falco, +Kyverno, and Argus already installed: + +```bash +make demo-cluster +``` + +The command displays the active context and requires you to type it exactly before +creating anything. It uses a dedicated `argus-demo` namespace, collects runtime +evidence, and deletes that namespace on exit. Run `make demo-cluster-dry-run` for a +read-only prerequisite check. ## Part of the Sentinel multi-agent platform diff --git a/cluster/test-diverse-threats.sh b/cluster/test-diverse-threats.sh index b68eaa2..7fb88c7 100755 --- a/cluster/test-diverse-threats.sh +++ b/cluster/test-diverse-threats.sh @@ -1,17 +1,19 @@ -#!/bin/bash -set -e +#!/usr/bin/env bash +set -Eeuo pipefail + +namespace="${DEMO_NAMESPACE:-argus-demo}" echo "🎯 Creating diverse security threats for Argus detection..." echo "These pods comply with Kyverno policies but trigger Falco runtime rules" echo "" # Clean up any existing threat pods first -kubectl delete pods -l 'threat-type' --force --grace-period=0 2>/dev/null || true +kubectl delete pods -n "$namespace" -l 'threat-type' --force --grace-period=0 2>/dev/null || true sleep 2 # 1. Shell spawn in container (Falco will detect shell execution) echo "1️⃣ Creating pod that spawns shell..." -cat </dev/null || true; sleep 10; done"] securityContext: @@ -77,7 +79,7 @@ EOF # 3. Process execution patterns echo "3️⃣ Creating pod with suspicious process execution..." -cat </dev/null || ps; ls -la /proc 2>/dev/null || true; sleep 20; done"] securityContext: @@ -110,7 +112,7 @@ EOF # 4. File modification attempts echo "4️⃣ Creating pod that attempts file modifications..." -cat < /tmp/suspicious-file; rm /tmp/suspicious-file 2>/dev/null || true; sleep 8; done"] securityContext: @@ -143,7 +145,7 @@ EOF # 5. Network activity simulation echo "5️⃣ Creating pod with network activity..." -cat </dev/null || true; sleep 15; done"] securityContext: @@ -187,7 +189,7 @@ echo "🔍 Check the Argus UI Command Center for detections" echo "⏱️ Pods will run continuously until deleted" echo "" echo "To view pod status:" -echo " kubectl get pods -l 'threat-type'" +echo " kubectl get pods -n $namespace -l 'threat-type'" echo "" echo "To clean up all threat pods:" -echo " kubectl delete pods -l 'threat-type' --force --grace-period=0" +echo " kubectl delete namespace $namespace" diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index 211cc44..f9b737d 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -99,19 +99,38 @@ The UI proxies `/api/*` to the agent at `http://127.0.0.1:8000`. ## Full-cluster mode -Full-cluster mode provisions three k3s nodes on OrbStack and deploys Cilium, Falco, -Kyverno, Prometheus, Grafana, Loki, and Argus. Follow [setup.md](../setup.md) for the -complete prerequisites and deployment sequence. +The guarded demo works with an existing Kubernetes cluster where Cilium, Falco, +Kyverno, and Argus are already installed. The repository also includes an OrbStack/k3s +development-cluster bootstrap; follow [setup.md](../setup.md) when you need that stack. -After the stack is healthy, create real policy-compliant workloads that trigger runtime -rules: +After the stack is healthy, run the guarded cluster demo: ```bash -bash cluster/test-diverse-threats.sh +make demo-cluster ``` -This script requires a working `kubectl` context. It creates real pods and runs continuously -until those pods are deleted. +The command prints the active Kubernetes context and API server, then requires the exact +context name before it creates resources. It validates node, Cilium, Falco, Kyverno, and +Argus readiness; launches bounded workloads in an isolated `argus-demo` namespace; +collects evidence; and removes the namespace on exit. + +Run the read-only preflight first when using a new cluster: + +```bash +make demo-cluster-dry-run +``` + +For non-interactive execution, bind authorization to the exact context: + +```bash +make demo-cluster DEMO_CLUSTER_CONTEXT="$(kubectl config current-context)" +``` + +Retain evidence workloads only when deliberately requested: + +```bash +make demo-cluster DEMO_KEEP_RESOURCES=true +``` ## Troubleshooting diff --git a/scripts/demo-cluster.sh b/scripts/demo-cluster.sh new file mode 100644 index 0000000..e6259da --- /dev/null +++ b/scripts/demo-cluster.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +namespace="${DEMO_NAMESPACE:-argus-demo}" +wait_seconds="${DEMO_WAIT_SECONDS:-30}" +keep_resources="${DEMO_KEEP_RESOURCES:-false}" +confirmed_context="${DEMO_CLUSTER_CONTEXT:-}" +dry_run=false + +usage() { + cat <<'EOF' +Usage: scripts/demo-cluster.sh [--dry-run] [--keep] + +Environment: + DEMO_CLUSTER_CONTEXT Exact kubectl context; skips the interactive prompt + DEMO_NAMESPACE Isolated namespace (default: argus-demo) + DEMO_WAIT_SECONDS Evidence collection window (default: 30) + DEMO_KEEP_RESOURCES true to retain the demo namespace +EOF +} + +for arg in "$@"; do + case "$arg" in + --dry-run) dry_run=true ;; + --keep) keep_resources=true ;; + --help|-h) usage; exit 0 ;; + *) echo "Unknown argument: $arg" >&2; usage >&2; exit 2 ;; + esac +done + +case "$namespace" in + default|kube-system|kube-public|kube-node-lease|argus-system|kyverno) + echo "Refusing unsafe demo namespace: $namespace" >&2; exit 2 ;; +esac +[[ "$namespace" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]] || { + echo "DEMO_NAMESPACE must be a valid DNS label" >&2; exit 2; +} +[[ "$wait_seconds" =~ ^[0-9]+$ ]] && (( wait_seconds >= 5 && wait_seconds <= 300 )) || { + echo "DEMO_WAIT_SECONDS must be between 5 and 300" >&2; exit 2; +} + +for command in kubectl; do + command -v "$command" >/dev/null || { echo "Missing required command: $command" >&2; exit 1; } +done + +context="$(kubectl config current-context)" +server="$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')" + +echo "Argus guarded cluster demo" +echo " Context: $context" +echo " API: $server" +echo " Namespace: $namespace" + +if [[ "$dry_run" == true ]]; then + echo "DRY RUN: preflight only; no cluster resources will be changed." +elif [[ -n "$confirmed_context" ]]; then + [[ "$confirmed_context" == "$context" ]] || { + echo "DEMO_CLUSTER_CONTEXT does not match the active context '$context'" >&2; exit 2; + } +elif [[ -t 0 ]]; then + read -r -p "Type the exact context name to authorize bounded demo resources: " confirmed_context + [[ "$confirmed_context" == "$context" ]] || { echo "Context confirmation failed." >&2; exit 2; } +else + echo "Non-interactive runs require DEMO_CLUSTER_CONTEXT='$context'" >&2; exit 2 +fi + +echo "==> Checking Kubernetes API and node readiness" +kubectl --request-timeout=10s get --raw=/readyz >/dev/null +kubectl wait --for=condition=Ready nodes --all --timeout=30s >/dev/null + +require_ready_workload() { + local label="$1" target_namespace="$2" description="$3" count + count="$(kubectl get pods -n "$target_namespace" -l "$label" --field-selector=status.phase=Running --no-headers 2>/dev/null | wc -l | tr -d ' ' || true)" + [[ "$count" != "0" ]] || { + echo "Required component is not running: $description ($target_namespace, $label)" >&2; exit 1; + } + kubectl wait -n "$target_namespace" --for=condition=Ready pods -l "$label" --timeout=60s >/dev/null + echo " ready: $description ($count pod(s))" +} + +echo "==> Validating the installed security stack" +require_ready_workload "k8s-app=cilium" "kube-system" "Cilium" +require_ready_workload "app.kubernetes.io/name=falco" "kube-system" "Falco" +require_ready_workload "app.kubernetes.io/component=admission-controller" "kyverno" "Kyverno" +require_ready_workload "app=argus-agent" "argus-system" "Argus agent" + +if [[ "$dry_run" == true ]]; then + echo "Preflight passed. Re-run without --dry-run to execute the demo." + exit 0 +fi + +cleanup() { + if [[ "$keep_resources" == true ]]; then + echo "Keeping namespace $namespace (DEMO_KEEP_RESOURCES=true)." + else + echo "==> Cleaning up isolated demo namespace" + kubectl delete namespace "$namespace" --wait=false --ignore-not-found >/dev/null + fi +} +trap cleanup EXIT +trap 'exit 130' INT +trap 'exit 143' TERM + +echo "==> Creating isolated namespace" +kubectl create namespace "$namespace" --dry-run=client -o yaml | kubectl apply -f - >/dev/null +kubectl label namespace "$namespace" argus.io/demo=true --overwrite >/dev/null + +echo "==> Launching bounded threat workloads" +DEMO_NAMESPACE="$namespace" bash cluster/test-diverse-threats.sh + +echo "==> Waiting for workloads and telemetry" +kubectl wait -n "$namespace" --for=condition=Ready pods -l threat-type --timeout=90s >/dev/null +sleep "$wait_seconds" + +echo ""; echo "==> Workload evidence" +kubectl get pods -n "$namespace" -l threat-type -o wide +echo ""; echo "==> Recent Falco evidence" +kubectl logs -n kube-system -l app.kubernetes.io/name=falco --since=5m --tail=80 --prefix=true || true +echo ""; echo "==> Recent Argus evidence" +kubectl logs -n argus-system -l app=argus-agent --since=5m --tail=80 --prefix=true || true +echo ""; echo "==> Kyverno policy evidence" +kubectl get policyreport -n "$namespace" 2>/dev/null || echo "No namespaced PolicyReport resource is available." +echo ""; echo "==> Cilium flow command" +echo "hubble observe --namespace $namespace --since 5m" +echo ""; echo "Demo completed. Review the evidence above and the Argus console." diff --git a/scripts/tests/test-demo-cluster.sh b/scripts/tests/test-demo-cluster.sh new file mode 100644 index 0000000..464fc53 --- /dev/null +++ b/scripts/tests/test-demo-cluster.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +script="$repo_root/scripts/demo-cluster.sh" + +expect_failure() { + local description="$1" + shift + if "$@" >/dev/null 2>&1; then + echo "FAIL: $description" >&2 + exit 1 + fi +} + +bash -n "$script" +bash -n "$repo_root/cluster/test-diverse-threats.sh" +bash "$script" --help >/dev/null +expect_failure "rejects default namespace" env DEMO_NAMESPACE=default bash "$script" --dry-run +expect_failure "rejects system namespace" env DEMO_NAMESPACE=kube-system bash "$script" --dry-run +expect_failure "rejects invalid DNS label" env DEMO_NAMESPACE=Bad_Name bash "$script" --dry-run +expect_failure "rejects short evidence window" env DEMO_WAIT_SECONDS=1 bash "$script" --dry-run +expect_failure "rejects long evidence window" env DEMO_WAIT_SECONDS=301 bash "$script" --dry-run +expect_failure "rejects unknown arguments" bash "$script" --destroy-everything + +echo "demo-cluster guard tests passed" From eb77222ec748ae2a54e0e59ee3ea452883714699 Mon Sep 17 00:00:00 2001 From: kaushik-kumaran Date: Sat, 18 Jul 2026 01:26:07 -0500 Subject: [PATCH 2/2] docs(cluster): pave the OrbStack k3s demo path Document the built-in OrbStack versus Argus k3s contexts, safe cluster reuse, live component expectations, real demo lifecycle, manual UI access, known bootstrap constraints, and observed troubleshooting paths.\n\nRefs #98 --- README.md | 98 ++++++++++++++--------------- cluster/test-diverse-threats.sh | 2 +- docs/GETTING_STARTED.md | 27 ++++---- setup.md | 108 +++++++++++++++++++++++++++++--- 4 files changed, 162 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index d32af50..77d38a4 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,40 @@ creating anything. It uses a dedicated `argus-demo` namespace, collects runtime evidence, and deletes that namespace on exit. Run `make demo-cluster-dry-run` for a read-only prerequisite check. +### Full k3s demo — existing OrbStack cluster + +OrbStack exposes its own single-node Kubernetes context named `orbstack`. That is +**not** the Argus cluster. Argus runs on three Ubuntu VMs managed by OrbStack and uses +the kubeconfig context named `argus`. + +```text +OrbStack application +├── built-in Kubernetes → context: orbstack → node: orbstack +└── Linux machines + ├── k3s-master ┐ + ├── k3s-worker1 ├→ context: argus → real three-node k3s cluster + └── k3s-worker2 ┘ +``` + +If those three machines already exist, do **not** run `make cluster-up` again. Select +and verify the existing cluster: + +```bash +orb list +kubectl config get-contexts +kubectl config use-context argus +kubectl get nodes -o wide +make demo-cluster-dry-run +make demo-cluster +``` + +Expected preflight components are three ready k3s nodes plus Cilium, Falco, Kyverno, +and the Argus agent. At the confirmation prompt, type the exact context printed by the +command (`argus` for this repository's OrbStack cluster). + +`make demo-cluster` currently collects real cluster evidence in the terminal. It does +not yet launch the React console; cluster UI startup is the next integration step. + ## Part of the Sentinel multi-agent platform Argus is the security domain agent in a larger autonomous-infrastructure system. It @@ -122,9 +156,9 @@ The Kubernetes deployment uses the in-cluster Sentinel service address automatic | Node | Role | IP | Status | |---|---|---|---| -| k3s-master | Control plane | 192.168.139.42 | Active | -| k3s-worker1 | Worker | 192.168.139.77 | Active | -| k3s-worker2 | Worker | 192.168.139.45 | Active | +| k3s-master | Control plane | 192.168.139.42 | Ready — k3s v1.34.6 | +| k3s-worker1 | Worker | 192.168.139.77 | Ready — k3s v1.34.6 | +| k3s-worker2 | Worker | 192.168.139.45 | Ready — k3s v1.34.6 | **Cilium:** v1.15.0 — eBPF mode, kube-proxy replacement enabled **Hubble:** Relay + UI enabled — live network flow observability active @@ -290,57 +324,17 @@ Kyverno catches a separate category of threat — workloads that violate securit Start with the cluster-free [Quickstart](#quickstart--no-cluster-required) unless you specifically need real kernel, network, and admission-control evidence. -### Prerequisites -- macOS (Apple Silicon M-series) -- OrbStack installed (`brew install orbstack`) -- CLI tools: `brew install kubectl helm k3sup cilium-cli hubble k9s` - -### Spin up the cluster -```bash -make cluster-up -``` +Use exactly one of these paths: -This provisions 3 OrbStack VMs, installs k3s, deploys Cilium with eBPF kube-proxy replacement, enables Hubble, and creates all namespaces. - -### Verify the cluster -```bash -make cluster-status -cilium hubble ui -``` - -### Run services separately - -**Backend agent:** -```bash -make dev-agent -``` - -**Console UI:** -```bash -make dev-ui -``` - -The UI runs at `http://localhost:5173` and proxies `/api/*` to the agent at port 8000. - -The backend must be running before generating incidents. To populate the console: -```bash -make simulate-threats THREAT_COUNT=10 THREAT_SCENARIO=mixed -``` - -The generator randomizes threat type, workload, blast radius, enrichment evidence, and -recommended response. Use a seed to replay the exact same selection during tests or a -recorded demo: - -```bash -make simulate-threats \ - THREAT_COUNT=10 \ - THREAT_SCENARIO=mixed \ - THREAT_SEED=20260717 -``` +- Existing `k3s-*` OrbStack machines: select `argus`, run the dry-run, then run + `make demo-cluster`. +- Fresh Apple Silicon workstation: review the environment-specific bootstrap variables, + run `make cluster-up`, deploy the required security components, then run the dry-run. -Supported scenarios are `mixed`, `human_approval`, and `attack_chain`. The response -includes severity and action distributions plus the effective seed. Omitting `seed` -generates a new random run; reusing the returned seed reproduces its incident selection. +The complete commands, expected output, context explanation, manual cluster UI path, +and troubleshooting guide are in [setup.md](setup.md). Do not use `make dev-agent` or +`make simulate-threats` as substitutes for real-cluster evidence; those commands run +the local synthetic application path. ## Architecture decisions diff --git a/cluster/test-diverse-threats.sh b/cluster/test-diverse-threats.sh index 7fb88c7..1be88d0 100755 --- a/cluster/test-diverse-threats.sh +++ b/cluster/test-diverse-threats.sh @@ -186,7 +186,7 @@ echo " - File modification patterns" echo " - Network activity" echo "" echo "🔍 Check the Argus UI Command Center for detections" -echo "⏱️ Pods will run continuously until deleted" +echo "⏱️ The demo-cluster wrapper observes these pods, then deletes the demo namespace" echo "" echo "To view pod status:" echo " kubectl get pods -n $namespace -l 'threat-type'" diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index f9b737d..8625d3e 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -103,6 +103,18 @@ The guarded demo works with an existing Kubernetes cluster where Cilium, Falco, Kyverno, and Argus are already installed. The repository also includes an OrbStack/k3s development-cluster bootstrap; follow [setup.md](../setup.md) when you need that stack. +OrbStack's built-in `orbstack` Kubernetes context is separate from the three-node k3s +cluster. Select the Argus context before preflight: + +```bash +kubectl config get-contexts +kubectl config use-context argus +kubectl get nodes -o wide +``` + +The expected nodes are `k3s-master`, `k3s-worker1`, and `k3s-worker2`. If the output +contains one node named `orbstack`, the wrong cluster is selected. + After the stack is healthy, run the guarded cluster demo: ```bash @@ -120,18 +132,6 @@ Run the read-only preflight first when using a new cluster: make demo-cluster-dry-run ``` -For non-interactive execution, bind authorization to the exact context: - -```bash -make demo-cluster DEMO_CLUSTER_CONTEXT="$(kubectl config current-context)" -``` - -Retain evidence workloads only when deliberately requested: - -```bash -make demo-cluster DEMO_KEEP_RESOURCES=true -``` - ## Troubleshooting | Symptom | Cause | Fix | @@ -142,6 +142,9 @@ make demo-cluster DEMO_KEEP_RESOURCES=true | Console opens but has no incidents | The backend was restarted or never seeded | Run `make simulate-threats` | | Cluster panels show no live data | Local synthetic mode has no Kubernetes telemetry | Use full-cluster mode for real telemetry | | `kubectl` connection failure | Full-cluster mode is not configured | Complete [setup.md](../setup.md) | +| Cilium missing while the three OrbStack VMs are running | The `orbstack` context is selected instead of the k3s cluster | Run `kubectl config use-context argus`, then repeat the dry run | +| `make cluster-up` reports that machines already exist | The k3s VMs are already provisioned | Do not recreate them; select `argus` and run `make demo-cluster-dry-run` | +| Cluster demo finishes without opening a browser | The current cluster runner collects terminal evidence only | Use the manual port-forward in [setup.md](../setup.md); integrated UI startup is pending | ## Verify the repository diff --git a/setup.md b/setup.md index b64055e..6a6aab7 100644 --- a/setup.md +++ b/setup.md @@ -20,13 +20,41 @@ ANTHROPIC_API_KEY=your_key_here --- -## 1. Cluster setup +## 1. Choose fresh setup or existing cluster + +OrbStack has a built-in Kubernetes cluster whose context is normally `orbstack`. +Argus does not use it. Argus uses a separate, real three-node k3s cluster running +inside the `k3s-master`, `k3s-worker1`, and `k3s-worker2` OrbStack machines. + +Inspect what already exists before provisioning anything: + +```bash +orb list +kubectl config get-contexts +``` + +If the three `k3s-*` machines and the `argus` context already exist, reuse them: + +```bash +kubectl config use-context argus +kubectl get nodes -o wide +make demo-cluster-dry-run +``` + +Do not run `make cluster-up` against an existing installation. The bootstrap scripts +create fixed machine names and are not an idempotent upgrade workflow. + +## 2. Fresh cluster setup ### Provision the VMs and install k3s ```bash make cluster-up ``` +The bootstrap is designed for this repository owner's current OrbStack layout. Before +using it on another workstation, review the username, SSH key, and IP variables in +`cluster/bootstrap/*.sh`; they are currently environment-specific. + This runs four steps in sequence: 1. Provisions three OrbStack VMs (`k3s-master`, `k3s-worker1`, `k3s-worker2`) running Ubuntu 22.04 ARM64 2. Installs k3s on the master node with `--flannel-backend=none` (Cilium takes over networking) @@ -35,11 +63,15 @@ This runs four steps in sequence: ### Verify the cluster is healthy ```bash +kubectl config use-context argus make cluster-status ``` Expected output: 3 nodes in Ready state, Cilium status shows all agents operational. +If you see one node named `orbstack`, you selected OrbStack's built-in Kubernetes +context. Switch back with `kubectl config use-context argus`. + ### Open the Hubble network flow UI ```bash cilium hubble ui @@ -47,7 +79,7 @@ cilium hubble ui --- -## 2. Security stack +## 3. Security stack ### Deploy Falco (runtime threat detection) ```bash @@ -68,11 +100,14 @@ Installs Kyverno and applies three policies: --- -## 3. Observability +## 4. Observability + +The repository's `make deploy-observability` target is currently a placeholder. Do not +rely on it for a fresh installation. On the maintained Argus cluster, verify the +existing Prometheus, Grafana, Loki, and Promtail workloads with: -### Deploy Prometheus, Grafana, and Loki ```bash -make deploy-observability +kubectl get pods -n monitoring ``` ### Access Grafana @@ -83,7 +118,7 @@ make grafana-ui --- -## 4. Detection agent +## 5. Detection agent ### Deploy to cluster ```bash @@ -112,7 +147,27 @@ The agent exposes these endpoints: --- -## 5. Console UI +## 6. Run the guarded real-cluster demo + +First perform the read-only preflight: + +```bash +kubectl config use-context argus +make demo-cluster-dry-run +``` + +It must report ready Cilium, Falco, Kyverno, and Argus components. Then run: + +```bash +make demo-cluster +``` + +Type `argus` when asked to confirm the active context. The command creates only the +`argus-demo` namespace, launches bounded workloads, waits for telemetry, prints real +Falco/Argus/Kyverno evidence, and deletes the namespace on exit. `Ctrl-C` also runs the +scoped cleanup. + +## 7. Console UI ### Install dependencies and start ```bash @@ -123,7 +178,19 @@ npm run dev The console runs at `http://localhost:5173`. All `/api/*` requests are proxied to the agent at `http://localhost:8000`. -### Populate with sample data +For a cluster agent, first forward the in-cluster service in a separate terminal: + +```bash +kubectl port-forward -n argus-system svc/argus-agent 8000:80 +``` + +Cluster UI startup is not yet integrated into `make demo-cluster`. + +### Optional: populate the local synthetic API + +This endpoint creates synthetic UI data. It is not evidence from Falco, Cilium, or +Kyverno and is not part of the full-cluster demo. + ```bash curl -X POST http://localhost:8000/simulate-threats \ -H "Content-Type: application/json" \ @@ -168,3 +235,28 @@ The Kyverno admission webhook must be ready before any workload deployments. Wai **Cilium not ready after cluster-up** Cilium takes 60–90 seconds to initialize on first install. Run `cilium status --wait` to block until all agents are healthy. + +**`demo-cluster-dry-run` says Cilium is missing, but the k3s VMs are running** + +Check the selected context: + +```bash +kubectl config current-context +kubectl config get-contexts +``` + +If it prints `orbstack`, that is the separate built-in cluster. Run: + +```bash +kubectl config use-context argus +make demo-cluster-dry-run +``` + +**Threat pods remain after an interrupted manual test** + +The guarded runner deletes its namespace automatically. For manual recovery, delete +only the labelled demo namespace: + +```bash +kubectl delete namespace argus-demo --ignore-not-found +```