Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ 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 Run real-cluster threats and console"
@echo " make demo-cluster-dry-run Validate cluster prerequisites without changes"
@echo ""
@echo " Security"
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,10 @@ Expected preflight components are three ready k3s nodes plus Cilium, Falco, Kyve
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.
`make demo-cluster` starts a supervised service port-forward and the React console,
injects the real workloads, prints terminal evidence, and keeps the console available
at **http://127.0.0.1:5173** until `Ctrl-C`. Cleanup stops both local processes and
deletes only the namespace created by that run.

## Part of the Sentinel multi-agent platform

Expand Down
5 changes: 3 additions & 2 deletions docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ make demo-cluster
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.
starts the React console against the in-cluster agent; collects evidence; and remains
available until `Ctrl-C` removes the namespace and stops the supervised local processes.

Run the read-only preflight first when using a new cluster:

Expand All @@ -144,7 +145,7 @@ make demo-cluster-dry-run
| `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 |
| Cluster console URL does not respond | Port 8000 or 5173 is occupied, or the service port-forward exited | Stop the conflicting local process and rerun `make demo-cluster`; startup prints the failed child-process log |

## Verify the repository

Expand Down
100 changes: 98 additions & 2 deletions scripts/demo-cluster.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
#!/usr/bin/env bash
set -Eeuo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
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
port_forward_pid=""
ui_pid=""
port_forward_log=""
ui_log=""
cleanup_started=false
namespace_created=false

usage() {
cat <<'EOF'
Expand Down Expand Up @@ -43,6 +50,12 @@ for command in kubectl; do
command -v "$command" >/dev/null || { echo "Missing required command: $command" >&2; exit 1; }
done

if [[ "$dry_run" != true ]]; then
for command in curl npm; do
command -v "$command" >/dev/null || { echo "Missing required command: $command" >&2; exit 1; }
done
fi

context="$(kubectl config current-context)"
server="$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')"

Expand Down Expand Up @@ -90,19 +103,91 @@ if [[ "$dry_run" == true ]]; then
fi

cleanup() {
if [[ "$keep_resources" == true ]]; then
[[ "$cleanup_started" == false ]] || return
cleanup_started=true

if [[ -n "$ui_pid" ]]; then
kill "$ui_pid" 2>/dev/null || true
wait "$ui_pid" 2>/dev/null || true
fi
if [[ -n "$port_forward_pid" ]]; then
kill "$port_forward_pid" 2>/dev/null || true
wait "$port_forward_pid" 2>/dev/null || true
fi
if [[ "$namespace_created" != true ]]; then
:
elif [[ "$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
if [[ -n "$port_forward_log" ]]; then rm -f "$port_forward_log"; fi
if [[ -n "$ui_log" ]]; then rm -f "$ui_log"; fi
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

if kubectl get namespace "$namespace" >/dev/null 2>&1; then
echo "Namespace $namespace already exists; refusing to reuse or delete it." >&2
echo "Choose another DEMO_NAMESPACE or inspect and remove the existing namespace yourself." >&2
exit 1
fi

if curl --fail --silent --max-time 2 http://127.0.0.1:8000/health >/dev/null 2>&1; then
echo "Port 8000 already has an HTTP service. Stop it before running make demo-cluster." >&2
exit 1
fi
if curl --fail --silent --max-time 2 http://127.0.0.1:5173 >/dev/null 2>&1; then
echo "Port 5173 already has an HTTP service. Stop it before running make demo-cluster." >&2
exit 1
fi

if [[ ! -d "$repo_root/ui/node_modules" ]]; then
echo "==> Installing missing UI dependencies"
npm --prefix "$repo_root/ui" ci
fi

wait_for_url() {
local url="$1" process_id="$2" description="$3" log_file="$4"
for _attempt in $(seq 1 30); do
if curl --fail --silent --max-time 2 "$url" >/dev/null 2>&1; then
echo " ready: $description"
return 0
fi
if ! kill -0 "$process_id" 2>/dev/null; then
echo "$description exited before becoming ready:" >&2
tail -30 "$log_file" >&2 || true
return 1
fi
sleep 1
done
echo "$description did not become ready within 30 seconds:" >&2
tail -30 "$log_file" >&2 || true
return 1
}

port_forward_log="$(mktemp -t argus-port-forward.XXXXXX)"
ui_log="$(mktemp -t argus-ui.XXXXXX)"

echo "==> Connecting the local console to the in-cluster Argus agent"
kubectl port-forward --address 127.0.0.1 -n argus-system svc/argus-agent 8000:80 >"$port_forward_log" 2>&1 &
port_forward_pid=$!
wait_for_url "http://127.0.0.1:8000/health" "$port_forward_pid" "Argus service port-forward" "$port_forward_log"

echo "==> Starting the Argus console"
npm --prefix "$repo_root/ui" run dev -- --host 127.0.0.1 >"$ui_log" 2>&1 &
ui_pid=$!
wait_for_url "http://127.0.0.1:5173/api/health" "$ui_pid" "Argus console and API proxy" "$ui_log"

echo ""
echo "Argus cluster console: http://127.0.0.1:5173"
echo ""

echo "==> Creating isolated namespace"
kubectl create namespace "$namespace" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
namespace_created=true
kubectl label namespace "$namespace" argus.io/demo=true --overwrite >/dev/null

echo "==> Launching bounded threat workloads"
Expand All @@ -122,4 +207,15 @@ 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."
echo ""; echo "Demo evidence collected. The real-cluster console remains available at:"
echo " http://127.0.0.1:5173"
echo "Press Ctrl-C to stop the console, port-forward, and demo workloads."

while kill -0 "$ui_pid" 2>/dev/null && kill -0 "$port_forward_pid" 2>/dev/null; do
sleep 2
done

echo "A supervised demo process exited unexpectedly." >&2
if ! kill -0 "$ui_pid" 2>/dev/null; then tail -30 "$ui_log" >&2 || true; fi
if ! kill -0 "$port_forward_pid" 2>/dev/null; then tail -30 "$port_forward_log" >&2 || true; fi
exit 1
5 changes: 5 additions & 0 deletions scripts/tests/test-demo-cluster.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,9 @@ expect_failure "rejects short evidence window" env DEMO_WAIT_SECONDS=1 bash "$sc
expect_failure "rejects long evidence window" env DEMO_WAIT_SECONDS=301 bash "$script" --dry-run
expect_failure "rejects unknown arguments" bash "$script" --destroy-everything

grep -q 'kubectl port-forward --address 127.0.0.1' "$script"
grep -q 'http://127.0.0.1:5173/api/health' "$script"
grep -q 'namespace_created=true' "$script"
grep -q 'Press Ctrl-C to stop the console' "$script"

echo "demo-cluster guard tests passed"
10 changes: 5 additions & 5 deletions setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,9 @@ 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.
Falco/Argus/Kyverno evidence, forwards the in-cluster agent, and starts the React
console at [http://127.0.0.1:5173](http://127.0.0.1:5173). It remains active until
`Ctrl-C`, which stops the console and port-forward and deletes the demo namespace.

## 7. Console UI

Expand All @@ -178,14 +179,13 @@ npm run dev

The console runs at `http://localhost:5173`. All `/api/*` requests are proxied to the agent at `http://localhost:8000`.

For a cluster agent, first forward the in-cluster service in a separate terminal:
`make demo-cluster` handles the cluster service port-forward and console automatically.
When developing the UI separately, the equivalent manual port-forward is:

```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
Expand Down
Loading