Free lesson · GenAI Security Engineering
Deploy agent security monitor on GKE
Build agent execution trace collector as sidecar, deploy with Helm and GKE Workload Identity, and configure agent resource quotas.
Course: AI Security Engineering · Chapter 10 · Agentic AI Security
Free to read — no subscription required.
Introduction
In production, deploying a security sidecar alongside your agent runtime means separating the security team's release cadence from the agent team's — but hard-coding environment URLs in container images defeats that flexibility. When you manage the sidecar's kill-switch endpoint, egress allowlist, and resource limits through a Helm values.yaml, each environment gets its own overrides without a rebuild. By the end of this lesson, you'll be able to author a production-ready values.yaml that pins the security sidecar by image digest, configures the kill-switch URL via chart values rather than the image, enforces a pod disruption budget, and restricts egress to the LLM gateway and tool registry.
Key Terminology
- Image digest pin — a reference to the sidecar container image by its immutable
sha256:content address (e.g.,registry.example.com/agents/security@sha256:a7c3f1e8b2d94c0fa...), guaranteeing that exactly the audited binary is deployed rather than whatever a mutable tag currently points to. - Values overlay — a secondary Helm values file (e.g.,
values-prod.yaml) that overrides only the fields that differ per environment; merged at deploy time via multiple-fflags inhelm upgrade --install, keeping the basevalues.yamlenvironment-agnostic and rebuild-free. - Kill-switch URL (
killSwitchUrl) — thesidecar.env.killSwitchUrlchart value that tells the security sidecar which in-cluster endpoint to call to halt agent execution; sourced from chart values rather than baked into the image so it resolves to the correct service in every environment without a container rebuild. - PodDisruptionBudget (
pdb.minAvailable) — a Kubernetes policy, driven by thepdb.minAvailablechart value, that caps how many agent pods the cluster may take offline simultaneously during node-pool upgrades or voluntary disruptions, preserving continuous coverage from the security monitor fleet. - Egress allowlist — the NetworkPolicy rendered by the chart that restricts the sidecar's outbound connections to the namespaces and pod labels declared in
network.llmGatewayNamespaceandnetwork.toolRegistryPodLabel, bounding the blast radius of any tool-misuse attempt. forbidPatterns— the list of pattern identifiers (e.g.,sql_meta,shell_exfil,protocol_smuggle) insidecar.envthat the security sidecar evaluates against agent tool calls at runtime to detect and block prohibited request shapes before they reach downstream systems.
Concepts
Decoupling the Security Sidecar's Release Cadence
The central design idea behind this lesson's values.yaml is that the security team and the agent team ship on independent schedules. If the kill-switch URL, sidecar binary version, or egress rules were baked into the agent image, a security hotfix would require the agent team to cut a new release — slowing incident response at exactly the moment speed matters most. By externalizing every security-team-owned decision into Helm chart values, the security team controls a file they can change, PR-review, and deploy without touching the agent image at all.
This separation is reinforced structurally by the chart linter: if sidecar.resources.limits is absent, CI rejects the rollout before any manifest reaches the cluster. The linter acts as a policy gate — encoding the operational rule that a sidecar without CPU and memory limits can starve the agent runtime it is supposed to protect.
Image Digest Pinning and Configuration Externalization
Helm charts can reference images by tag or by digest. Tags are mutable — even a semver tag can be silently overwritten in a registry. A sha256: digest is an immutable content address: the exact bytes deployed are known, reproducible, and auditable against a signed artifact. For a security sidecar — the component whose entire job is detecting adversarial behavior — a mutable image reference undermines the trustworthiness of the defense plane itself.
The same immutability logic applies to environment-specific configuration. The killSwitchUrl differs between staging and production; if it were a ENV instruction in the Dockerfile, promoting the image across environments would either silently call the wrong endpoint or require a separate image build per environment. Sourcing it from sidecar.env.killSwitchUrl means the same digest runs everywhere while each environment resolves to its correct kill-switch service (see Code Walkthrough).
Defense-Plane Availability: PDB and Rollout Strategy
A security sidecar that goes dark during a Kubernetes node-pool upgrade leaves agents unmonitored during that window. The pdb.minAvailable: "80%" value instructs Kubernetes to drain at most 20 % of agent pods at a time during voluntary disruptions — keeping the majority of the security monitor fleet running continuously. Paired with rollout.maxUnavailable: 0, the chart's Deployment template ensures no pod is terminated before a healthy replacement is ready.
Both values belong in values.yaml — not hard-coded in the Deployment template — because different environments carry different risk tolerances. A development cluster might accept a lower minAvailable for faster node recycling; production holds at 80 %. Capturing that decision in values-prod.yaml places it in a version-controlled, PR-reviewed file alongside all other production configuration, rather than buried in a shared template that every environment shares without differentiation.
Code Walkthrough
Building on the operating discipline from the Concepts section, the values.yaml is the single file the security team edits before each rollout — image digest, kill-switch URL, resource limits, egress targets, and disruption tolerance all live here rather than inside the container image.
Code snippetyaml
1# values.yaml — security-monitor Helm chart 2sidecar: 3 # Pin by digest; the security team controls exactly which binary runs. 4 image: registry.example.com/agents/security@sha256:a7c3f1e8b2d94c0fa631785e4d2b9c17 5 resources: 6 requests: {cpu: 200m, memory: 256Mi} 7 limits: {cpu: 500m, memory: 512Mi} 8 env: 9 # Kill-switch URL sourced from values, never baked into the image. 10 killSwitchUrl: http://kill-switch.agents.svc:8081 11 baselineBucket: gs://agent-baselines/v3 12 forbidPatterns: [sql_meta, shell_exfil, protocol_smuggle] 13 14agent: 15 image: registry.example.com/agents/runtime:1.9.0 16 serviceAccount: agent-sa 17 18network: 19 llmGatewayNamespace: ai-llm-gateway 20 toolRegistryPodLabel: tool-registry 21 22pdb: 23 minAvailable: "80%" 24 25rollout: 26 maxSurge: 1 27 maxUnavailable: 0
A separate values-prod.yaml overlays only the fields that differ in production — typically the sidecar digest (a newer pinned hash) and the killSwitchUrl (the production endpoint). The chart then renders the Deployment, NetworkPolicy, ServiceAccount, and PodDisruptionBudget from a single invocation:
Code snippetbash
1helm upgrade --install agent-security ./chart \ 2 -f values.yaml \ 3 -f values-prod.yaml \ 4 --namespace agents
The chart's linter rejects a missing sidecar.resources.limits block before the manifest reaches the cluster, enforcing a resource-limit requirement — a sidecar without limits can starve the agent runtime it is supposed to protect.
Because pdb.minAvailable and the rollout strategy are also chart values, the security team adjusts disruption tolerance per environment without touching the Deployment template. They edit values-prod.yaml, open a PR, and CI lints and deploys in one step — decoupling the security sidecar's release cadence from the agent's release cadence exactly as the Concepts section calls for.
Confirm that helm template ./chart -f values.yaml -f values-prod.yaml renders a Deployment whose sidecar container references a digest-pinned image, a PodDisruptionBudget with minAvailable: 80%, and no literal kill-switch hostname anywhere in the container's env block.
Do's and Don'ts
Now that you have worked through the implementation, the practices below separate a durable approach from a fragile one.
Do's
- ✓Do pin
sidecar.imageby SHA-256 digest invalues.yaml— a mutable tag likelatestor1.9.0can be silently overwritten in the registry, letting an untested binary reach production without any chart change; the digest-pinned formregistry.example.com/agents/security@sha256:a7c3f1e8b2d94c0fa631785e4d2b9c17gives the security team deterministic control over exactly which binary runs in each environment. - ✓Do source
sidecar.env.killSwitchUrl(and other environment-varying endpoints) from chart values, not from the container image — externalizing the endpoint intovalues.yamland overriding it per environment invalues-prod.yamllets the security team change the kill-switch target without a rebuild, which is the mechanism that decouples the security team's release cadence from the agent team's. - ✓Do run
helm template ./chart -f values.yaml -f values-prod.yamlbefore everyhelm upgrade— inspecting the rendered output is the only way to confirm the sidecar container references a digest-pinned image, thePodDisruptionBudgetcarriesminAvailable: 80%, and no literal kill-switch hostname appears in the container'senvblock.
Don'ts
- ✗Don't omit
sidecar.resources.limitsfromvalues.yaml— the chart linter rejects a manifest missing this block before it reaches the cluster, but if the linter is bypassed, a limitless sidecar can exhaust node CPU and memory and starve the agent runtime it is supposed to protect; thelimits: {cpu: 500m, memory: 512Mi}block is the guard against that failure mode. - ✗Don't put production-specific fields — the pinned sidecar digest and the production
killSwitchUrl— in the basevalues.yaml— collapsing environment overrides into the base file erases the environment boundary, making it impossible to distinguish what changed between environments in a PR and defeating the layeredhelm upgrade -f values.yaml -f values-prod.yamlpattern the chart is built around. - ✗Don't leave
network.llmGatewayNamespaceornetwork.toolRegistryPodLabelunset or misnamed — both values feed the chart'sNetworkPolicytemplate that restricts egress to the LLM gateway and tool registry; a wrong label selector renders a policy whose pod-selector matches nothing, silently leaving the sidecar with unrestricted egress and undoing the tool-misuse containment the chart is designed to enforce.
This lesson is free to read. Its 3 hands-on labs — real code, in a cloud IDE — are part of the GenAI Security Engineering subscription.
From · cancel anytime · Already a subscriber? Sign in →
More free lessons in AI Security Engineering
- Ch 10Enforce least-privilege for agent tool access
- Ch 10Detect and contain rogue agent behavior
- Ch 10Deploy agent security monitor on GKEYou are here
- Ch 10Test agent security with adversarial scenarios
- Ch 11Detect tool poisoning in MCP tool descriptions
- Ch 11Implement MCP server authentication and authorization
- Ch 11Secure agent-to-agent communication channels