Ephemeral mode

Configure the LinuxGuard agent in ephemeral container mode — TOTP enrol, in-memory cert chain, PID 1 auto-detection, TLS cache restart semantics.

Ephemeral mode is the container-friendly operating shape of the LinuxGuard agent. The agent enrols once via a short-lived TOTP token, holds the resulting mTLS cert chain in memory, skips PID-file machinery (the orchestrator owns lifecycle), and never writes to /var/lib/linuxguard/. This page documents the trigger conditions, the runtime behavior, and the restart semantics — including the optional TLS cache that survives container restarts without re-enrolling.

Important: Ephemeral mode is selected by any of the following triggers: the --ephemeral flag, a non-empty --enroll-token (or its LINUXGUARD_ENROLL_TOKEN env-var binding), os.Getpid() == 1 (the agent is the container's init process), or the LINUXGUARD_PID1_CHILD re-exec sentinel. Explicit --ephemeral is NOT required when any of the implicit triggers fires.

When ephemeral mode is the right choice

Ephemeral mode is designed for containers whose lifecycle is owned by the orchestrator and whose workload identity is derived from the container's runtime context, not from a persisted enrollment on disk:

  • Kubernetes DaemonSet pods — node-level coverage, workload identity from the Downward API.

  • Kubernetes sidecars — pod-level coverage, workload identity from metadata.uid.

  • Docker / Podman containers running as PID 1 — single-tenant edge appliance, CI runner, build container.

  • One-shot CI / build jobs — a fresh enrol per invocation, no persistent state.

It is NOT the right choice for:

  • A long-lived host installation managed by systemd. Use the per-distro install guides for that shape.

  • A container that runs the agent as a non-PID-1 worker alongside an application. The PID-file machinery the agent skips in ephemeral mode is the same machinery that prevents two start invocations from competing for the same host identity. Use the host install path inside the container instead.

Flags consumed by ephemeral mode

The flags below are the surface ephemeral mode actually consumes. The full start flag reference (defaults, env-var bindings, source line numbers) lives at start CLI reference.

Flag
Purpose in ephemeral mode

--ephemeral

Explicit trigger. Optional when any implicit trigger fires (PID 1, non-empty --enroll-token, LINUXGUARD_PID1_CHILD).

--enroll-token

TOTP enrolment token. Prefer the LINUXGUARD_ENROLL_TOKEN env var so the token does not appear on the command line (see Enrollment tokens).

--tenant-id

Tenant the agent enrols into. Required on the TOTP path — the backend's /agent/enroll handler rejects with 400 "tenantId required for TOTP enrollment" when missing. Bound to LINUXGUARD_TENANT_ID.

--workload-id

Explicit workload identifier (sha256 hex). Required only when the Downward API env vars are absent. Hard-error if neither --workload-id nor LINUXGUARD_NODE_NAME + LINUXGUARD_POD_UID are set.

--node-id

Kubernetes node name from the Downward API. Bound to LINUXGUARD_NODE_NAME. Feeds the workload-id derivation when --workload-id is not provided.

--tls-cache

Opt-in: mirror the cert chain + token-hash tag to /run/linuxguard/tls/ (mode 0700/0600) so a container restart can reuse the cache without re-enrolling. Off by default. Requires a tmpfs mount at /run/linuxguard.

--loader-embedded

Force the in-process embedded eBPF loader. Auto-on under PID 1 or --ephemeral when the binary was built with the loader_embedded build tag (the published image is built with this tag set). Bound to LINUXGUARD_LOADER_EMBEDDED.

--api-url

Override the LinuxGuard API base URL. Bound to LINUXGUARD_API_URL. Whitespace-only values treated as empty. Channel default applies when absent.

LINUXGUARD_POD_UID is read directly via os.Getenv — it has no flag binding. It pairs with LINUXGUARD_NODE_NAME for the workload-id derivation.

PID 1 auto-detection

The agent inspects its own PID at startup. When os.Getpid() == 1 (or when the LINUXGUARD_PID1_CHILD sentinel is set by the agent's own init re-exec wrapper), ephemeral semantics activate even if the --ephemeral flag was not passed. This matches the common container pattern of running the agent as the container's main process (Dockerfile CMD ["start"], distroless image default).

When PID 1 is detected, the agent additionally:

  1. Skips agent.EnsureNotRunning and agent.WritePID so there is no PID-file collision with itself across re-execs.

  2. Installs the PID-1 init shim (build tag pid1) that reaps zombie children — required for any container that may spawn subprocesses, even transiently.

  3. Re-execs itself with LINUXGUARD_PID1_CHILD=1 set; this sentinel marks the second invocation as the actual agent process, not the init shim.

LINUXGUARD_PID1_CHILD is internal-only. Operators must NOT set it manually. Documented in Environment variables only so process-introspection tooling (e.g., reading /proc/<pid>/environ during incident triage) can identify the value as agent-internal rather than user-supplied.

Workload identity derivation

The workload identifier is the value the backend uses to distinguish one ephemeral agent from another. It is also used as the Idempotency-Key header on the enrolment POST so a retried request does not produce two server identities.

The derivation order is:

  1. Explicit --workload-id <hex>sha256(explicit). The flag value is hashed (not used verbatim) so misuse — passing a non-hex value, passing whitespace — produces a deterministic identifier rather than a backend rejection.

  2. Downward API env varssha256(LINUXGUARD_NODE_NAME + ":" + LINUXGUARD_POD_UID). Both env vars must be set; either one missing falls through to error.

  3. Neither set → hard error: --ephemeral requires either Downward API env vars (LINUXGUARD_NODE_NAME and LINUXGUARD_POD_UID, set via valueFrom.fieldRef) OR --workload-id CLI flag; none of the three were present.

There is no silent UUID fallback. An ephemeral agent that cannot derive a workload identity refuses to start, by design — otherwise a misconfigured Pod would silently enrol with a random identity on every restart and produce a sprawl of orphan enrollments in the tenant.

For the Kubernetes pattern using valueFrom.fieldRef, see Downward API integration.

TLS cache restart semantics

By default, an ephemeral agent that restarts performs a fresh enrolment with the supplied TOTP token. This is the safest default: no cert material survives container exit; a stolen image filesystem yields no live mTLS keys.

For deployments where restart is frequent and re-enrolment is undesirable (e.g., a sidecar that restarts on application crashes), the optional --tls-cache flag mirrors the cert chain + a token-hash tag to a tmpfs at /run/linuxguard/tls/:

File
Mode
Contents

/run/linuxguard/tls/agent.crt

0600

mTLS client certificate

/run/linuxguard/tls/agent.key

0600

mTLS client private key

/run/linuxguard/tls/ca.crt

0600

Backend CA chain

/run/linuxguard/tls/.tag

0600

hex(sha256(token + ":" + workloadID)) — token-hash tag used to invalidate stale caches across token rotations

On restart with --tls-cache, the agent computes the current .tag value from the env-var token + workload-id and compares it against the file. If they match, the cached cert chain is reused without re-enrolling. If they diverge — because the TOTP token has been rotated, or because the workload-id changed (a new pod UID under Kubernetes) — the cache directory is deleted and a fresh enrolment runs.

Important: --tls-cache requires LINUXGUARD_ENROLL_TOKEN (or a pre-computed EnrollTokenTag value) to be set. The agent refuses to seed a cache that cannot be cryptographically invalidated — otherwise a token rotation would silently leave the agent using cert material attached to the prior identity. Without the token + tag pair, the agent returns the error --tls-cache requires LINUXGUARD_ENROLL_TOKEN (or pre-computed EnrollTokenTag) to be set; refusing to seed a cache that cannot be cryptographically invalidated.

The tmpfs mount is mandatory when --tls-cache is active. Without it, the cert chain ends up on the container's writable layer — discoverable via docker diff, persisted in image-export workflows, and survivable to disk. The Docker run incantation is:

The Kubernetes equivalent (in the Pod spec) is an emptyDir with medium: Memory:

Restart semantics summary

Scenario

Without --tls-cache

With --tls-cache

Container restart, same TOTP token, same workload-id

Fresh enrol with the token

Cert chain reused from tmpfs; no enrol call

Container restart, rotated TOTP token, same workload-id

Fresh enrol with the new token

.tag mismatch → cache deleted → fresh enrol with the new token

Container restart, same TOTP token, new workload-id (e.g., new pod UID)

Fresh enrol under the new identity

.tag mismatch → cache deleted → fresh enrol under the new identity

Container exit (SIGTERM / SIGINT)

Cert chain is GC'd from memory; exits with os.Exit(143) / os.Exit(130) respectively

Cert chain is GC'd from memory; the tmpfs cache survives until the next start (tmpfs is the orchestrator's lifecycle, not the container's)

Security context

Ephemeral mode runs the agent under the same security context as any other container deployment of the distroless image — see Distroless image § Security context for the canonical reference. Specifically:

  • Runs as nonroot UID 65532.

  • Uses Linux file capabilities (CAP_BPF, CAP_PERFMON, CAP_DAC_READ_SEARCH, CAP_SYS_PTRACE, CAP_SETPCAP) baked into the binary.

  • Requires the runtime to add the same capabilities to the bounding set (--cap-add or securityContext.capabilities.add).

  • Requires a custom seccomp profile (or --security-opt seccomp=unconfined for UAT/dev) so perf_event_open(2) is permitted.

  • Sets runAsNonRoot: true, allowPrivilegeEscalation: false, readOnlyRootFilesystem: true at the Pod level.

Host paths

Ephemeral mode requires fewer host paths than the DaemonSet shape because it does not need to observe other workloads on the node:

Path
Mount
Purpose

/run/linuxguard

tmpfs, mode 0700, size 10m, read-write

TLS cert cache when --tls-cache is set. Mandatory when the flag is active; optional otherwise.

/sys/kernel/tracing

hostPath, read-only

Required for probe attach. Omit when running on a kernel without eBPF or in DegradedNoEBPFArch mode.

/sys/fs/bpf

hostPath, read-write

Required when the loader pins maps.

/proc

hostPath, read-only

Required when ephemeral mode is observing other host processes (rare; typical ephemeral is per-pod or per-container scope only).

The DaemonSet shape, by contrast, mounts host PID + the full host-path set. See Kubernetes DaemonSet § Host paths.

Pod Security Standard compatibility

PSS profile
Compatible?
Notes

privileged

Yes

Default profile that admits all capabilities and host namespaces required by the agent.

baseline

Yes — with caveat

The agent's CAP_BPF + CAP_PERFMON are within the baseline allowlist; tmpfs mount and runAsNonRoot satisfy baseline constraints. Caveat: ephemeral mode without hostPID works under baseline; ephemeral mode with hostPID: true requires privileged.

restricted

Not currently supported

The agent's required CAP_BPF and CAP_PERFMON are NOT in the restricted allowlist (which drops all capabilities by default). Requires an explicit AdmissionPolicy waiver. Consult support.

RBAC

Ephemeral mode makes no Kubernetes API calls. Workload identity comes from Downward API env vars injected at Pod admission time (no apiserver lookups), and telemetry ships via outbound mTLS to the LinuxGuard API (no in-cluster service mesh).

The Pod runs under any ServiceAccount (the namespace default SA is acceptable). No Role, ClusterRole, RoleBinding, or ClusterRoleBinding needs to be created for the agent itself. RBAC is required only for sibling components (e.g., the profile-installer DaemonSet that writes seccomp/AppArmor profiles to host paths — that DaemonSet's RBAC is documented in Kubernetes DaemonSet § RBAC).

Example: Docker run, ephemeral, no cache

The simplest ephemeral invocation. Cert chain lives in memory only; a restart re-enrols with whatever value is then in LINUXGUARD_ENROLL_TOKEN.

Notes on the flags:

  • --cap-add adds the five required caps to the container's bounding set so the file-cap transition at execve succeeds.

  • --security-opt seccomp=unconfined is acceptable for UAT/dev; production should use a custom seccomp profile that explicitly permits perf_event_open(2).

  • -v /sys/kernel/tracing:/sys/kernel/tracing:ro is the tracefs bind-mount required for probe attach.

  • The four env vars supply the token, the tenant, and the workload-id inputs. LINUXGUARD_ENROLL_TOKEN is read once and immediately unset; the other three persist for the lifetime of the agent process.

Example: Docker run, ephemeral, with TLS cache

The same agent with --tls-cache enabled so a container restart reuses the cert chain (within the same TOTP token + workload-id):

The --restart unless-stopped policy is appropriate when --tls-cache is in use because restart no longer triggers re-enrolment (within the same token + workload-id window).

Example: Kubernetes DaemonSet env block (abbreviated)

The full DaemonSet manifest (with securityContext, host-path volumes, PSS comment, and RBAC) lives in Kubernetes DaemonSet. The ephemeral-mode env block within that manifest is:

The TOTP token is sourced from a Kubernetes Secret (not a literal value) per the enrollment-tokens guide. The node name and pod UID come from the Downward API per Downward API integration.


Next Step: Kubernetes DaemonSet →

Related: Container deployment hub | Distroless image reference | Downward API integration | Enrollment tokens | start CLI reference | Environment variables

Last updated

Was this helpful?