Kubernetes DaemonSet

Kubernetes DaemonSet deployment of the LinuxGuard agent — copy-pasteable YAML with PSS profile comment, line-by-line securityContext rationale, eBPF prerequisites, and RBAC.

The DaemonSet shape runs one LinuxGuard agent pod per node. The agent observes every workload on that node through host PID + tracefs, so a single DaemonSet covers all containerized and host-native workloads on the cluster. This page provides a complete, copy-pasteable manifest with line-by-line rationale for every privilege the Pod requests.

Important: The manifest below is classified # Pod Security Standard: privileged because it requests CAP_BPF, CAP_PERFMON, hostPID: true, and host-path volumes for /sys/kernel/tracing and /sys/fs/bpf. These cannot be granted under the baseline or restricted PSS profiles. Clusters that enforce restricted cluster-wide require an explicit AdmissionPolicy waiver — see Pod Security Standard compatibility below.

Prerequisites

Before applying the manifest, verify the cluster meets the following requirements. Skipping any of these produces a DaemonSet that admits but does not produce eBPF telemetry — the agent runs in DEGRADED_PROBES_PARTIAL mode and the cluster's evidence chain has a silent gap.

Kernel version

The agent's eBPF probes require Linux kernel 5.8 or later (the kernel that introduced CAP_BPF and CAP_PERFMON). On older kernels the agent falls back to CAP_SYS_ADMIN-based loading, but the file-cap transition baked into the binary does not include SYS_ADMIN and the loader degrades. Verify per-node:

kubectl get nodes -o wide   # check KERNEL-VERSION column

kernel.perf_event_paranoid sysctl

The agent calls perf_event_open(2) via link.Tracepoint() to attach the eBPF programs. This syscall is gated by the host sysctl kernel.perf_event_paranoid. The required value is <= 2.

Ubuntu 24.04 ships with kernel.perf_event_paranoid = 4 by default. SET THIS AT NODE LEVEL — it CANNOT be enforced from the Pod spec.

echo 'kernel.perf_event_paranoid = 2' | sudo tee /etc/sysctl.d/99-linuxguard-ebpf.conf
sudo sysctl --system

Seccomp + AppArmor profiles installed on every node

The reference seccomp profile (linuxguard-agent.seccomp.json) and AppArmor profile (linuxguard-agent.apparmor) MUST be present on every node before the agent DaemonSet schedules. The recommended pattern is a sibling DaemonSet that runs an init container with privileged: true to write the profiles to the host's /var/lib/kubelet/seccomp/profiles/ and /etc/apparmor.d/ directories.

The profile-installer DaemonSet (reference manifest available in the agent packaging artifacts shipped with each release) follows the same shape used by KubeArmor and Falco. Adapt your existing profile-installer if your cluster already runs one.

Image-registry pull access

The published image at packages.linuxguard.io/linuxguard-agent:v3.0.0 is signed; verify the signature once at build time per Distroless image § Image signing. If your cluster pulls from a mirrored registry, mirror the digest (not the tag) so the deployed bytes match the signed bytes.

The manifest

Line-by-line rationale

Each privilege the Pod requests is documented below with the rationale and the consequence of removing it.

hostPID: true

Required for the node-level coverage shape. With hostPID: true, the agent's /proc view exposes every process on the node — including processes inside other containers. Without it, the agent sees only its own PID namespace (the Pod's /proc) and produces telemetry only about itself, defeating the DaemonSet purpose.

If your cluster forbids hostPID, use the sidecar shape instead.

hostNetwork: false

The agent does NOT need host networking. It makes outbound HTTPS to the LinuxGuard API via the Pod's own network namespace; the Pod's CNI handles the outbound routing. Setting hostNetwork: true would put the agent on the host network namespace unnecessarily and expand blast radius.

capabilities.add: [BPF, PERFMON, DAC_READ_SEARCH, SYS_PTRACE, SETPCAP]

These five capabilities are the bounding-set requirement that lets the file-cap transition (baked into the agent ELF) succeed at execve:

  • BPF — load eBPF programs.

  • PERFMON — call perf_event_open(2) for tracepoint attach.

  • DAC_READ_SEARCH — read /proc/<pid>/exe and /proc/<pid>/maps for arbitrary PIDs (process introspection).

  • SYS_PTRACE — required by the BTF resolution path on older kernels.

  • SETPCAP — required for DropCapabilitiesPostAttach(). Without SETPCAP, the post-attach PR_CAPBSET_DROP calls return EPERM (non-fatal, but the agent silently holds CAP_BPF for its full lifetime).

