Respond (RS) · Identity
Identity at the server side layer
external · Outside the agent entirely
How do I stop the bleeding now?
What this cell does
Revoke OIDC token at IdP, disable ServiceAccount, rotate IAM keys, attach deny-all IAM policy, invalidate all active sessions.
Artifacts (2)
agent-revoke-serverview on GitHub#!/usr/bin/env bash
# ABOUTME: Server-side identity-revocation runbook. Disables SA, force-deletes pods, attaches IAM deny-all, revokes OIDC sessions.
# ABOUTME: Pre-authorized at on-call level. Reversible. Pre-stage iam-deny-all.json under /etc/agents/emergency/ before relying on this script.
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: agent-revoke-server <AGENT_NAME>" >&2
exit 64
fi
AGENT_NAME="$1"
NAMESPACE="agent-${AGENT_NAME}"
INCIDENT_ID="$(uuidgen 2>/dev/null || python3 -c 'import uuid; print(uuid.uuid4())')"
EMERGENCY_DIR="${EMERGENCY_DIR:-/etc/agents/emergency}"
# 1. Disable ServiceAccount automount (existing pods keep their tokens until restart).
kubectl patch sa -n "$NAMESPACE" claude-code \
--type='merge' \
-p '{"automountServiceAccountToken":false}' 2>/dev/null || \
echo "WARN: kubectl patch failed; SA may not exist in $NAMESPACE" >&2
# 2. Delete all pods in the namespace to force token re-mount (which will fail).
# Background so we can continue with IAM revocation in parallel.
kubectl delete pods -n "$NAMESPACE" --all --force --grace-period=0 2>/dev/null &
KUBE_PID=$!
# 3. Attach IAM deny-all policy.
if [[ -r "$EMERGENCY_DIR/iam-deny-all.json" ]]; then
aws iam put-role-policy \
--role-name "$AGENT_NAME" \
--policy-name "EmergencyDenyAll-${INCIDENT_ID}" \
--policy-document "file://$EMERGENCY_DIR/iam-deny-all.json" 2>/dev/null || \
echo "WARN: aws iam put-role-policy failed" >&2
else
echo "WARN: $EMERGENCY_DIR/iam-deny-all.json not found; pre-stage this file before depending on this runbook." >&2
fi
# 4. Revoke OIDC tokens at the IdP. Provider-specific; Okta example below.
# Set OKTA_API_TOKEN, OKTA_DOMAIN in the operator's break-glass env before
# invoking. For other IdPs (Auth0, Keycloak, Dex), substitute the equivalent
# session-clear API call.
if [[ -n "${OKTA_DOMAIN:-}" && -n "${OKTA_API_TOKEN:-}" ]]; then
USER_ID=$(curl -sS "https://${OKTA_DOMAIN}/api/v1/users?q=${AGENT_NAME}" \
-H "Authorization: SSWS $OKTA_API_TOKEN" \
-H "Accept: application/json" | jq -r '.[0].id // empty')
if [[ -n "$USER_ID" ]]; then
curl -sS -X DELETE "https://${OKTA_DOMAIN}/api/v1/users/${USER_ID}/sessions" \
-H "Authorization: SSWS $OKTA_API_TOKEN" \
-H "Accept: application/json" >/dev/null
fi
fi
# 5. Wait for pod deletion to complete before declaring done.
wait "$KUBE_PID" 2>/dev/null || true
# 6. Log to cluster events and to syslog.
kubectl create event --namespace "$NAMESPACE" \
--type=Warning \
--reason=IdentityRevoked \
--message="Emergency identity revocation for $AGENT_NAME, incident $INCIDENT_ID" 2>/dev/null || true
logger -t agent-incident -p user.warning \
"$(jq -n \
--arg event "identity_revoked_server" \
--arg agent "$AGENT_NAME" \
--arg incident "$INCIDENT_ID" \
--arg actor "$(whoami)" \
--arg ts "$(date -Iseconds)" \
'{event:$event, agent:$agent, incident:$incident, actor:$actor, ts:$ts}')"
echo "Server-side identity revoked for $AGENT_NAME (incident $INCIDENT_ID)"
iam-deny-all.jsonview on GitHub{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EmergencyDenyAll",
"Effect": "Deny",
"Action": "*",
"Resource": "*"
}
]
}
Cell notes
Interventions, Identity / Server-side
Trigger. Same as client-side, plus indicators that the agent has cluster or cloud reach.
Authority. On-call, no second approval. Reversible.
Speed target. Under 30 seconds plus token TTL window.
Tooling
- -
kubectlwith permission to patch ServiceAccounts and delete Pods in agent namespaces. - - AWS CLI (or GCP/Azure equivalent) with permission to attach IAM policies.
- - IdP API access for OIDC session revocation (Okta/Auth0/Keycloak/Dex).
Files in this directory
- -
agent-revoke-server, runbook script. Disables ServiceAccount automount, force-deletes pods to break token mounts, attaches an IAMDeny *policy to the agent role, terminates active OIDC sessions. - -
iam-deny-all.json, pre-staged IAM policy document used by the runbook.Denyon. Pre-stage this file at/etc/agents/emergency/iam-deny-all.json.
Verification
# 1. SA automount disabled
kubectl get sa -n agent-claude-code-prod claude-code -o jsonpath='{.automountServiceAccountToken}'
# expected: false
# 2. Pods recreated and failing to mount token
kubectl get pods -n agent-claude-code-prod
# expected: error/CrashLoopBackOff state, unable to start
# 3. IAM deny-all attached
aws iam list-role-policies --role-name claude-code-prod | grep EmergencyDenyAll
# 4. OIDC sessions terminated (provider-specific verification)
Common mistakes
- - Forgetting that existing pods retain their mounted tokens until restart. Step 2 (pod deletion) is mandatory.
- - Token TTL of 1 hour means deny-policy is the only fast cutoff; revocation alone leaves a window.
- - Deleting the IAM role instead of attaching a deny policy. Deletion can fail if other resources reference the role; deny policy is faster and reversible.
- - Not testing the IdP API path before the incident. The emergency is the wrong time to learn that your Okta API token expired.
Citation
NIST CSF 2.0 RS.MI-01, RS.MI-02; PR.AA-01 (response dimension). NIST SP 800-63B Rev. 4 (credential lifecycle). NIST SP 800-207 (Zero Trust). OWASP ASI03, ASI10. NIST AI RMF MANAGE 4.1.
Primary failure modes
Documented, not hypothetical. A control whose bypass is undocumented is worse than no control, because somebody trusted it.
- JWT TTL window remains valid until expiry
- existing pods retain mounted tokens until restart
Crosswalk
| NIST CSF 2 0 | RS.MI-01, RS.MI-02, PR.AA-01 |
|---|---|
| NIST AI RMF | MANAGE 4.1 |
| OWASP AGENTIC | ASI03, ASI10 |
| OTHER | NIST SP 800-63B Rev. 4, NIST SP 800-207 |
Cite this cell:
https://agenticcovenants.com/respond/identity/server-side/