KubeArmor Daemon
Welcome back to the KubeArmor tutorial! In our journey so far, we've explored the key components that make KubeArmor work:
Security Policies: Your rulebooks for security.
Container/Node Identity: How KubeArmor knows who is doing something.
Runtime Enforcer: The component that translates policies into kernel rules and blocks forbidden actions.
System Monitor: KubeArmor's eyes and ears, observing system events.
BPF (eBPF): The powerful kernel technology powering much of the monitoring and enforcement.
In this chapter, we'll look at the KubeArmor Daemon. If the other components are like specialized tools or senses, the KubeArmor Daemon is the central brain and orchestrator that lives on each node. It brings all these pieces together, allowing KubeArmor to function as a unified security system.
What is the KubeArmor Daemon?
The KubeArmor Daemon is the main program that runs on every node (Linux server) where you want KubeArmor to provide security. When you install KubeArmor, you typically deploy it as a DaemonSet in Kubernetes, ensuring one KubeArmor Daemon pod runs on each of your worker nodes. If you're using KubeArmor outside of Kubernetes (on a standalone Linux server or VM), the daemon runs directly as a system service.
Think of the KubeArmor Daemon as the manager for that specific node. Its responsibilities include:
Starting and stopping all the other KubeArmor components (System Monitor, Runtime Enforcer, Log Feeder).
Communicating with external systems like the Kubernetes API server or the container runtime (Docker, containerd, CRI-O) to get information about running workloads and policies.
Building and maintaining the internal mapping for Container/Node Identity.
Fetching and processing Security Policies (KSP, HSP, CSP) that apply to the workloads on its node.
Instructing the Runtime Enforcer on which policies to load and enforce for specific containers and the host.
Receiving security events and raw data from the System Monitor.
Adding context (like identity) to raw events received from the monitor.
Forwarding processed logs and alerts to the Log Feeder for external consumption.
Handling configuration changes and responding to shutdown signals.
Without the Daemon, the individual components couldn't work together effectively to provide end-to-end security.
Why is the Daemon Needed? A Coordinated Use Case
Let's trace the journey of a security policy and a system event, highlighting the Daemon's role.
Imagine you want to protect a specific container, say a database pod with label app: my-database
, by blocking it from executing the /bin/bash
command. You create a KubeArmor Policy (KSP) like this:
And let's say later, a process inside that database container actually attempts to run /bin/bash
.
Here's how the KubeArmor Daemon on the node hosting that database pod orchestrates the process:
Policy Discovery: The KubeArmor Daemon, which is watching the Kubernetes API server, detects your new
block-bash-in-db
policy.Identify Targets: The Daemon processes the policy's
selector
(app: my-database
). It checks its internal state (built by talking to the Kubernetes API and container runtime) to find which running containers/pods on its node match this label. It identifies the specific database container.Prepare Enforcement: The Daemon takes the policy rule (
Block /bin/bash
) and tells its Runtime Enforcer component to load this rule specifically for the identified database container. The Enforcer translates this into the format needed by the underlying OS security module (AppArmor, SELinux, or BPF-LSM) and loads it into the kernel.System Event: A process inside the database container tries to execute
/bin/bash
.Event Detection & Enforcement: The OS kernel intercepts this action. If using BPF-LSM, the Runtime Enforcer's BPF program checks the loaded policy rules (which the Daemon put there). It sees the rule to
Block
/bin/bash
for this container's identity. The action is immediately blocked by the kernel.Event Monitoring & Context: Simultaneously, the System Monitor's BPF programs also detect the
exec
attempt on/bin/bash
. It collects details like the process ID, the attempted command, and the process's Namespace IDs. It sends this raw data to the Daemon (via a BPF ring buffer).Event Processing: The Daemon receives the raw event from the Monitor. It uses the Namespace IDs to look up the Container/Node Identity in its internal map, identifying that this event came from the database container (
app: my-database
). It sees the event includes an error code indicating it was blocked by the security module.Log Generation: The Daemon formats a detailed log/alert message containing all the information: the event type (process execution), the command (
/bin/bash
), the outcome (Blocked), and the workload identity (container ID, Pod Name, Namespace, Labels).Log Forwarding: The Daemon sends this formatted log message to its Log Feeder component, which then forwards it to your configured logging/monitoring system.
This diagram illustrates how the Daemon acts as the central point, integrating information flow and control between external systems (K8s, CRI), the low-level kernel components (Monitor, Enforcer), and the logging/alerting system.
The Daemon Structure
Let's look at the core structure representing the KubeArmor Daemon in the code. It holds references to all the components it manages and the data it needs.
Referencing KubeArmor/core/kubeArmor.go
:
Explanation:
The
KubeArmorDaemon
struct contains fields likeNode
(details about the node it runs on),K8sEnabled
(whether it's in a K8s cluster), and maps/slices to store information aboutK8sPods
,Containers
,EndPoints
, and parsedSecurityPolicies
. Locks (*sync.RWMutex
) are used to safely access this shared data from multiple parts of the Daemon's logic.Crucially, it has pointers to the other main components:
Logger
,SystemMonitor
, andRuntimeEnforcer
. This shows that the Daemon owns and interacts with instances of these components.WgDaemon
is async.WaitGroup
used to track background processes (goroutines) started by the Daemon, allowing for a clean shutdown.
Daemon Lifecycle: Initialization and Management
When KubeArmor starts on a node, the KubeArmor()
function in KubeArmor/main.go
(which calls into KubeArmor/core/kubeArmor.go
) initializes and runs the Daemon.
Here's a simplified look at the initialization steps within the KubeArmor()
function:
Explanation:
NewKubeArmorDaemon
is like the constructor; it creates the Daemon object and initializes its basic fields and locks. Pointers to components likeLogger
,SystemMonitor
,RuntimeEnforcer
are initially zeroed.The main
KubeArmor()
function then calls dedicatedInit...
methods on thedm
object (likedm.InitLogger()
,dm.InitSystemMonitor()
,dm.InitRuntimeEnforcer()
).These
Init...
methods are responsible for creating the actual instances of the other components using their respectiveNew...
functions (e.g.,mon.NewSystemMonitor()
) and assigning the returned object to the Daemon's pointer field (dm.SystemMonitor = ...
). They pass necessary configuration and references (like theLogger
) to the components they initialize.After initializing components, the Daemon starts goroutines (using
go dm.SomeFunction()
) for tasks that need to run continuously in the background, like serving logs, monitoring system events, or watching external APIs.The main flow then typically waits for a shutdown signal (
<-sigChan
).When a signal is received,
dm.DestroyKubeArmorDaemon()
is called, which in turn callsClose...
methods on the components to shut them down gracefully.
This demonstrates the Daemon's role in the lifecycle: it's the entity that brings the other parts to life, wires them together by passing references, starts their operations, and orchestrates their shutdown.
Daemon as the Information Hub
The Daemon isn't just starting components; it's managing the flow of information:
Policies In: The Daemon actively watches the Kubernetes API (or receives updates in non-K8s mode) for changes to KubeArmor policies. When it gets a policy, it stores it in its
SecurityPolicies
orHostSecurityPolicies
lists and notifies the Runtime Enforcer to update the kernel rules for affected workloads.Identity Management: The Daemon watches Pod/Container/Node events from Kubernetes and the container runtime. It populates internal structures (like the
Containers
map) which are then used by the System Monitor to correlate raw kernel events with workload identity (Container/Node Identity). While theNsMap
itself might live in the Monitor (as seen in Chapter 4 context), the Daemon is responsible for gathering the initial K8s/CRI data needed to populate that map.Events Up: The System Monitor constantly reads raw event data from the kernel (via BPF ring buffer). It performs the initial lookup using the Namespace IDs and passes the enriched events (likely via Go channels, as hinted in Chapter 4 code) back to the Daemon or a component managed by the Daemon (like the logging pipeline within the Feeder).
Logs Out: The Daemon (or its logging pipeline) takes these enriched events and passes them to the Log Feeder component. The Log Feeder is then responsible for sending these logs/alerts to the configured output destinations.
The Daemon acts as the central switchboard, ensuring that policies are delivered to the enforcement layer, that kernel events are enriched with workload context, and that meaningful security logs and alerts are generated and sent out.
Daemon Responsibilities Summary
Component Management
Starts, stops, and manages the lifecycle of Monitor, Enforcer, Logger.
System Monitor, Runtime Enforcer, Log Feeder
External Comm.
Watches K8s API for policies & workload info; interacts with CRI.
Kubernetes API Server, Container Runtimes (Docker, containerd, CRI-O)
Identity Building
Gathers data (Labels, Namespaces, Container IDs, PIDs, NS IDs) to map low-level events to workloads.
Kubernetes API Server, Container Runtimes, OS Kernel (/proc
)
Policy Processing
Fetches policies, identifies targeted workloads on its node.
Kubernetes API Server, Internal state (Identity)
Enforcement Orchest.
Tells the Runtime Enforcer which policies to load for which workload.
Runtime Enforcer, Internal state (Identity, Policies)
Event Reception
Receives raw or partially processed events from the Monitor.
System Monitor (via channels/buffers)
Event Enrichment
Adds full workload identity and policy context to incoming events.
System Monitor, Internal state (Identity, Policies)
Logging/Alerting
Formats events into structured logs/alerts and passes them to the Log Feeder.
Log Feeder, Internal state (Enriched Events)
Configuration/Signal
Reads configuration, handles graceful shutdown requests.
Configuration files/API, OS Signals
This table reinforces that the Daemon is the crucial integration layer on each node.
Conclusion
In this chapter, you learned that the KubeArmor Daemon is the core process running on each node, serving as the central orchestrator for all other KubeArmor components. It's responsible for initializing, managing, and coordinating the System Monitor (eyes/ears), Runtime Enforcer (security guard), and Log Feeder (reporter). You saw how it interacts with Kubernetes and container runtimes to understand Container/Node Identity and fetch Security Policies, bringing all the pieces together to enforce your security posture and report violations.
Understanding the Daemon's central role is key to seeing how KubeArmor operates as a cohesive system on each node. In the final chapter, we'll focus on where all the security events observed by the Daemon and its components end up
Last updated
Was this helpful?