Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
21 changes: 20 additions & 1 deletion demo/assets/agents/github_agent/k8s/configmaps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,23 @@ data:
# This allows the agent to call LLM APIs, telemetry endpoints, and other
# services without interference from the token exchange proxy.
#
# Update the host pattern if the github-tool service name or namespace changes.
# Two routes, one per tool host:
# github-tool-mcp the production tool, which the agent's MCP_URL targets.
# github-tool the onboarding demo's stand-in tool, which this install path
# deploys instead of github-tool-mcp. Its fields:
# host the tool's Service name; decides whether token exchange fires.
# target_audience the tool's SPIFFE ID, the audience its inbound validates
# (target side) and input.identity.service_id on the agent's
# outbound leg (agent side).
# token_scopes agent-team1-github-tool-aud is the audience scope the operator
# creates when it registers github-tool. The onboarding demo's
# driver.sh (phase PREP) and the system tests grant it to
# github-agent's client.
# The stand-in route is static, so it is fixed to namespace team1 and trust domain
# localtest.me — the values the demo and the system tests use by default. For
# another namespace or trust domain, edit target_audience and token_scopes to match.
# The sidecar reads this at start, so apply it before the agent Deployment, or
# restart the agent after a change.
apiVersion: v1
kind: ConfigMap
metadata:
Expand All @@ -63,3 +79,6 @@ data:
- host: "github-tool-mcp"
target_audience: "github-tool"
token_scopes: "openid github-tool-aud github-full-access"
- host: "github-tool"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3. Route for production github-tool-mcp host is replaced, not kept

The route for the production github-tool-mcp host is replaced instead of kept next to the new one. But the agent's own MCP_URL (http://github-tool-mcp:9090/mcp) and the github-agent.md prerequisite still target github-tool-mcp. The agent's real tool calls now pass through with no token exchange. routes.yaml is a list, so both routes can be kept.

Failure scenario: Deploy github-agent with the production github-tool-mcp as docs/specs/demo/github-agent.md describes. The agent's MCP calls to github-tool-mcp match no route, so token-exchange never fires and the call reaches the tool with the agent's inbound token. The tool's jwt-validation rejects it (401), or OPA sees no identity.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in de906e5. routes.yaml now lists both routes: the original github-tool-mcp route (matches MCP_URL) and the stand-in github-tool route. The deployment manifest's invariant comment and github-agent.md say to keep both.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Low] A route that only the demo uses is now in a shared manifest, with fixed values.

Every consumer of configmaps.yaml now gets a route for a stand-in tool that it possibly does not deploy, and a comment ("edit to match") is the only guard on the namespace and trust domain. Think about rendering the route at deploy time (for example, envsubst with NAMESPACE / TRUST_DOMAIN in deploy.sh / deploy_workload), or keep one helper with parameters. Then the demo, the system lane and non-default clusters cannot drift apart.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not changed in this PR. Rendering at deploy time would make a plain kubectl apply -f configmaps.yaml apply placeholders, and the docs and the troubleshooting fix use that command. The manifest is already fixed to team1 by metadata.namespace, so the route follows the same rule. Templating the namespace and trust domain for all of the demo manifests can be a follow-up.

target_audience: "spiffe://localtest.me/ns/team1/sa/github-tool"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4. Static route hardcodes team1 and localtest.me

The route that is now static hardcodes team1 and localtest.me in target_audience and token_scopes. The removed code derived both from NS (driver.sh), NAMESPACE (AIAC_DEMO_NAMESPACE) and TRUST_DOMAIN (AIAC_TRUST_DOMAIN). The harness's _tool_audience() / _tool_aud_scope() still derive them, so the two now drift apart.

Failure scenario: Run the system lane with AIAC_TRUST_DOMAIN=example.org, or the driver with NS=team2. The sidecar exchanges for spiffe://localtest.me/ns/team1/sa/github-tool with scope agent-team1-github-tool-aud. These do not exist or are the wrong audience, so every outbound or tool-inbound decision denies. exchanged_subject also no longer matches what the route actually sends, although its docstring says it mirrors the route.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept static on purpose. The manifest is already fixed to team1 (metadata.namespace), and the demo and system tests use localtest.me by default. In de906e5 the comment in configmaps.yaml and github-agent.md state this, and say to edit target_audience and token_scopes for another namespace or trust domain.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[High] The trust domain localtest.me is fixed in the route.

The removed ensure_github_tool_route built this audience from _tool_audience() (AIAC_TRUST_DOMAIN), and exchanged_subject still uses _tool_audience(). If you run the system lane with a different AIAC_TRUST_DOMAIN, the sidecar exchanges for spiffe://localtest.me/..., but the github-tool inbound validates spiffe://<domain>/.... Then the outbound probes in rungs 2/3 and the failure-rollback test deny, but the direct exchange in exchanged_subject passes. The results disagree and are hard to diagnose.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept static, as in the reply to finding 4. The route is fixed to team1 / localtest.me, the defaults of the demo and the system lane, and configmaps.yaml and github-agent.md say to edit target_audience and token_scopes for another trust domain. A run with a non-default AIAC_TRUST_DOMAIN must edit the route to match.

