Docker Workload Attestor
The Docker attestor identifies workloads running inside Docker or Podman containers by querying the container runtime API. It discovers the container ID from the workload's cgroup path and uses it to fetch container metadata.
Configuration
Enable the Docker attestor in the WorkloadAttestation section:
section: WorkloadAttestation
schema: v1
spec:
docker:
enabled: true
| Field | Default | Description |
|---|---|---|
docker.enabled | false | Enable or disable Docker attestation |
docker.dockerSocketPath | unix:///var/run/docker.sock | Path to the Docker daemon socket |
docker.podmanSocketPath | unix:///run/podman/podman.sock | Path to the rootful Podman socket |
docker.podmanSocketPathTemplate | unix:///run/user/%d/podman/podman.sock | Template for the rootless Podman socket path. Must contain exactly one %d placeholder, substituted with the workload user ID |
docker.includeEnvVars | [] | Environment variable name prefixes to expose as docker.container.env.* attributes. When empty, no env var attributes are produced. Supports hot-reload |
docker.sigstore | — | Optional. Configures Sigstore image-signature verification. See Sigstore Image Verification below. |
The agent must have read access to the Docker or Podman socket. For rootless Podman, the agent requires sufficient privileges to read sockets owned by other users.
The attestor automatically detects whether to use Docker or Podman, including rootless Podman, without any additional configuration.
Environment variable filtering
By default, no environment variables are collected. Container env vars frequently contain secrets, so only variables whose name starts with a configured prefix are emitted as docker.container.env.* attributes.
section: WorkloadAttestation
schema: v1
spec:
docker:
enabled: true
includeEnvVars:
- APP_
- SERVICE_
- ENVIRONMENT
Matching is case-sensitive. With APP_VERSION=1.2.3 SERVICE_NAME=payments SECRET_KEY=abc ENVIRONMENT=prod, the above configuration emits APP_VERSION, SERVICE_NAME, and ENVIRONMENT. The SECRET_KEY variable is dropped.
Attributes
The following attributes are produced for successfully attested Docker and Podman containers and can be used in path templates, JWT custom claims, and X.509 Subject customization.
| Attribute | Description |
|---|---|
docker.container.image | Image ID string as recorded by the runtime, e.g. envoyproxy/envoy:v1.29.1 |
docker.container.label[<label_key>] | One attribute per container label. The label key forms the last segment of the attribute name, e.g. docker.container.label["com.example.team"] |
docker.container.env.<key> | One attribute per environment variable matching an includeEnvVars prefix. Not produced unless includeEnvVars is configured |
docker.container.image_config_digest | Content-addressed image config digest, e.g. sha256:9f86d0…. Stable across image mirrors and re-tags. Omitted if the image inspect call fails |
docker.image.signature.subject | Identity that signed the image (e.g. a CI workflow URL or signer email). Only present when Sigstore is enabled and verification succeeds |
docker.image.signature.issuer | OIDC issuer that vouched for the signer. Only present when Sigstore is enabled and verification succeeds |
docker.image.signature.value | Base64-encoded signature bytes. Only present when Sigstore is enabled and verification succeeds |
docker.image.signature.log_id | Identifier of the Rekor instance that logged the entry. Omitted when skipTransparencyLog is true |
docker.image.signature.log_index | Monotonic index of the entry within that Rekor instance. Omitted when skipTransparencyLog is true |
docker.image.signature.integrated_time | Unix timestamp at which Rekor recorded the entry. Omitted when skipTransparencyLog is true |
docker.image.signature.signed_entry_timestamp | Rekor's signed inclusion promise for the entry. Omitted when skipTransparencyLog is true |
Example SPIFFE ID template using a container label:
/workloads/{{docker.container.label["com.example.app"]}}
docker.container.image reflects the tag at container start time and can drift if the same tag is re-pushed. Use docker.container.image_config_digest in policies that require a pinned, immutable image identity.
Sigstore Image Verification
The Docker 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 |
|---|---|---|
docker.image.signature.subject | string | Identity that signed the image (e.g. a CI workflow URL or signer email) |
docker.image.signature.issuer | string | OIDC issuer that vouched for the signer |
docker.image.signature.value | string | Base64-encoded signature bytes |
docker.image.signature.log_id | string | Identifier of the Rekor instance that logged the entry |
docker.image.signature.log_index | uint64 | Monotonic index of the entry within that Rekor instance |
docker.image.signature.integrated_time | uint64 | Unix timestamp at which Rekor recorded the entry |
docker.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 docker block of the WorkloadAttestation section:
section: WorkloadAttestation
schema: v1
spec:
docker:
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 docker.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 docker.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 docker.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.
Repo Digest Availability
Sigstore verification requires a repo digest — the registry/image@sha256:<digest> form produced when an image is pushed to a registry. Locally built images that have never been pushed will not have a repo digest, and verification is silently skipped.
Ensure images are pushed to a registry before deployment if Sigstore attributes are required by your policies.
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 the image has a repo digest — if the agent cannot resolve a digest, verification cannot run.
The agent emits the following Prometheus metrics. Every metric carries an attestor label (e.g. docker) 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.