Kubernetes operator: protect workloads by annotation
The Shield360 operator applies zero-code setup to Kubernetes workloads. It works like the OpenTelemetry Operator’s auto-instrumentation.
When a pod is created, a mutating admission webhook checks whether the pod or its namespace opted in. If it did, the webhook:
- adds an init container that copies the SDK into a shared volume,
- mounts that volume read-only into the app container, and
- sets the language’s start-up variables (
NODE_OPTIONS,PYTHONPATH,DOTNET_STARTUP_HOOKSorJAVA_TOOL_OPTIONS).
Your images don’t change. The operator runs unprivileged and is separate from the node-level Shield360 controller.
Install
Section titled “Install”The operator, its SDK init images and its Helm chart are published on GitHub Container Registry. The chart’s default images are the published ones for its version, so the install needs no image settings.
| Artifact | Name |
|---|---|
| Helm chart | oci://ghcr.io/thinkfleetai/charts/shield360-operator |
| Operator | ghcr.io/thinkfleetai/shield360-operator |
| SDK init images | ghcr.io/thinkfleetai/shield360-autoinstrumentation-nodejs, -python, -dotnet, -java |
| System logs agent | ghcr.io/thinkfleetai/shield360-system-logs-agent |
Images are multi-arch (linux/amd64, linux/arm64) and signed with cosign. The published Python init image is built for Python 3.12 on glibc. If your apps run another Python version or an Alpine (musl) image, ask support@shield360.ai for a matching init image, and set it with sdkImages.python or the instrumentation’s spec.python.image.
helm install shield360-operator oci://ghcr.io/thinkfleetai/charts/shield360-operator \ --version 0.3.0 \ --namespace shield360-system --create-namespaceTo see the published versions, run helm show chart oci://ghcr.io/thinkfleetai/charts/shield360-operator. The webhook needs TLS. The chart generates a certificate by default. Set certManager.enabled=true to use cert-manager instead.
If your cluster pulls through a mirror registry, set image.repository and sdkImageDefaults.repository (for example mirror.example.com/thinkfleetai/shield360-autoinstrumentation; the chart adds -<language>:<version>), or a full reference per language in sdkImages.<language>.
In each app namespace, create a Secret with your environment’s API key (Settings → Developer → API keys) under the key api-key, then a Shield360Instrumentation:
apiVersion: shield360.ai/v1alpha1kind: Shield360Instrumentationmetadata: name: default namespace: ordersspec: endpoint: https://app.shield360.ai apiKey: secretName: shield360 key: api-key environment: production rasp: mode: monitor configPublicKey: '<JWK from the app’s Runtime protection settings>' iast: enabled: true sampleRate: "0.1"The API key is always read from the Secret. The resource has no field that can hold it.
Annotate the pod template, or the whole namespace:
spec: template: metadata: annotations: shield360.ai/inject-nodejs: "true"Existing pods aren’t changed. Restart them (kubectl rollout restart) to apply it.
Opt whole namespaces in with one command
Section titled “Opt whole namespaces in with one command”The chart can also create the Shield360Instrumentation and opt every pod in a namespace in, without any annotations. Create the API key Secret in each namespace first, then install:
kubectl -n orders create secret generic shield360 --from-literal=api-key=<key>
helm install shield360 oci://ghcr.io/thinkfleetai/charts/shield360-operator \ --version 0.3.0 \ --namespace shield360-system --create-namespace \ --set shield360.endpoint=https://app.shield360.ai \ --set apiKey.existingSecret=shield360 \ --set injectNamespaces.orders=nodejs| Value | Meaning |
|---|---|
shield360.endpoint | Your Shield360 URL |
apiKey.existingSecret, apiKey.key | The Secret, in each listed namespace, that holds the API key (key api-key by default) |
injectNamespaces.<namespace> | Comma-separated languages: nodejs, python, dotnet, java, or sdk |
shield360.environment | Optional deployment.environment, such as production |
shield360.raspMode | Optional runtime protection default: off, monitor or block |
The chart never takes the API key itself, so the key isn’t in rendered manifests or in the Helm release. A namespace or pod annotation still wins over injectNamespaces, so one pod opts out with shield360.ai/inject-nodejs: "false". Restart the workloads (kubectl -n orders rollout restart deployment) to instrument running pods.
To send the cluster’s logs too, add --set systemLogs.enabled=true (see System logs). The agent image defaults to the published one. It reuses shield360.endpoint and apiKey.existingSecret, with the Secret in the release namespace.
Annotations
Section titled “Annotations”| Annotation | Value |
|---|---|
shield360.ai/inject-nodejs, inject-python, inject-dotnet, inject-java | "true" (the namespace’s only instrumentation), the instrumentation’s name, or "false" |
shield360.ai/inject-sdk | Adds only the configuration, for apps that already ship the SDK |
shield360.ai/container-names | Which containers to change, when a pod has several |
Annotations work on a pod or a namespace, and the pod’s annotation wins, which is how one pod opts out of a namespace-wide setting. The instrumentation must be in the pod’s namespace.