Runtime events from Falco
Attestkeep decides what may run. Falco watches what a container does once it is running. This integration is the join between them: your Falco's alerts arrive here, are filed against the image that produced them, and appear beside that image's scan.
Attestkeep does not ship, install or run Falco. The detection is yours; this is a place to put what it found. Nothing that arrives on this endpoint changes an admission decision — an alert cannot deny a pod, and a broken alert stream cannot break your cluster. Runtime behaviour detection stays outside what this product does, for the reasons on what Attestkeep does not do.
What you need
- Falco with Falcosidekick in front of it. Falcosidekick's generic
webhookoutput is what this endpoint speaks; it forwards Falco's alert JSON unchanged. The Falco Helm chart installs it withfalcosidekick.enabled=true. - Network reach from Falcosidekick to the operator's API service. Both usually live in the same cluster, so this is a ClusterIP address and no ingress.
1. Create the token
The endpoint has no session behind it — Falcosidekick has no account here — so a shared bearer token is the whole of its authentication. Generate one and keep it in a Secret; a token in values.yaml is a token in your Helm release history.
kubectl -n attestkeep create secret generic attestkeep-falco \
--from-literal=token="$(openssl rand -hex 32)"
2. Turn the endpoint on
integrations:
falco:
existingSecret:
name: attestkeep-falco
key: token
minPriority: warning
helm upgrade attestkeep oci://ghcr.io/attestkeep/charts/attestkeep \
-n attestkeep -f values.yaml
Until this is set the operator has no such endpoint: /api/v1/integrations/falco answers 404, which is also what it answers to anything probing for it.
3. Point Falcosidekick at it
The address is the operator's API service — <release>.<namespace>.svc on api.port, which is 8080 unless you changed it. For a release named attestkeep in namespace attestkeep:
# values for the falcosecurity/falco chart
falcosidekick:
enabled: true
config:
webhook:
address: "http://attestkeep.attestkeep.svc:8080/api/v1/integrations/falco"
customHeaders: "Authorization:Bearer <the token you generated>"
minimumpriority: warning
The same three settings as environment variables, if you configure Falcosidekick directly rather than through the chart:
WEBHOOK_ADDRESS=http://attestkeep.attestkeep.svc:8080/api/v1/integrations/falco
WEBHOOK_CUSTOMHEADERS=Authorization:Bearer <the token you generated>
WEBHOOK_MINIMUMPRIORITY=warning
Put the token in Falcosidekick's own Secret rather than inline, the same way you did on this side.
Set the floor on both ends. WEBHOOK_MINIMUMPRIORITY stops the event being sent; integrations.falco.minPriority stops it being stored. Dropping it here still costs the delivery, so the sender's floor is the one that saves work and this one is the backstop.
Priorities
Falco's own ordering, most severe first, and the names are matched case-insensitively:
emergency alert critical error warning notice informational debug
warning is the default floor. Notice and below is most of a cluster's alert volume and very little of its signal, and this table is read by an evidence document. A priority neither side recognises is kept, not dropped: a level a newer Falco introduced must not be silently discarded by an older operator.
Response codes
| Code | Meaning |
|---|---|
202 | Stored. |
204 | Accepted and not stored: below minPriority, or already on record. |
400 | Not a Falco event, or missing rule, priority or time. |
401 | The Authorization header does not carry the configured token. |
404 | No token configured — the integration is off. |
413 | The body is larger than 64 KiB. |
500 | The event could not be written. Falcosidekick logs the failed delivery. |
Falcosidekick treats any non-2xx as a delivery error and logs it, which is why a dropped event answers 204 rather than a 4xx: it is not the sender's mistake.
Redelivery is safe. Falcosidekick puts a fresh uuid on every delivery, so that field cannot carry idempotence; the operator keys on the detector source, the rule, the event's own timestamp, the container id and the pod. The same detection delivered twice is one row and a 204.
What you get
In the console. The images list grows a Runtime column with each image's alert count for the last seven days — only once the operator has actually been sent an event, so an installation that does not use this sees no new column. The image detail panel lists the last 20 alerts for that image: time, priority, rule, namespace and pod, and Falco's own output line.
In the evidence pack. A runtime section reporting the source, the period's events by priority, and the loudest rule-and-image pairs; and a runtime-observed attestation. That attestation is met only when the feed is configured and delivered something inside the period — a configured feed that stayed silent for a whole period reports partial, because from inside the operator a detector with nothing to say and a detector that stopped are the same silence.
In the metrics.
attestkeep_runtime_events_total{source, priority}
attestkeep_runtime_events_dropped_total{reason="priority|duplicate|unresolved_image"}
unresolved_image is not a lost event. Falco watches the host as well as the containers on it, and an alert about a process in no container is stored unattributed — it appears in the evidence pack's runtime section with no image beside it.
How an alert finds its image
An alert names the image the way the runtime pulled it — nginx:1.27 — while this operator records the canonical name docker.io/library/nginx. The endpoint normalises to the second before it stores anything, using the same code path admission uses, so the two match. If the alert carries no image digest, the digest of whatever the cluster last admitted under that name and tag is filled in; when there is nothing to fill in, it is left empty rather than guessed.