Skip to content
Closed
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
36 changes: 36 additions & 0 deletions authbridge/demos/github-issue/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Client configurations with roles
# Each client can have multiple roles
clients:
- client_id: "spiffe://localtest.me/ns/team1/sa/git-issue-agent"
secret: "github-agent-secret"
direct_access_grants: true
roles:
- name: "github-agent"
description: "Access role for the GitHub issue agent"
- client_id: "github-tool"
secret: "github-tool-secret"
roles:
- name: "github-tool-aud"
description: "Audience role for GitHub tool access"
- name: "github-full-access"
description: "Full access role for GitHub tool operations"

# Realm roles
realm_roles:
- regular
- privileged

# Client audience targets
client_audience_targets:
spiffe://localtest.me/ns/team1/sa/git-issue-agent:
- github-tool
github-tool: []

# User configurations (only username and roles needed, rest uses defaults)
users:
- username: alice
roles:
- regular
- username: bob
roles:
- privileged
146 changes: 63 additions & 83 deletions authbridge/demos/github-issue/demo-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,64 +229,14 @@ Once the webhook is deployed, create the namespace and apply the ConfigMaps:

```bash
kubectl create namespace team1
kubectl apply -f authbridge/demos/github-issue/k8s/configmaps-webhook.yaml -n team1
kubectl apply -f authbridge/demos/webhook/k8s/configmaps-webhook.yaml -n team1
```

> **Note:** If you want to use a different namespace, set `AUTHBRIDGE_NAMESPACE=<your-namespace>` and update all subsequent commands accordingly.

---

## Step 2: Configure Keycloak

Keycloak needs to be configured with the correct clients, scopes, and users for the
token exchange flow between the agent and the GitHub tool.

### Port-forward Keycloak (if needed)

The setup script connects to Keycloak at `http://keycloak.localtest.me:8080`.
If Keycloak is not already reachable at that address (e.g., via an ingress),
start a port-forward in a separate terminal:

```bash
kubectl port-forward service/keycloak-service -n keycloak 8080:8080
```

### Run the setup script

```bash
cd authbridge

# Create virtual environment (if not already done)
python -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

# Run the Keycloak setup for this demo
python demos/github-issue/setup_keycloak.py
```

Or with a custom namespace/service account:

```bash
python demos/github-issue/setup_keycloak.py --namespace myns --service-account mysa
```

This creates:

| Resource | Name | Purpose |
|----------|------|---------|
| **Realm** | `kagenti` | Keycloak realm for the demo |
| **Client** | `github-tool` | Target audience for token exchange |
| **Scope** | `agent-team1-git-issue-agent-aud` | Realm DEFAULT — auto-adds Agent's SPIFFE ID to all tokens |
| **Scope** | `github-tool-aud` | Realm OPTIONAL — for exchanged tokens targeting the tool |
| **Scope** | `github-full-access` | Realm OPTIONAL — for privileged GitHub API access |
| **User** | `alice` (password: `alice123`) | Regular user — public access |
| **User** | `bob` (password: `bob123`) | Demo user — request with `scope=github-full-access` for privileged access |

---

## Step 3: Apply Demo ConfigMaps
## Step 2: Apply Demo ConfigMaps

The Kagenti installer creates default ConfigMaps (`authbridge-config`,
`spiffe-helper-config`, `envoy-config`) and the `keycloak-admin-secret` Secret
Expand Down Expand Up @@ -319,7 +269,7 @@ kubectl apply -f demos/github-issue/k8s/configmaps.yaml

---

## Step 4: Create GitHub PAT Secret
## Step 3: Create GitHub PAT Secret

The GitHub tool needs PAT tokens to access the GitHub API. Create a Kubernetes secret
with your tokens:
Expand All @@ -340,7 +290,7 @@ kubectl create secret generic github-tool-secrets -n team1 \

---

## Step 5: Deploy the GitHub Tool
## Step 4: Deploy the GitHub Tool

Deploy the GitHub MCP tool as a target service. This deployment does **not** get
AuthBridge injection (it is the target, not the caller):
Expand All @@ -353,7 +303,7 @@ kubectl wait --for=condition=available --timeout=120s deployment/github-tool -n

---

## Step 6: Deploy the GitHub Issue Agent
## Step 5: Deploy the GitHub Issue Agent