capabilities.drop: [ALL]

Drop every capability not explicitly added. Defense-in-depth: a future binary that gains additional caps via image rebuild does not silently get to use them at runtime.

appArmorProfile.localhostProfile: linuxguard-agent

The AppArmor profile is installed by the profile-installer DaemonSet (sibling deployment, not shown above). The profile constrains the agent's filesystem access patterns so a compromised binary cannot, for example, write to /etc/sudoers or read /root/.ssh/.

runAsNonRoot: true + runAsUser: 65532

The distroless base image runs as nonroot UID 65532 by default. Enforcing it at the Pod level produces an admission rejection rather than a silent base-image override — protective against a downstream layer that sets USER root.

allowPrivilegeEscalation: false

The agent does not need to setuid or setgid at runtime. The file caps baked into the binary provide the required privileges through execve's standard cap-transition rules.

readOnlyRootFilesystem: true

The agent writes only to /run/linuxguard (the tls-cache tmpfs) and /tmp (writable in the distroless base). The image filesystem is fully read-only at runtime.

seccompProfile.localhostProfile: profiles/linuxguard-agent.json

The default Docker / containerd seccomp profile blocks perf_event_open(2) regardless of CAP_PERFMON. The custom profile shipped with the agent (linuxguard-agent.seccomp.json in the agent packaging artifacts) adds perf_event_open to the allowlist. Without this override, probe attach trips "opening tracepoint perf event: permission denied" and the agent degrades.

Security context

The DaemonSet Pod requests the following security context — every field has the same rationale as the equivalent setting on the Distroless image reference; the table below summarizes what the manifest above sets and links each setting to its full rationale in Line-by-line rationale.

Surface
Setting in the manifest
Rationale

hostPID

true

Node-level coverage. See hostPID: true.

hostNetwork

false

Agent does not need host networking. See hostNetwork: false.

capabilities.add

[BPF, PERFMON, DAC_READ_SEARCH, SYS_PTRACE, SETPCAP]

Bounding-set caps for the file-cap execve transition. See capabilities.add.

capabilities.drop

[ALL]

Defense-in-depth. See capabilities.drop.

seccompProfile

Localhost: profiles/linuxguard-agent.json

Custom profile permits perf_event_open(2). See seccompProfile.

appArmorProfile

Localhost: linuxguard-agent

AppArmor constraints on filesystem access. See appArmorProfile.

runAsNonRoot

true

Image runs as nonroot UID 65532. See runAsNonRoot: true.

runAsUser / runAsGroup

65532 / 65532

Distroless nonroot UID/GID.

allowPrivilegeEscalation

false

File caps make setuid unnecessary. See allowPrivilegeEscalation: false.

readOnlyRootFilesystem

true

Agent does not write to image filesystem. See readOnlyRootFilesystem: true.

The manifest's security context corresponds to PSS privileged — see § Pod Security Standard compatibility below for the classification and the restricted-cluster guidance.

Pod Security Standard compatibility

The manifest above is classified as privileged under the Kubernetes Pod Security Standards.

PSS profile
Compatible with this manifest?
What's blocked

privileged

Yes

No PSS-level constraints.

baseline

No

baseline disallows hostPID: true, host-path volumes for /sys/kernel/tracing and /proc, and the BPF + PERFMON capabilities.

restricted

No

restricted requires runAsNonRoot: true (satisfied), but ALSO disallows ALL added capabilities, host namespaces, and most hostPath volumes.

Running under restricted clusters

