Skip to content

Workload Attestation

Workload attestation is the process of proving a process’s identity to the kernel module before it can use a workload identity. It’s the same concept as SPIFFE workload attestation: rather than trusting a workload because of where it runs (an IP, a hostname, a network zone), attestation looks at what the workload actually is and derives its identity from that.

It happens after node attestation has already established trust between the daemon and the control plane: node attestation identifies the machine, workload attestation identifies the individual processes running on it.

Workload attestation happens at connection time, entirely on the node, with no round-trip to the control plane. When a process opens a new network connection, the daemon collects metadata about the process and its environment, then evaluates it against the selectors defined in WorkloadIdentity resources. A process receives an identity if it satisfies the selectors for that WorkloadIdentity.

This evaluation is not a one-time check. Every new connection triggers selector evaluation, so a process that previously matched but has since changed (for example, a different binary was exec’d into the same PID) will not pass attestation.

Selectors follow the same matching rules as a Verifier’s requiredMetadata (see Node Attestation): a WorkloadIdentity’s selectors field is a list of key-value groups, and a process matches if it satisfies all keys within any one group (AND within a group, OR across groups). A key within a group can also list multiple values; the process matches that key if it has any one of them:

selectors:
- process:name: nginx
k8s:pod:namespace: [production, staging]
- process:name: nginx
docker:label:env: production

This matches an nginx process in either the production or staging Kubernetes namespace, or an nginx process running in a Docker container labeled env: production outside Kubernetes entirely.

apiVersion: core.riptides.io/v1alpha1
kind: WorkloadIdentity
metadata:
name: nginx-web
namespace: riptides-system
spec:
workloadID: my-app/web/nginx
selectors:
- process:name: nginx
k8s:pod:namespace: my-app
k8s:label:app: nginx
connection:
tls:
mode: PERMISSIVE

This issues an identity to any process named nginx, running in a pod labeled app: nginx within the my-app Kubernetes namespace.

A batch worker running directly on an EC2 instance, with no Kubernetes or Docker involved:

apiVersion: core.riptides.io/v1alpha1
kind: WorkloadIdentity
metadata:
name: batch-worker
namespace: riptides-system
spec:
workloadID: myapp/batch-worker
selectors:
- process:name: batch-worker
ec2:placement:region: us-east-1
connection:
tls:
mode: MUTUAL

ec2:placement:region alone would match every process on every instance in that region, so it’s combined with the process-scoped process:name selector to scope the identity to just the intended workload rather than the whole host.

A service that’s mid-migration from plain Docker hosts to Kubernetes, or that simply runs on both, can keep a single WorkloadIdentity by giving it one selector group per environment:

apiVersion: core.riptides.io/v1alpha1
kind: WorkloadIdentity
metadata:
name: nginx-web
namespace: riptides-system
spec:
workloadID: my-app/web/nginx
selectors:
- process:name: nginx
k8s:pod:namespace: [production, staging]
- process:name: nginx
docker:label:env: production
connection:
tls:
mode: PERMISSIVE

This issues the same spiffe://example.com/my-app/web/nginx identity to an nginx process whether it’s running in the production/staging Kubernetes namespaces or in a plain Docker container labeled env: production, so the identity, TLS policy, and any egress/credential bindings built on top of it don’t need to be duplicated per environment.

The daemon runs a set of collectors that gather metadata about a process and its host. Each collector namespaces its output under a prefix (process:, k8s:, docker:, ec2:, gcp:, azure:, linuxos:, node:, sysfsdmi:), which is what you reference in a selector key.

Collectors fall into two categories:

  • Process-scoped: produce different metadata per process on the same host: process (from /proc), kubernetes (pod/container context, via the kubelet API), and docker (container context, via the Docker socket). These require resolving the PID to a pod or container, and are unavailable (and silently skipped) for processes that aren’t running in a pod or container.
  • Host-scoped: produce the same metadata for every process on a given host: ec2, gcp, azure, linuxos, node, sysfsdmi. Each cloud collector auto-detects whether the host is actually running there (e.g. azure checks /sys/class/dmi/id/sys_vendor) and produces no labels otherwise.

