Downward API integration
Kubernetes Downward API integration for the LinuxGuard agent — LINUXGUARD_NODE_NAME and LINUXGUARD_POD_UID via fieldRef, workload identity derivation.
The LinuxGuard agent derives its workload identifier in ephemeral mode from two Kubernetes Downward API values — the node name and the pod UID. This page documents the binding, the workload-id derivation rule, and the consequences of getting the binding wrong.
Important: The agent refuses to start in ephemeral mode when neither (a) both
LINUXGUARD_NODE_NAMEANDLINUXGUARD_POD_UIDare set, NOR (b)--workload-id <hex>is supplied on the command line. There is no silent UUID fallback. The hard-error message lists all three input sources so the operator can diagnose without reading code.
Required env-var bindings
Two env vars are sourced from the Kubernetes Downward API via valueFrom.fieldRef. Both MUST be present in the Pod spec for ephemeral mode to start; missing either one produces the workload-id derivation error.
env:
- name: LINUXGUARD_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: LINUXGUARD_POD_UID
valueFrom:
fieldRef:
fieldPath: metadata.uidEnv var
fieldRef.fieldPath
What it resolves to
LINUXGUARD_NODE_NAME
spec.nodeName
The name of the node the Pod is scheduled on (e.g., ip-10-0-0-42, worker-node-3, gke-default-pool-abc123-xyz). Kubelet sets this when the Pod is admitted; it never changes for the lifetime of the Pod.
LINUXGUARD_POD_UID
metadata.uid
The RFC 4122 v4 UUID Kubernetes assigns to the Pod (e.g., 7b8f9c1d-3e4a-4b5c-9d6e-1f2a3b4c5d6e). Stable for the lifetime of the Pod; a restart with the same Pod name produces a NEW uid.
Both fields are populated at Pod admission time by the Kubernetes API server. No apiserver lookups occur from inside the container — the values are baked into the container's environ block at scheduling time, and the agent reads them with os.Getenv at startup.
Workload identifier derivation
The agent uses the two env vars to derive its workload identifier. The derivation is deterministic:
The colon separator distinguishes the two-part hash from a malicious value that crafts a node name containing a UID-looking suffix (or vice versa). The hex-encoded SHA-256 is used as the Idempotency-Key HTTP header on the enrolment POST so a retried request does not produce two server identities for the same workload.
The derivation order is:
Explicit
--workload-id <hex>→sha256(explicit). The flag value is hashed (not used verbatim).Downward API env vars →
sha256(LINUXGUARD_NODE_NAME + ":" + LINUXGUARD_POD_UID). Both must be present.Neither set → hard error:
This deliberate hard-error matters: a misconfigured Pod that derived a random UUID at startup would silently enrol a new identity on every restart, producing a sprawl of orphan enrollments in the tenant. The explicit failure forces the operator to fix the manifest before the cluster's agent fleet accumulates phantom identities.
Stability properties
Event
LINUXGUARD_NODE_NAME changes?
LINUXGUARD_POD_UID changes?
Workload-id changes?
Pod restarts on the same node (e.g., kubelet restart, container OOM)
No
No (same Pod object)
No
Pod evicted, rescheduled to the same node
No
Yes (new Pod object → new uid)
Yes
Pod evicted, rescheduled to a different node
Yes
Yes
Yes
DaemonSet rolled (new revision)
No
Yes (new Pod per node)
Yes
Container restarts within the same Pod (e.g., crash loop)
No
No
No
For the DaemonSet shape, this means every DaemonSet roll produces a new workload identity per node. The agent re-enrols on each roll because the workload-id has changed. This is the intended behavior — a freshly-deployed agent gets a fresh server identity rather than inheriting state from the prior pod.
For the sidecar shape, the workload-id changes whenever the application Pod is rescheduled. Sidecars in a Deployment get a new uid on every Pod replacement; sidecars in a StatefulSet get a stable uid only when the Pod is recreated under the same name (which still produces a new uid in Kubernetes' model).
Cross-reference with TLS cache
The TLS cert cache (--tls-cache, see Ephemeral mode § TLS cache restart semantics) keys its invalidation tag on the workload-id:
This means a workload-id change (e.g., Pod evicted and rescheduled) automatically invalidates the cached cert chain, and the agent performs a fresh enrolment under the new identity. Operators do not need to manually clear the cache on reschedule.
Security context
The Downward API binding itself has no additional security context implications beyond the base ephemeral mode security context — see Distroless image § Security context. Both env vars are public Kubernetes metadata (node names and Pod UIDs appear in kubectl get pods -o wide output) and do not require Secret-style protection.
The LINUXGUARD_POD_UID env var is exposed in /proc/<pid>/environ for the agent's lifetime — this is acceptable because the Pod UID is not a secret. The token (LINUXGUARD_ENROLL_TOKEN) IS a secret and receives the os.Unsetenv scrub at startup; the Downward API env vars do NOT receive that treatment and remain readable for the agent's lifetime.
Host paths
The Downward API binding requires no additional host-path mounts beyond those documented in the per-orchestrator spoke (Kubernetes DaemonSet § Host paths or Ephemeral mode § Host paths). The fieldRef values are baked into the container's environ block at admission time — no node-level filesystem access is involved.
Pod Security Standard compatibility
PSS profile
Downward API fieldRef compatible?
Notes
privileged
Yes
No PSS-level constraints.
baseline
Yes
The Downward API fieldRef mechanism is permitted under baseline; the binding involves no privileged operations.
restricted
Yes
The Downward API fieldRef mechanism is permitted under restricted; the binding involves no privileged operations.
The Downward API binding is PSS-neutral. The agent's other capability requirements (CAP_BPF, CAP_PERFMON, hostPID) are what classify the Pod spec as privileged — not the Downward API itself. A future PSS-compatible deployment shape would inherit the same Downward API binding unchanged.
RBAC
The Downward API fieldRef mechanism requires no RBAC — the values are populated by the kubelet at Pod admission time, not by an apiserver call from inside the container. The Pod's ServiceAccount needs no Role or ClusterRole to read spec.nodeName or metadata.uid because the agent does not read them via the apiserver; it reads them from os.Environ.
This contrasts with the Projected ServiceAccount Token pattern (where the kubelet projects a token into a volume) — that pattern DOES require a workload-identity-binding RBAC, but the LinuxGuard agent does not use it.
Common mistakes
Hardcoding the node name in a ConfigMap
This populates the same node name on every pod of the DaemonSet. The workload-id collapses to a single value across the cluster, every agent enrols under the same identity, and the backend de-duplicates them — silently dropping coverage of every node except the first.
Correct: Always use valueFrom.fieldRef.fieldPath: spec.nodeName.
Hardcoding a UUID
The same problem — the workload-id derives the same value across all pods. Every pod enrols under the same identity; coverage collapses to one phantom workload.
Correct: Always use valueFrom.fieldRef.fieldPath: metadata.uid.
Using metadata.name instead of metadata.uid
metadata.name instead of metadata.uidA Pod's name is not the same as its uid. In a DaemonSet, names follow the pattern <daemonset-name>-<hash> and DO vary per pod; this misconfiguration may appear to work in a fresh deployment but produces collisions whenever a Pod is recreated under the same name (StatefulSet recreations under the same ordinal, for example).
Correct: Use metadata.uid — the only field guaranteed unique across the entire cluster lifetime.
Forgetting one of the two env vars
The agent refuses to start with the hard-error message above. There is no fallback to "node-name-only" workload identity by design.
Correct: Always set BOTH env vars together, OR supply --workload-id <hex> on the command line as the explicit override.
Non-Kubernetes ephemeral deployment
In Docker / Podman ephemeral deployments (no Kubernetes), spec.nodeName and metadata.uid do not exist. The Downward API binding is Kubernetes-specific.
Correct: Supply both env vars manually via -e LINUXGUARD_NODE_NAME=<hostname> -e LINUXGUARD_POD_UID=<uuid>, OR pass --workload-id <hex> as the explicit identifier. See Ephemeral mode § Example: Docker run.
Verification
After applying a manifest that uses the Downward API binding, verify the env vars are populated correctly:
The output should show the derived workload-id and the node name. If show-config reports a missing workload-id, the most common causes are:
The Pod was not scheduled with the Downward API env block in its spec — recheck
kubectl -n linuxguard get pod $POD -o yaml | grep -A 3 LINUXGUARD_NODE_NAME.A
fieldRef.fieldPathis wrong — confirm it is exactlyspec.nodeNamefor the node andmetadata.uidfor the Pod UID.The values were overridden by a ConfigMap or
envFromblock that took precedence — verify env-var precedence withkubectl exec $POD -- env | grep LINUXGUARD.
Next Step: Enrollment tokens →
Related: Container deployment hub | Ephemeral mode | Kubernetes DaemonSet | Environment variables | start CLI reference
Last updated
Was this helpful?