If your cluster enforces restricted cluster-wide and the policy CANNOT be relaxed at the namespace level, two options exist:

  1. AdmissionPolicy waiver. Carve out the linuxguard namespace via a ValidatingAdmissionPolicy that exempts the agent's specific privileges. This is the auditable path: the waiver lives in cluster config, is reviewable in change management, and applies only to the agent's namespace + ServiceAccount.

  2. Consult support. v4.0 does not ship a restricted-compatible deployment shape. If your environment requires one, open a support case so the alternative deployment patterns (e.g., a host-installed agent reaching into the cluster's container runtime) can be evaluated.

Do NOT attempt to run the manifest with capabilities stripped to fit restricted — the agent will start and silently produce no behavioral telemetry, and the cluster's evidence chain will have a silent gap.

Host paths

Path
Mount mode
Justification
Removable?

/sys/kernel/tracing

read-only hostPath

link.Tracepoint() reads /sys/kernel/tracing/events/<group>/<name>/id to resolve tracepoint IDs at probe-attach time. The distroless image does not include /sys mounts.

No — without this, probe attach fails with "neither debugfs nor tracefs are mounted" and the agent degrades.

/sys/fs/bpf

read-write hostPath

BPF pinned maps. Required when the in-process loader pins maps for cross-process sharing.

No — pinned-map creation fails without write access.

/proc

read-only hostPath (mounted at /host/proc)

Process introspection. The agent walks the host's /proc for the workload-discovery pass when hostPID is set.

No — without it, the agent observes only its own PID namespace.

/run/linuxguard

tmpfs (emptyDir medium: Memory)

TLS cert cache when --tls-cache is set. Tmpfs (not durable storage) so cert material never reaches disk.

Only if --tls-cache is removed from args.

Security Note: All host-path mounts are read-only except /sys/fs/bpf (which requires write for pinned-map creation) and /run/linuxguard (which is a tmpfs, not a host path). Do NOT mount the host root (/) — the agent does not need it; mounting it expands the blast radius of a container escape to the entire node. See the anti-patterns hub section.

RBAC

The agent makes no Kubernetes apiserver calls at runtime. Workload identity is derived from the Downward API env vars (injected at admission time, no apiserver lookups), and telemetry ships via outbound mTLS to the LinuxGuard API.

The minimal RBAC is therefore just a ServiceAccount with no Role or ClusterRole:

The Pod runs under this ServiceAccount. No RoleBinding or ClusterRoleBinding is required because no Role exists to bind.

Profile-installer DaemonSet RBAC

The sibling profile-installer DaemonSet (which writes seccomp + AppArmor profiles to host paths) DOES require its own ServiceAccount + RBAC for the init-container's privileged host-path writes. Its RBAC ships in the agent packaging artifacts.

linuxguard-enroll Secret

The Secret referenced by the valueFrom.secretKeyRef env block above must be created out-of-band before the DaemonSet rolls out:

The token is single-use per pod identity — rotate it on every DaemonSet roll if you want each pod to enrol with a fresh identity. See Enrollment tokens for the rotation pattern.

Sidecar alternative

The DaemonSet shape is preferred. The sidecar shape exists for the narrow case where pod-level isolation is mandated by policy and a node-level pod is explicitly rejected.

Differences from the DaemonSet manifest:

  • hostPID: false — the sidecar observes only its sibling containers in the same Pod.

  • No tolerations — sidecars run alongside the application.

  • The agent runs as an additional container in the application's Pod spec, not in a dedicated namespace.

  • Resource cost is per-pod, not per-node — at 100 application pods, you run 100 agent sidecars instead of one agent per node.

Important: Do NOT run both a DaemonSet AND a per-pod sidecar. The DaemonSet's hostPID: true already observes every process on the node, including the sidecar's host. Double-deploying produces duplicate events that double-count in console alerts and SIEM ingestion. See the anti-patterns hub section.

Node selection and tolerations

The manifest above selects kubernetes.io/os: linux and tolerates all taints. Common adjustments:

  • Exclude control-plane nodes — if your security model excludes the control plane from coverage, replace the Exists toleration with a NoSchedule toleration that lists only the worker-node taints.

  • Restrict to a specific node pool — add a nodeSelector or nodeAffinity rule.

  • Exclude legacy kernel nodes — the agent degrades cleanly on kernels older than 5.8, but excluding those nodes via affinity makes the coverage gap explicit.

Verification

After applying the manifest, verify the DaemonSet is fully ready and the agent is producing heartbeats:

A healthy DaemonSet produces a heartbeat log line within 20 seconds of pod start and probe reports kernel/bpf/fanotify/netlink/audit/caps all green. A degraded probe report points to a missing prerequisite — most commonly kernel.perf_event_paranoid > 2 or a missing seccomp profile.


Next Step: Downward API integration →

Related: Container deployment hub | Distroless image reference | Ephemeral mode | Enrollment tokens | probe CLI reference

Last updated

Was this helpful?