Agentic Covenants

Respond (RS) · Blast radius

Blast radius at the server side layer

external · Outside the agent entirely

How do I stop the bleeding now?

What this cell does

Apply emergency NetworkPolicy default-deny, scale Deployment to zero, force-delete pods, optionally cordon node, block egress at cloud firewall.

Artifacts (3)

agent-contain-serverview on GitHub
#!/usr/bin/env bash
# ABOUTME: Server-side blast-radius containment runbook. NetworkPolicy default-deny, scale to zero, force-delete pods.
# ABOUTME: Pre-stage networkpolicy-emergency-deny.yaml and sg-deny-all-egress.json under /etc/agents/emergency/.

set -euo pipefail

if [[ $# -lt 1 ]]; then
  echo "Usage: agent-contain-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}"
DRAIN_NODES="${DRAIN_NODES:-0}"   # set to 1 to cordon and drain nodes hosting the agent
BLOCK_EGRESS_SG="${BLOCK_EGRESS_SG:-1}"

if [[ ! -r "$EMERGENCY_DIR/networkpolicy-emergency-deny.yaml" ]]; then
  echo "REFUSING: $EMERGENCY_DIR/networkpolicy-emergency-deny.yaml not pre-staged" >&2
  exit 1
fi

# 1. Apply emergency NetworkPolicy default-deny in the agent's namespace.
kubectl apply -n "$NAMESPACE" -f "$EMERGENCY_DIR/networkpolicy-emergency-deny.yaml"

# 2. Scale all Deployments in the agent namespace to zero.
kubectl scale deployment -n "$NAMESPACE" --all --replicas=0 2>/dev/null || true

# 3. Force-delete pods that did not respect scale-down.
kubectl delete pods -n "$NAMESPACE" --all --force --grace-period=0 2>/dev/null &
DELETE_PID=$!

# 4. Optional: cordon and drain the nodes hosting the agent if compromise
# extends to the host (Falco events from outside the namespace, kernel-level
# escape suspected, etc.).
if [[ "$DRAIN_NODES" == "1" ]]; then
  mapfile -t AGENT_NODES < <(
    kubectl get pods -n "$NAMESPACE" -o jsonpath='{.items[*].spec.nodeName}' 2>/dev/null \
      | tr ' ' '\n' | sort -u
  )
  for node in "${AGENT_NODES[@]}"; do
    [[ -z "$node" ]] && continue
    kubectl cordon "$node" 2>/dev/null || true
    kubectl drain "$node" \
      --grace-period=0 --force \
      --delete-emptydir-data --ignore-daemonsets &
  done
fi

# 5. Block egress at cloud firewall (AWS Security Group example).
if [[ "$BLOCK_EGRESS_SG" == "1" ]] && command -v aws >/dev/null 2>&1; then
  SG_ID="$(aws ec2 describe-security-groups \
    --filters "Name=tag:agent,Values=$AGENT_NAME" \
    --query 'SecurityGroups[0].GroupId' --output text 2>/dev/null || true)"
  if [[ -n "$SG_ID" && "$SG_ID" != "None" ]]; then
    aws ec2 update-security-group-rule-descriptions-egress \
      --group-id "$SG_ID" \
      --ip-permissions "$(cat "$EMERGENCY_DIR/sg-deny-all-egress.json")" \
      2>/dev/null || echo "WARN: SG rule update failed; SG may be referenced elsewhere" >&2
  fi
fi

wait

logger -t agent-incident -p user.warning \
  "$(jq -n \
      --arg event "blast_radius_contained_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 blast radius contained for $AGENT_NAME (incident $INCIDENT_ID)"
networkpolicy-emergency-deny.yamlview on GitHub
# ABOUTME: Emergency default-deny NetworkPolicy applied during blast-radius containment. No allow rules; ingress and egress fully denied.
# ABOUTME: Pre-stage at /etc/agents/emergency/. CNI-dependent — verify your CNI enforces NetworkPolicy at all (some legacy setups do not).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: emergency-deny-everything
  annotations:
    incident: "true"
spec:
  podSelector: {}   # all pods in the namespace
  policyTypes:
  - Ingress
  - Egress
# No ingress or egress rules below = deny everything.
sg-deny-all-egress.jsonview on GitHub
[
  {
    "IpProtocol": "-1",
    "IpRanges": [
      {
        "CidrIp": "0.0.0.0/0",
        "Description": "Emergency lockdown: incident response. Egress denied."
      }
    ]
  }
]

Cell notes

Interventions, Blast radius / Server-side

Trigger. Falco alert from cluster, NetworkPolicy violation spike, ResourceQuota near-limit emergency, cross-namespace operations from an agent SA.

Authority. On-call, no second approval.

Speed target. Under 5 seconds for namespace freeze, under 60 seconds for full drain.

Tooling

  • - kubectl with permission to apply NetworkPolicies, scale Deployments, and (for cordon-and-drain) cordon nodes.
  • - AWS CLI (or GCP/Azure equivalent) with permission to update Security Group egress rules.

Files in this directory

  • - agent-contain-server, runbook script. Applies emergency NetworkPolicy default-deny, scales agent Deployments to zero, force-deletes pods, optionally cordons and drains nodes, and blocks egress at the cloud firewall.
  • - networkpolicy-emergency-deny.yaml, pre-staged default-deny NetworkPolicy with no allow rules. Applied to the agent namespace. Pre-stage at /etc/agents/emergency/networkpolicy-emergency-deny.yaml.
  • - sg-deny-all-egress.json, pre-staged AWS Security Group egress-rule descriptions used by the runbook to update the agent's SG. Pre-stage at /etc/agents/emergency/sg-deny-all-egress.json.

Verification


# 1. NetworkPolicy in effect
kubectl get networkpolicy -n agent-claude-code-prod emergency-deny-everything

# 2. Egress blocked
kubectl exec -n agent-claude-code-prod $(kubectl get pods -n agent-claude-code-prod -o name | head -1) -- \
  curl -sS --max-time 3 https://example.com 2>&1 || echo "OK: egress blocked"

# 3. Deployment scaled to zero
kubectl get deployments -n agent-claude-code-prod -o jsonpath='{.items[*].spec.replicas}'
# expected: 0 0 0...

# 4. No pods running
kubectl get pods -n agent-claude-code-prod
# expected: empty

Common mistakes

  • - kubectl drain without --grace-period=0 honors terminationGracePeriodSeconds: 300, the pod runs for 5 minutes. Use --force --grace-period=0.
  • - Existing TCP connections survive NetworkPolicy changes (CNI-dependent). Verify by attempting a fresh connection after applying.
  • - Cordoning the wrong node, verify the pods' actual node placement before draining.
  • - Cloud firewall rule modification fails silently if the security group is referenced by other resources. Verify rule before declaring contained.

Citation

NIST CSF 2.0 RS.MI-01, RS.MI-02; PR.IR-01 (response dimension). NIST SP 800-61 Rev. 2. NISTIR 8596. OWASP ASI05, ASI08. 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.

  • drain without --grace-period=0 honors 5-min terminationGracePeriodSeconds
  • existing TCP connections survive NetworkPolicy changes (CNI-dependent)
  • cloud SG modification fails silently if SG is referenced elsewhere

Crosswalk

NIST CSF 2 0RS.MI-01, RS.MI-02, PR.IR-01
NIST AI RMFMANAGE 4.1
OWASP AGENTICASI05, ASI08
OTHERNIST SP 800-61 Rev. 2, NISTIR 8596