Because host-scoped collectors don’t distinguish between processes, a selector built only from host-scoped keys (e.g. just ec2:placement:region) matches every process on that host. Combine a host-scoped selector with a process-scoped one (e.g. process:name) to scope a WorkloadIdentity to a specific workload rather than the whole node.

The kubernetes and docker collectors are enabled by default but no-op safely when their dependency is absent (no kubelet reachable, no Docker socket); they don’t fail attestation for non-containerized processes.

Collected from /proc/<pid> for the connecting process.

KeyDescription
process:pidProcess ID.
process:nameExecutable name.
process:cmdlineFull command line.
process:uid / process:uid:effectiveEffective UID.
process:uid:realReal UID.
process:gid / process:gid:effectiveEffective GID.
process:gid:realReal GID.
process:gid:additionalSupplementary group IDs (multi-valued).
process:binary:pathPath to the executable (/proc/<pid>/exe).
process:binary:hashSHA-256 digest of the executable, cached by mount-namespace + device/inode.
process:network:bindingip:port for each socket the process holds in LISTEN state (multi-valued).
process:env:<NAME>Environment variable, keyed by uppercased name. Only collected if the daemon’s extractEnvs option is enabled (default: off).

On the degraded attestation path (when the process has already exited or its PID was reused by the time the daemon re-derives its identity for evaluation), only process:pid, process:name, process:binary:path, process:uid*, and process:gid* (not :additional) are available, reconstructed from the connection-time snapshot rather than a live /proc read. process:cmdline, process:binary:hash, process:gid:additional, process:env:*, and all k8s:*/docker:* labels are unavailable on this path.

Collected by resolving the process’s cgroup to a pod/container ID, then querying the kubelet (or API server) for that pod’s spec and status.

KeyDescription
k8s:pod:namePod name.
k8s:pod:namespacePod namespace.
k8s:pod:serviceaccountPod’s service account name.
k8s:pod:owner:kindOwning controller kind (e.g. replicaset), lowercased.
k8s:pod:owner:kind-with-versionOwning controller’s apiVersion/kind, lowercased.
k8s:pod:owner:nameOwning controller’s name.
k8s:node:nameName of the node the pod is scheduled on.
k8s:container:nameName of the container within the pod.
k8s:container:image:idImage ID of that container.
k8s:label:<key>Value of pod label <key>.
k8s:annotation:<key>Value of pod annotation <key>.
k8s:pod:image:name / k8s:pod:image:idImage name/ID for each container in the pod (multi-valued).
k8s:pod:image:countNumber of containers in the pod.
k8s:pod:init-image:name / :id / :countSame, for init containers.
k8s:pod:ephemeral-image:name / :id / :countSame, for ephemeral containers.

Collected by resolving the process’s cgroup to a container ID, then inspecting that container via the Docker API.

KeyDescription
docker:idContainer ID.
docker:nameContainer name.
docker:cmdlineContainer entrypoint plus arguments.
docker:label:<key>Container label.
docker:env:<NAME>Container environment variable, keyed by uppercased name.
docker:image:nameImage name/tag the container was created from.
docker:image:hashImage ID/digest.
docker:network:modeContainer network mode.
docker:network:hostnameContainer hostname.
docker:port-bindingEach published port binding (multi-valued).

Host-level metadata from the EC2 Instance Metadata Service (IMDS). Distinct from the awsiid:* metadata produced during node attestation: this collector reads IMDS directly rather than parsing the signed identity document, and runs on every workload evaluation, not just once at daemon startup.

