Kubernetes Workload Attestor
The Kubernetes attestor identifies workloads by querying the Kubernetes API for the pod that owns the requesting process.
Configuration
Enable the Kubernetes attestor in the WorkloadAttestation section:
section: WorkloadAttestation
schema: v1
spec:
kubernetes:
enabled: true
includeAnnotationPrefixes:
- "example.com/"
| Field | Default | Description |
|---|---|---|
kubernetes.enabled | true | Enable or disable Kubernetes attestation |
kubernetes.includeAnnotationPrefixes | [] | Annotation key prefixes to include as attributes. Empty means no annotations. When applied via Managed Configuration, changes propagate live without a restart. |
kubernetes.sigstore | — | Optional. Configures Sigstore image-signature verification. See Sigstore Image Verification below. |
Attributes
The following attributes are collected for workloads running in
Kubernetes-based platforms (e.g. k8s) and can be used in path
templates, JWT custom claims, and X.509 Subject customization. Some
attributes are restricted to specific surfaces — see the Notes column.
| Kubernetes Attribute | Path Template Variable | Notes |
|---|---|---|
| Pod Image Count | kubernetes.pod.image_count | Number of containers in the pod. E.g. 1 |
| Pod Init Image Count | kubernetes.pod.init_image_count | Number of init containers in the pod. E.g. 0 |
| Pod Name | kubernetes.pod.name | |
| Namespace | kubernetes.pod.namespace | |
| Pod UID | kubernetes.pod.uid | |
| Pod Node Name | kubernetes.pod.node.name | |
| Pod Owner | kubernetes.pod.owner | Owner of the pod, formatted as Type:Name. E.g. ReplicaSet:my-replicaset-5cfd45f6c9. See note below. |
| Pod Owner UID | kubernetes.pod.owner_uid | UID of the pod's owner. E.g. 316ea285-b878-4d20-a9fe-2e1c79b0f083. See note below. |
| Service Account | kubernetes.pod.service_account | |
| Pod Container Image Name | kubernetes.pod.container.<container_name>.image.name | One entry per container in the pod. Not available in path templates. |
| Pod Container Image ID | kubernetes.pod.container.<container_name>.image.id | One entry per container in the pod. Not available in path templates. |
| Pod Init Container Image Name | kubernetes.pod.init_container.<container_name>.image.name | One entry per init container in the pod. Not available in path templates. |
| Pod Init Container Image ID | kubernetes.pod.init_container.<container_name>.image.id | One entry per init container in the pod. Not available in path templates. |
| Pod Label | kubernetes.pod.label[<label_key>] | One entry per label on the pod |
| Pod Annotation | kubernetes.pod.annotation[<annotation_key>] | One entry per allowlisted annotation on the pod |
| Container Name | kubernetes.container.name | Disabled by default. See below. |
| Container Image Name | kubernetes.container.image.name | Disabled by default. See below. |
| Container Image ID | kubernetes.container.image.id | Disabled by default. See below. |
| Image Signature Subject | kubernetes.image.signature.subject | Emitted only when Sigstore verification succeeds. See note below. |
| Image Signature Issuer | kubernetes.image.signature.issuer | Emitted only when Sigstore verification succeeds. See note below. |
| Image Signature Value | kubernetes.image.signature.value | Emitted only when Sigstore verification succeeds. See note below. |
| Image Signature Log ID | kubernetes.image.signature.log_id | Emitted only when Sigstore verification succeeds. See note below. |
| Image Signature Log Index | kubernetes.image.signature.log_index | Emitted only when Sigstore verification succeeds. See note below. |
| Image Signature Integrated Time | kubernetes.image.signature.integrated_time | Emitted only when Sigstore verification succeeds. See note below. |
| Image Signature Signed Entry Timestamp | kubernetes.image.signature.signed_entry_timestamp | Emitted only when Sigstore verification succeeds. See note below. |
kubernetes.pod.owner and kubernetes.pod.owner_uid are derived from the pod's OwnerReferences. If there are multiple entries, the last owner in OwnerReferences is used.
Pod label and pod annotation keys containing forward slashes (/) or
dashes (-) require Trust Domain Server version 0.28.0 or newer. On
earlier versions, configuration using these keys will fail
synchronization, and the server will remain on its previous
configuration.
Pod annotations are emitted only for keys matching an allowlisted
prefix. Configure prefixes via kubernetes.includeAnnotationPrefixes
in the WorkloadAttestation managed configuration section. For
example, to expose the annotation example.com/my-annotation:
section: WorkloadAttestation
schema: v1
spec:
kubernetes:
includeAnnotationPrefixes:
- "example.com/"
Container attributes must be enabled by setting
includeContainerAttributes: true in the relevant platform block
(e.g. k8s, istio) of the spirl-system Helm values.
The kubernetes.container.* attributes describe the container running
the attested workload. To access attributes of other containers in the
same pod, use the kubernetes.pod.container.<container_name>.*
attributes instead.
kubernetes.image.signature.* attributes are emitted only when the
agent successfully verifies the workload's container image against
your configured Sigstore trust policy. Verification is opt-in and
requires the kubernetes.sigstore block in WorkloadAttestation.
Sigstore Image Verification
The Kubernetes attestor can additionally verify Sigstore signatures on a workload's container image and expose the verified signature metadata as attestation attributes. Trust policies can then condition SVID issuance on who signed the image: For example, by specifying that production workloads must run an image signed by your release CI workflow.
Why Verify Image Signatures
Container registries are a supply-chain entry point. Attackers have published malicious images on Docker Hub under names that mimic real projects, hoping these images are pulled into production clusters. Sigstore mitigates this class of attack by letting image publishers cryptographically sign their images, and consumers verify those signatures before trusting the image. This applies equally to public images and to images your own CI builds for internal use.
When the agent verifies a signature, it records who signed it, which OIDC provider vouched for them, and proof from the Rekor transparency log that the signature was created while the signing certificate was valid. Trust policies can use these attributes to decide whether to issue an SVID at all, and what privileges that SVID carries.
Sigstore Attributes
When verification succeeds, the agent emits:
| Attribute | Type | Description |
|---|---|---|
kubernetes.image.signature.subject | string | Identity that signed the image (e.g. a CI workflow URL or signer email) |
kubernetes.image.signature.issuer | string | OIDC issuer that vouched for the signer |
kubernetes.image.signature.value | string | Base64-encoded signature bytes |
kubernetes.image.signature.log_id | string | Identifier of the Rekor instance that logged the entry |
kubernetes.image.signature.log_index | uint64 | Monotonic index of the entry within that Rekor instance |
kubernetes.image.signature.integrated_time | uint64 | Unix timestamp at which Rekor recorded the entry |
kubernetes.image.signature.signed_entry_timestamp | string | Rekor's signed inclusion promise for the entry |
These attributes are left out (omitted) whenever verification fails — see Failure Behavior. A missing subject is not a denial signal; it just means the agent did not produce a successful verification. Trust policies should require these attributes to be present in order to grant the privileges that depend on them.
Using Sigstore Attributes in Policy
The subject and issuer attributes are the primary handles for trust decisions. Use them together to pin both the signer and the IdP that issued their identity:
| Attribute | How it's useful in a trust policy |
|---|---|
subject | Specify who must have signed the image — e.g. require a specific release CI workflow, not an arbitrary developer or unrelated workflow in the same org. |
issuer | Pair with subject to also pin which IdP vouched for the signer — e.g. only accept identities issued by your corporate OIDC provider. |
log_id | Require entries from a specific Rekor instance — e.g. only accept signatures logged in your private Rekor, not the public one. |
integrated_time | Enforce recency — reject SVIDs whose images were signed before a known cutoff (e.g. a key-rotation event), or too long ago to be a fresh build. |
log_index | Reject entries below a cutoff index — useful for invalidating everything logged before a known incident without rotating the entire Rekor instance. |
value | Rarely used in policy directly. Most useful as an audit record of which signature was honored when the SVID was issued. |
signed_entry_timestamp | Rarely used in policy directly. Most useful as an audit artifact you can re-verify offline to prove the entry was in the log at issuance time. |
Sigstore Configuration
Enable Sigstore verification under the kubernetes block of the WorkloadAttestation section:
section: WorkloadAttestation
schema: v1
spec:
kubernetes:
enabled: true
sigstore:
enabled: true
allowedIdentities: # Required.
# OIDC issuer URL → list of allowed subjects. Globbing supported in both.
"https://token.actions.githubusercontent.com":
- "https://github.com/myorg/myrepo/.github/workflows/release.yaml@refs/heads/main"
- "https://github.com/myorg/*/.github/workflows/release.yaml@refs/heads/main"
rekorURL: https://rekor.sigstore.dev # Optional, defaults to this.
skipProvenance: false # Optional, defaults to this.
skipTransparencyLog: false # Optional, defaults to this.
| Field | Default | Description |
|---|---|---|
sigstore.enabled | — | Required when the sigstore block is present. See enabled below. |
sigstore.allowedIdentities | — | Required when enabled: true. Map of OIDC issuer URLs to allowed subjects. See allowedIdentities below. |
sigstore.rekorURL | https://rekor.sigstore.dev | Rekor instance to use. See rekorURL below. |
sigstore.skipProvenance | false | When false, a SLSA provenance attestation is also required. See skipProvenance below. |
sigstore.skipTransparencyLog | false | When false, signatures are checked against Rekor. See skipTransparencyLog below. |
Hot configuration reloads are supported. When applied via Managed Configuration, changes propagate live without an agent restart.
enabled
Set to true to turn on Sigstore verification. Required whenever the sigstore block is present — there is no implicit default.
allowedIdentities
A map of allowed OIDC issuer URLs to the list of subjects allowed under each. Must contain at least one issuer with at least one subject.
Patterns use globs rather than regular expressions, because it's much harder to write a glob that accidentally matches everything than it is to leave a regex unanchored:
*matches any sequence of characters, including none.?matches exactly one character.- The pattern is anchored: it must match the entire string.
- Character classes, alternation, and other regex syntax are not supported.
To accept any subject from a given issuer, use ["*"]. Avoid this on a public issuer such as GitHub Actions — it allows any GitHub workflow in any organization to sign images you'll trust.
A pattern like https://*.github.com* (note the trailing *) matches https://anything.github.com.attacker.example — almost certainly not what you want. Anchor the suffix: https://*.github.com/..., or omit the trailing wildcard.
rekorURL
The Rekor transparency-log instance the agent queries when verifying signatures. Defaults to the public Sigstore Rekor at https://rekor.sigstore.dev. Override only if you run your own instance. Must be an HTTPS URL.
skipProvenance
Controls whether the agent additionally verifies a SLSA provenance attestation after signature verification. See Provenance Attestations below for what provenance is and why it matters.
When false (default), a valid provenance attestation must exist and be signed by an identity matching allowedIdentities. If provenance verification fails, the entire kubernetes.image.signature.* attribute set is rejected for that image.
When true, the provenance step is skipped. Use this for images that are signed (cosign sign) but don't have provenance — common outside highly regulated environments.
skipTransparencyLog
Controls whether signatures are verified against the Sigstore transparency log (Rekor).
When false (default), the agent checks Rekor to confirm the signature was created while the signing certificate was still valid. This is required for keyless signing with short-lived Fulcio certificates, which is the most common Sigstore setup.
When true, the Rekor check is skipped. Use this only if your images are signed with long-lived keys, or if you cannot reach the public Rekor and have configured a private instance via rekorURL.
Setting skipTransparencyLog: true causes verification to fail for keyless-signed images. Only set it if you are not using keyless signing.
Provenance Attestations
A provenance attestation is a separate signed statement, stored alongside the image, that records facts about how the image was built — which commit, which CI workflow, which build runner. It's signed by your build system's identity (e.g. GitHub Actions), not by a developer.
Even if an attacker pushes a tampered image to your registry, they cannot forge the provenance from your CI signing identity. By default, the agent verifies both the signature and the provenance attestation against allowedIdentities before emitting any signature attributes. If your images are signed but do not have provenance, set skipProvenance: true.
Image publishers produce signatures with cosign sign and provenance with cosign attest; consumers verify them with cosign verify and cosign verify-attestation.
Operational Notes
Failure Behavior
Sigstore verification is fail-open with respect to attestation. If verification fails for any reason — image not signed, signature does not match allowedIdentities, Rekor unreachable, network timeout, missing provenance — the workload's attestation still succeeds, but the kubernetes.image.signature.* attributes are omitted and a warning is logged.
This is intentional: a transient Rekor outage should not take down credential issuance for every workload. The implication is the same as the warning above — trust policies must require these attributes to be present, not check them for a "valid" flag.
Templates are an exception. If a path template, X.509 SVID template, or JWT claim references a kubernetes.image.signature.* attribute and that attribute is absent, SVID issuance fails with a missing-attribute error rather than producing a malformed SVID. Reference these attributes from templates only on workloads where Sigstore verification is expected to succeed.
Verification Caching
Successful verifications are cached per image (by digest) with a 5-minute TTL. Failures are never cached.
Practical implications:
- After publishing a new image, verification happens on the first attestation and is then served from cache for ~5 minutes.
- If you tighten
allowedIdentitiesto revoke a previously trusted signer, in-flight cached results may persist for up to 5 minutes. The cache is dropped when central config is reloaded.
Registry Authentication
The agent authenticates to private registries using node-level IAM credential helpers (GCR, ECR, ACR) — whatever the underlying node's instance role is allowed to pull, the agent can pull.
Workloads pulling images via imagePullSecrets will have Sigstore attributes omitted. The failure is logged, but as a generic registry auth error from the underlying pull — not as an explicit "imagePullSecrets not supported" diagnostic. Full imagePullSecrets support requires the agent to read namespaced Secrets and is not yet available. Confirm your image-pull path uses node-level IAM credentials before relying on Sigstore attributes in trust policy.
Multiple Signatures
When an image has more than one valid signature matching allowedIdentities, only the first is used to populate attributes. A log line is emitted when this occurs.
Observability
When Sigstore attributes are not appearing as expected:
- Check the agent logs for warnings naming the image — every verification failure logs a reason.
- Inspect the Sigstore metrics described below to confirm the agent is attempting verification on the image you expect, and to see why it failed.
- Confirm registry credentials — if the agent cannot pull image metadata, verification cannot run.
The agent emits the following Prometheus metrics. Every metric carries an attestor label (e.g. k8s) so dashboards and alerts can be scoped to a single attestor.
| Metric | Key Labels | What it measures |
|---|---|---|
spirl_agent_sigstore_image_verification_total | resultattestor | Counter of verification attempts, classified by result |
spirl_agent_sigstore_image_verification_duration_seconds | resultattestor | Histogram of how long each verification took, classified by result |
spirl_agent_sigstore_signature_count | attestor | Histogram of valid signatures found per image — recorded only on successful verification |
spirl_agent_sigstore_verification_cache_total | resultattestor | Counter of cache lookups, where result is hit or miss |
The result label on the two _image_verification_* metrics takes one of:
| Value | Meaning |
|---|---|
verified | Verification succeeded |
not_signed | Image had no Sigstore signatures |
identity_mismatch | Signature did not match allowedIdentities |
network_error | Registry or Rekor network failure |
timeout | Verification did not complete within the agent's deadline |
cancelled | Context cancelled (typically the workload disconnected) |
error | Any other failure |
As the cache fills, signature-count and verification-duration observations naturally fall in inverse proportion to the rise in cache_total{result="hit"} — a cache hit short-circuits the verification path.