token_scopes: "openid agent-team1-github-tool-aud"
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@
# 3. authbridge-config ConfigMap (apply configmaps.yaml first)
# 4. authproxy-routes ConfigMap (apply configmaps.yaml first)
#
# Invariant: MCP_URL host ("github-tool-mcp") == authproxy-routes host entry
# Invariant: MCP_URL host ("github-tool-mcp") == one authproxy-routes host entry
# ("github-tool-mcp") == the Kubernetes Service name for the tool server.
# All three must match or token exchange will not fire for tool calls.
# authproxy-routes also carries a second route, host "github-tool", for the
# onboarding demo's stand-in tool. Keep both; neither replaces the other.
#
# Usage:
# kubectl apply -f configmaps.yaml
Expand Down
20 changes: 10 additions & 10 deletions demo/use-cases/onboarding/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#
# The live path (demo.md Parts 1-5) — deploying the workloads IS the onboarding trigger, and a
# real HTTP request through the live AuthBridge OPA plugin decides allow/deny:
# make enable && make opa && make users && make e2e
# make opa && make enable && make users && make e2e
#
# The narrated walkthrough (demo.md Part 6) — manual onboarding calls + offline `opa eval`, for
# inspecting what was generated step by step. Needs the workloads already deployed:
Expand Down Expand Up @@ -55,10 +55,10 @@ KC_ENV := . init/00-discover-keycloak.sh &&
help: ## Show this menu
@printf "\nonboarding demo — live AIAC onboarding of a real agent + tool. Full narrative: demo.md\n\n"
@printf "Live path (demo.md Parts 1-5) — deploying IS the trigger, real OPA enforces:\n"
@printf " \033[1mmake enable\033[0m (AIAC stack + NATS broker + Keycloak SPI)\n"
@printf " \033[1mmake opa\033[0m (wire the OPA plugin into both AuthBridge legs — one-time, cluster-level)\n"
@printf " \033[1mmake enable\033[0m (AIAC stack + NATS broker + Keycloak SPI)\n"
@printf " \033[1mmake users\033[0m (users/roles/policy — before the deploy that triggers onboarding)\n"
@printf " \033[1mmake e2e\033[0m (deploy -> verify trigger -> wire -> live enforce)\n\n"
@printf " \033[1mmake e2e\033[0m (deploy -> verify trigger -> prep -> live enforce)\n\n"
@printf "Narrated walkthrough (demo.md Part 6) — manual onboarding + offline opa eval:\n"
@printf " \033[1mmake demo\033[0m (init 00-03 -> onboard 04-05 -> run)\n"
@printf " \033[1mmake init / make onboard / make run\033[0m (one phase at a time)\n"
Expand Down Expand Up @@ -118,28 +118,28 @@ demo: init onboard run ## Full narrated walkthrough: init -> onboard -> run
# --- the live path (demo.md Parts 1-5) --------------------------------------------------------
# Thin wrappers over the scripts, so `make help` lists the whole demo in one place. These are NOT
# aggregated into a single target on purpose: OPA must be wired into both AuthBridge legs
# (`make opa`, a one-time cluster change owned by k8s/, not by this demo) between `enable` and
# `e2e`, and driver.sh's preflight fails loudly with that exact instruction if it isn't.
# (`make opa`, a one-time cluster change owned by k8s/, not by this demo) before `enable` — the
# AIAC Controller's start check #4 needs the combiner it applies — and driver.sh's preflight fails
# loudly with that exact instruction if it isn't.

enable: ## Part 1: install the AIAC stack + NATS broker + Keycloak SPI listener
enable: ## Part 2: install the AIAC stack + NATS broker + Keycloak SPI listener
./enable.sh

# The k8s/ scripts resolve OPERATOR_DIR / ROSSOCTL_DIR / CORTEX_DIR from their own location
# (sibling clones of aiac/), so running them from here needs no cd; exported overrides pass through.
opa: ## Part 2: wire the OPA plugin + parsers into both AuthBridge legs (k8s/opa-kind-enable.sh)
opa: ## Part 1: wire the OPA plugin + parsers into both AuthBridge legs (k8s/opa-kind-enable.sh)
$(AIAC_ROOT)/k8s/opa-kind-enable.sh

opa-restore: ## Revert `make opa`: restore the original AuthBridge pipeline (k8s/opa-kind-restore.sh)
$(AIAC_ROOT)/k8s/opa-kind-restore.sh

e2e: ## Parts 4-5: deploy (the trigger) -> verify trigger -> wire outbound -> live enforce
e2e: ## Parts 4-5: deploy (the trigger) -> verify trigger -> prep -> live enforce
./driver.sh

trigger: ## Part 4 alone: deploy the workloads and verify the live trigger fired
./driver.sh --only-deploy

enforce: ## Part 5 alone: wire the outbound leg, then probe the live OPA plugin
./driver.sh --only-wire-outbound
enforce: ## Part 5 alone: grant the exchange scope + restart the agent, then probe the live OPA plugin
./driver.sh --only-enforce

replay: ## Optional: re-prove the trigger against ALREADY-deployed workloads (forced replay)
Expand Down
Loading
Loading