KeyDescription
ec2:security-groupsSecurity groups attached to the instance.
ec2:instance:id / :typeInstance ID and type.
ec2:ami:idAMI ID.
ec2:kernel:idKernel ID, if set.
ec2:network:hostname / :local-hostname / :public-hostnameHostnames.
ec2:network:local-ipv4 / :public-ipv4 / :local-ipv6 / :public-ipv6IP addresses.
ec2:network:macPrimary network interface MAC address.
ec2:placement:availability-zone / :availability-zone-idAZ.
ec2:placement:regionRegion.
ec2:placement:group-name / :host-id / :partition-numberPlacement group details.
ec2:services:domain / :partitionAWS partition/domain (e.g. aws, amazonaws.com).

Host-level metadata from the GCP metadata server.

KeyDescription
gcp:instance:id / :nameInstance ID and name.
gcp:instance:cpu-platformCPU platform.
gcp:instance:attribute:<key>Instance metadata attribute (excludes ssh-keys).
gcp:instance:tagNetwork tag (multi-valued).
gcp:instance:image:project / :nameSource image.
gcp:instance:machine:project / :typeMachine type.
gcp:instance:placement:project / :zone / :regionZone and region.
gcp:instance:scheduling:automatic-restart / :onHostMaintenance / :preemptibleScheduling options.
gcp:instance:network:macNIC MAC address (multi-valued).
gcp:instance:network:private-ipv4 / :public-ipv4 / :private-ipv6 / :public-ipv6IP addresses (multi-valued).
gcp:instance:serviceaccount:<name>:emailAttached service account email.
gcp:instance:serviceaccount:<name>:alias / :scopeService account aliases/OAuth scopes (multi-valued).
gcp:project:id / :id:numericProject ID and numeric ID.
gcp:project:attribute:<key>Project-level metadata attribute.

Host-level metadata from the Azure Instance Metadata Service. Distinct from the azureimds:* metadata produced during node attestation: this collector reads the IMDS instance endpoint directly rather than the signed managed-identity token.

KeyDescription
azure:nameVM name.
azure:ostypeLinux or Windows.
azure:priorityVM priority (e.g. Spot, Regular).
azure:provider / :skuCompute provider and SKU.
azure:resourcegroup:nameResource group.
azure:subscription:idSubscription ID.
azure:placement:location / :zone / :groupidRegion, zone, placement group.
azure:vm:size / :id / :publisher / :offer / :versionVM image/size details.
azure:vm:scaleset:nameVMSS name, if the VM is part of one.
azure:tag:<key>Azure resource tag.
azure:network:macNIC MAC address (multi-valued).
azure:network:private-ipv4 / :public-ipv4 / :private-ipv6 / :public-ipv6IP addresses (multi-valued, includes load balancer frontend IPs for public-ipv4).

Host OS metadata from local system files (e.g. /etc/os-release).

KeyDescription
linuxos:nameLinux distribution name (e.g. ubuntu).
linuxos:versionDistribution version.
linuxos:kernel:releaseKernel name-release string.
linuxos:kernel:versionKernel version string.

General host metadata (distribution, virtualization, network interfaces) collected locally, independent of the OS-specific linuxos collector.

KeyDescription
node:hostnameHostname (MDX_NODE_HOSTNAME env var, else /etc/hostname, else system hostname).
node:uuidHost ID.
node:os:type / :versionOS type and platform version.
node:platform:name / :family / :versionPlatform details (e.g. ubuntu / debian / 22.04).
node:virtualization:type / :roleVirtualization technology and role (host/guest).
node:kernel:version / :archKernel version and architecture.
node:network:interface:countNumber of network interfaces.
node:network:interface:nameInterface name (multi-valued).
node:network:interface:<name>:mac_address / :mtu / :indexPer-interface details.
node:network:interface:<name>:ipEvery IP address on that interface (multi-valued).

Hardware/firmware metadata from /sys/class/dmi/id.

KeyDescription
sysfsdmi:bios:date / :release / :vendor / :versionBIOS details.
sysfsdmi:chassis:type / :vendor / :version / :serial / :asset-tagChassis details.
sysfsdmi:product:name / :family / :version / :serial / :skuProduct (hardware model) details.