Admission Webhook

Automatic sidecar injection via Kubernetes MutatingAdmissionWebhook.

Docs / Admission Webhook

How It Works

The PandoCore admission webhook uses Kubernetes' MutatingAdmissionWebhook API to automatically inject the PandoCore sidecar into pods at creation time.

When a pod is created with the label pandocore.io/protect: "true", the webhook intercepts the request and adds:

This is the same injection performed by inject-pandocore.sh, but automated cluster-wide.

Label Reference

Label / Annotation Type Values Description
pandocore.io/protect Label "true" Enables sidecar injection for pods with this label
pandocore.io/mode Annotation "monitor", "alert", "isolate", "enforce", "debug" Overrides the default operating mode for this pod. See Operating Modes.
pandocore.io/profile Annotation "http-server" Marks the workload as an HTTP server regardless of image name or CMD. Applies HTTP-tuned detection behavior and sensitivity. Use this when your framework (Django, FastAPI, Flask, Express, Rails, etc.) runs on a language base image or inside a shell-wrapped entrypoint.
pandocore.io/audit-network Annotation "true" or "false" Per-pod override for the cluster-wide networkAudit.enabled Helm value. When enabled, the sidecar baselines the set of remote IPs during learning and emits attestation evidence on connections to addresses outside the baseline + allowlist. Detects in-process exfil beacons and C2 callbacks that entropy drift alone cannot disambiguate.
pandocore.io/network-allowlist Annotation Comma-separated CIDRs Per-pod override for networkAudit.allowlistCIDRs. Connections to addresses inside any allowlisted CIDR are never flagged. Example: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16".

HTTP-server auto-detection

The webhook sets PANDO_HTTP_SERVER=true automatically from any of three signals — you rarely need to set the profile annotation by hand:

  1. Image-pattern match. Purpose-built web-server images: nginx, caddy, apache, traefik, envoy, haproxy, lighttpd, openresty, httpd.
  2. CMD/args basename match. Common Python, Node, and Ruby framework server entrypoints running on language base images (python:*, node:*, etc.) are auto-tagged by the webhook.
  3. Profile annotation. pandocore.io/profile: http-server on the pod template.
Shell-wrapped entrypoints

CMD/args basename matching looks at command[0]. If your Dockerfile or Helm chart uses sh -c "exec uvicorn main:app --host 0.0.0.0", the webhook sees sh, not uvicorn, and will not auto-tag. Add pandocore.io/profile: http-server to the pod template to cover this case.

Workloads that are not HTTP servers (Celery workers, ETL jobs, Jupyter notebooks, ML batch jobs) should be left untagged — the default profile is tuned for their behavior, and incorrectly marking them would mistune sensitivity.

Configuration

The webhook is configured via Helm values:

Value Default Description
licenseKey (required) Your PandoCore license key
defaultMode monitor Default operating mode for injected sidecars. One of monitor, alert, isolate, enforce.
excludedNamespaces [kube-system, kube-public, kube-node-lease] Namespaces where injection is always skipped
sidecar.image Latest sidecar from registry Sidecar container image to inject
replicas 2 Webhook server replicas (2 recommended for HA)
tls.certManager false Use cert-manager for TLS (otherwise self-signed)
networkAudit.enabled false Cluster-wide default for the network connection audit (Phase H5.2). Per-pod override via the pandocore.io/audit-network annotation.
networkAudit.allowlistCIDRs "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8,fd00::/8" Comma-separated CIDRs that are never flagged as unexpected. Default covers RFC1918, loopback, and ULA. Per-pod override via the pandocore.io/network-allowlist annotation.

Per-Namespace Setup

Each namespace with protected pods needs two resources:

# 1. ServiceAccount for the sidecar
kubectl create serviceaccount pando-sidecar -n your-namespace

# 2. RoleBinding to the cluster-wide sidecar ClusterRole
kubectl create rolebinding pando-sidecar \
  --clusterrole=pando-sidecar \
  --serviceaccount=your-namespace:pando-sidecar \
  -n your-namespace

# 3. Copy the license Secret into the namespace
kubectl create secret generic pando-license \
  --from-literal=license-key="YOUR_LICENSE_KEY" \
  -n your-namespace

Upgrading

# Upgrade the webhook (picks up new sidecar image + webhook config)
helm upgrade pando-webhook \
  oci://us-central1-docker.pkg.dev/pandocore-prod/charts/pando-webhook \
  --set licenseKey="YOUR_LICENSE_KEY"

# Existing pods are NOT affected. To inject the new sidecar version,
# restart protected deployments:
kubectl rollout restart deployment/your-app -n your-namespace

Troubleshooting

Sidecar not being injected

TLS certificate issues

Pod creation failing

The webhook is configured with failurePolicy: Ignore, so webhook failures will not block pod creation. If pods are failing for other reasons after injection, check: