Agentic Covenants

Respond (RS) · Authorization

Authorization at the server side layer

external · Outside the agent entirely

How do I stop the bleeding now?

What this cell does

Apply emergency Kyverno deny-all, replace agent Role with empty rules, attach IAM deny-all, Git pre-receive reject.

Artifacts (3)

agent-deny-all-serverview on GitHub
#!/usr/bin/env bash
# ABOUTME: Server-side authorization-shrink runbook. Applies Kyverno deny-all, empty Role, IAM deny-all.
# ABOUTME: Pre-stage kyverno-deny-all-agents.yaml, empty-role.yaml, and iam-deny-all.json under /etc/agents/emergency/ first.

set -euo pipefail

if [[ $# -lt 1 ]]; then
  echo "Usage: agent-deny-all-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}"

for src in \
    "$EMERGENCY_DIR/kyverno-deny-all-agents.yaml" \
    "$EMERGENCY_DIR/empty-role.yaml" \
    "$EMERGENCY_DIR/iam-deny-all.json"; do
  if [[ ! -r "$src" ]]; then
    echo "REFUSING: pre-staged artifact missing: $src" >&2
    exit 1
  fi
done

# 1. Apply emergency Kyverno deny-all ClusterPolicy.
kubectl apply -f "$EMERGENCY_DIR/kyverno-deny-all-agents.yaml"

# 2. Overwrite the agent's Role with empty rules. The Role's name must match
# the original (claude-code in agent-* namespaces by convention).
kubectl apply -n "$NAMESPACE" -f "$EMERGENCY_DIR/empty-role.yaml"

# 3. Attach IAM deny-all policy. Idempotent with identity-revocation runbook.
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 (already attached or role missing)" >&2

# 4. Cluster event for forensic timeline.
kubectl create event --namespace "$NAMESPACE" \
  --type=Warning \
  --reason=AuthorizationLocked \
  --message="Emergency authorization shrink for $AGENT_NAME, incident $INCIDENT_ID" 2>/dev/null || true

logger -t agent-incident -p user.warning \
  "$(jq -n \
      --arg event "authorization_locked_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 authorization locked down for $AGENT_NAME (incident $INCIDENT_ID)"
empty-role.yamlview on GitHub
# ABOUTME: Pre-staged empty Role used to overwrite an agent's namespace-scoped Role during response.
# ABOUTME: The Role name must match the original (claude-code by convention). Empty rules removes every permission.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: claude-code
  annotations:
    incident: "true"
rules: []
kyverno-deny-all-agents.yamlview on GitHub
# ABOUTME: Emergency Kyverno ClusterPolicy denying all operations from agent ServiceAccounts. Excludes break-glass.
# ABOUTME: Pre-stage at /etc/agents/emergency/. Kyverno must run in Enforce mode (audit-only does not block).
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: emergency-deny-all-agents
  annotations:
    incident: "true"
    policies.kyverno.io/title: Emergency lockdown of all agent ServiceAccounts
    policies.kyverno.io/severity: critical
spec:
  validationFailureAction: Enforce
  background: false
  rules:
  - name: deny-everything-from-agent-sa
    match:
      any:
      - resources:
          kinds: ["*"]
    exclude:
      any:
      # Break-glass identities. Add or remove based on your environment's
      # incident-response identities. The operator who needs to remediate
      # MUST be excluded or the lockdown locks them out too.
      - subjects:
        - kind: User
          name: "system:admin"
        - kind: User
          name: "break-glass-operator"
        - kind: ServiceAccount
          name: "kyverno-admission-controller"
          namespace: "kyverno"
    preconditions:
      any:
      - key: "{{ request.userInfo.username }}"
        operator: AnyIn
        value:
        - "system:serviceaccount:agent-*"
    validate:
      message: "Emergency lockdown: agent operations denied. See incident channel."
      deny: {}

Cell notes

Interventions, Authorization / Server-side

Trigger. RBAC denial spike, Kyverno PolicyReport failures from agent SAs, IAM Access Analyzer findings, Git pre-receive rejection spike.

Authority. On-call, no second approval.

Speed target. Under 10 seconds.

Tooling

  • - kubectl with permission to apply ClusterPolicies and Roles in agent namespaces.
  • - AWS CLI with permission to attach IAM policies.
  • - Kyverno 1.18+ in Enforce mode (audit-only does not block).

Files in this directory

  • - agent-deny-all-server, runbook script. Applies the emergency Kyverno ClusterPolicy, overwrites the agent's Role with empty rules, attaches IAM Deny * policy.
  • - kyverno-deny-all-agents.yaml, pre-staged ClusterPolicy denying every operation from any system:serviceaccount:agent-*. Excludes the break-glass operator. Pre-stage at /etc/agents/emergency/kyverno-deny-all-agents.yaml.
  • - empty-role.yaml, pre-staged Role with rules: [] and the same name as the original agent Role. Overwriting it removes every permission. Pre-stage at /etc/agents/emergency/empty-role.yaml.

The IAM Deny * policy is at ../../identity/server-side/iam-deny-all.json (shared with identity revocation).

Verification


# 1. Kyverno deny-all in effect
kubectl get clusterpolicy emergency-deny-all-agents
kubectl --as=system:serviceaccount:agent-claude-code-prod:claude-code get pods
# expected: failure with "Emergency lockdown: agent operations denied"

# 2. Role is empty
kubectl get role -n agent-claude-code-prod claude-code -o jsonpath='{.rules}'
# expected: [] or null

# 3. IAM deny-all attached
aws iam get-role-policy --role-name claude-code-prod --policy-name EmergencyDenyAll

Common mistakes

  • - Kyverno deny-all rule that does not exclude break-glass identities, locks out the operator who needs to remediate.
  • - Kyverno installed in audit-only mode org-wide, emergency Enforce policy still does not block.
  • - Empty Role applied with kubectl apply but a stale RoleBinding still references a different (non-empty) Role.
  • - IAM deny-all that interacts badly with explicit allow policies in the same role's permission boundary.

Citation

NIST CSF 2.0 RS.MI-01, RS.MI-02; PR.AA-05 (response dimension). NIST SP 800-207 (Zero Trust). OWASP ASI02, ASI03, ASI05. 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.

  • Kyverno in audit-only mode does not block
  • emergency policy locks out break-glass identity

Crosswalk

NIST CSF 2 0RS.MI-01, RS.MI-02, PR.AA-05
NIST AI RMFMANAGE 4.1
OWASP AGENTICASI02, ASI03, ASI05
OTHERNIST SP 800-207