Deploy the agent with AuthBridge labels. The webhook will automatically
inject one combined AuthBridge sidecar (post-#411). In envoy-sidecar mode
Expand Down Expand Up @@ -393,7 +343,7 @@ agent envoy-proxy

---

## Step 7: Validate the Deployment
## Step 6: Validate the Deployment

### Check pod status

Expand Down Expand Up @@ -497,6 +447,56 @@ ollama serve

---

## Step 7: Configure Keycloak

Keycloak needs to be configured with the correct clients, scopes, and users for the
token exchange flow between the agent and the GitHub tool.

### Port-forward Keycloak (if needed)

The setup script connects to Keycloak at `http://keycloak.localtest.me:8080`.
If Keycloak is not already reachable at that address (e.g., via an ingress),
start a port-forward in a separate terminal:

```bash
kubectl port-forward service/keycloak-service -n keycloak 8080:8080
```

### Run the setup script

```bash
cd authbridge

# Create virtual environment (if not already done)
python -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

# Run the Keycloak setup for this demo
python demos/github-issue/setup_keycloak.py
```

This creates:

| Resource | Name | Purpose |
|----------|------|---------|
| **Realm** | `kagenti` | Keycloak realm for the demo |
| **Client** | `spiffe://localtest.me/ns/team1/sa/git-issue-agent` | Agent client with github-agent role |
| **Client** | `github-tool` | Target audience for token exchange |
| **Client Role** | `github-agent` | Access role for the GitHub issue agent |
| **Client Role** | `github-tool-aud` | Audience role for GitHub tool access |
| **Client Role** | `github-full-access` | Full access role for GitHub tool operations |
| **Scope** | `github-agent` | Client scope for agent access (DEFAULT for agent client) |
| **Scope** | `github-tool-aud` | Client scope for tool audience (OPTIONAL for agent) |
| **Scope** | `github-full-access` | Client scope for privileged access (OPTIONAL for agent) |
| **Realm Role** | `regular` | Standard user access level |
| **Realm Role** | `privileged` | Elevated user access level |
| **User** | `alice` (password: `alice123`) | User with 'regular' realm role |
| **User** | `bob` (password: `bob123`) | User with 'privileged' realm role |

---

## Step 8: Test the AuthBridge Flow

These tests verify both **inbound** JWT validation and **outbound** token exchange
Expand Down Expand Up @@ -845,20 +845,6 @@ kubectl delete pod test-client -n team1 --ignore-not-found
---

## Step 10: Access Control — Alice vs Bob

<!-- WORKAROUND: Remove this note once kagenti-extensions#139 is implemented.
The full scope-forwarding feature in authbridge is required for this step to work
end-to-end. Until that lands, the exchanged token always includes github-full-access
(from the static token_scopes in the authproxy-routes ConfigMap).
Track: https://github.com/kagenti/kagenti-extensions/issues/139 -->

> **Known limitation:** This step requires the authbridge scope forwarding feature
> ([kagenti-extensions#139](https://github.com/kagenti/kagenti-extensions/issues/139)).
> Currently, `token_scopes` in the `authproxy-routes` ConfigMap is static per-route, so
> all exchanged tokens include `github-full-access` regardless of the original user's
> scopes. Once scope forwarding is implemented, Alice's exchanged token will omit
> `github-full-access` while Bob's will include it.

This step demonstrates **scope-based access control**: two users with different
privilege levels get different GitHub API access through the same agent.

Expand All @@ -869,14 +855,11 @@ privilege levels get different GitHub API access through the same agent.

The flow:
1. User authenticates with Keycloak using `password` grant
2. Alice requests a token **without** `github-full-access`; Bob explicitly requests **with** it
(`github-full-access` is a realm OPTIONAL scope — Keycloak only includes it when the
token request contains `scope=openid github-full-access`)
3. AuthBridge exchanges the token — once scope forwarding is implemented
([#139](https://github.com/kagenti/kagenti-extensions/issues/139)), the exchanged
token will preserve the scope difference
4. The GitHub tool checks for `REQUIRED_SCOPE` (`github-full-access`) in the exchanged token
5. Tokens with the scope get the privileged PAT; tokens without get the public-only PAT
2. Request is sent to Agent (on behalf of User)
3. Agent invokes Tool to perform its github task
4. AuthBridge exchanges the token
5. The GitHub tool checks for `REQUIRED_SCOPE` (`github-full-access`) in the exchanged token
6. Tokens with the scope get the privileged PAT; tokens without get the public-only PAT

> **Prerequisite:** You need a **private** GitHub repository that the `PRIVILEGED_ACCESS_PAT`
> can access but the `PUBLIC_ACCESS_PAT` cannot. Replace `<your-org/your-private-repo>`
Expand Down Expand Up @@ -914,8 +897,7 @@ echo "Client ID: $CLIENT_ID Secret length: ${#CLIENT_SECRET}"

### 10c. Test as Alice (public access only)

Alice authenticates with Keycloak using `password` grant **without** requesting the
`github-full-access` scope. Her token only has the default scopes.
Alice authenticates with Keycloak using `password` grant.

```bash
ALICE_TOKEN=$(curl -s -X POST \
Expand Down Expand Up @@ -977,16 +959,14 @@ curl -s --max-time 300 \

### 10d. Test as Bob (privileged access)

Bob authenticates with `scope=openid github-full-access`, explicitly requesting
the privileged scope:
Bob authenticates with Keycloak using `password` grant.

```bash
BOB_TOKEN=$(curl -s -X POST \
"http://keycloak-service.keycloak.svc:8080/realms/kagenti/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "username=bob" \
-d "password=bob123" \
-d "scope=openid github-full-access" \
--data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "client_secret=$CLIENT_SECRET" | jq -r ".access_token")

Expand Down
2 changes: 1 addition & 1 deletion authbridge/demos/github-issue/k8s/configmaps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ data:
routes.yaml: |
- host: "github-tool-mcp"
target_audience: "github-tool"
token_scopes: "openid github-tool-aud github-full-access"
token_scopes: "openid"
17 changes: 17 additions & 0 deletions authbridge/demos/github-issue/policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Access Control Policy
# Maps user roles (realm roles) to specific client roles
# Format: user_role_name -> list of client role mappings

policy:
privileged:
- client: spiffe://localtest.me/ns/team1/sa/git-issue-agent
role: github-agent
- client: github-tool
role: github-full-access
- client: github-tool
role: github-tool-aud
regular:
- client: spiffe://localtest.me/ns/team1/sa/git-issue-agent
role: github-agent
- client: github-tool
role: github-tool-aud
Loading
Loading