Ruben Groenewoud

Linux & Cloud Detection Engineering - Getting Started with Defend for Containers (D4C)

This technical resource provides a comprehensive walkthrough of Elastic’s Defend for Containers (D4C) integration, covering Kubernetes-based deployment, the analysis of BPF-enriched runtime telemetry, and the practical application of policy-driven security controls to monitor and alert on activities within containerized Linux environments.

Linux & Cloud Detection Engineering - Getting Started with Defend for Containers (D4C)

Introduction

Linux systems remain a critical foundation for modern infrastructure, particularly in cloud-native environments where containers and orchestration platforms are the norm. As workloads move from long-lived hosts to ephemeral containers, attacker tradecraft shifts as well. Activity that once left persistent artifacts on disk is increasingly confined to short-lived, runtime behavior that can be difficult to capture using traditional log sources.

Detection engineering in these environments, therefore, depends heavily on runtime visibility. Understanding how processes execute inside containers, how files are accessed, and how workloads interact with the host becomes more important than relying on static indicators or post-incident artifacts.

Elastic provides several Linux-focused telemetry sources to support this type of detection work. In earlier posts in this series, we focused on host-level visibility using Auditd and Auditd Manager, showing how low-level system events can be translated into high-fidelity detections. In this post, the focus shifts to Elastic’s Defend for Containers: a runtime security integration built specifically for containerized Linux workloads.

The goal of this article is not to document every Defend for Containers feature, but to provide a practical starting point for detection engineers: what data the integration produces and how to reason about that data. In the next part, we will look into how it can be applied to realistic container attack scenarios.

Streamlined visibility with Defend for Containers

We are excited to announce the arrival of Defend for Containers in the 9.3.0 release. This integration brings a streamlined approach to container security, offering a strong foundation for visibility in cloud-native infrastructures. Users can leverage a suite of detection rules tailored to defend against modern Kubernetes threats and container-specific vulnerabilities. The arrival of Defend for Containers is accompanied by a container-specific detection ruleset, designed around realistic container and Kubernetes threat models.

At the time of writing, the Defend for Containers ruleset provides baseline coverage for common container attack techniques, including reconnaissance activity, credential access attempts, kubelet attacks, service account token abuse, interactive process execution, file creation and modification, interpreter abuse, encoded payload execution, tooling installation, tunneling behavior, and multiple privilege escalation vectors. Importantly, all existing container- and Kubernetes-specific detection rules have been made compatible with Defend for Containers, allowing previously host-centric logic to operate directly on container runtime telemetry.

This makes Defend for Containers a practical and immediately usable data source for Linux detection engineers focused on behavior-driven runtime detection. The remainder of this post focuses on how that telemetry looks in practice and how it can be applied to real-world container attack scenarios.

Introduction to Defend for Containers

Defend for Containers is a runtime security integration that provides visibility into Linux containers as they execute. Instead of relying on static image scanning or post-execution logs, it focuses on observing container behavior in real time.

At a high level, Defend for Containers captures security-relevant runtime events from running containers, such as process execution and file access. These events are enriched with container and orchestration context and shipped into Elasticsearch, where they can be analyzed and used as input for detection rules.

From a detection engineering perspective, Defend for Containers sits at the intersection of traditional Linux behavior and the container context. Processes, syscalls, and file activity remain core signals, but they are now scoped to containers, namespaces, and workloads that may only exist briefly.

Defend for Containers is deployed as part of the Elastic Agent and integrates directly with Elastic Security. Once enabled, it provides a dedicated stream of container runtime events that can be queried using KQL or ES|QL, or consumed directly by detection analytics. This allows detection engineers to apply familiar analysis techniques while accounting for the operational realities of cloud-native workloads.

In the sections that follow, we will examine Defend for Containers events in more detail and walk through several container attack scenarios to illustrate how this data can be used in practice.

Defend for Containers setup

Before you can take advantage of Defend for Containers' runtime visibility and analytics, you need to deploy the integration and configure a policy that defines which events to observe and what actions to take when matching activity is encountered. More information about the integration and its setup can be found here. At a high level, this setup consists of:

  1. Deploying the Defend for Containers integration via Elastic Agent in your Kubernetes environment.
  2. Configuring or customizing the Defend for Containers policy, which consists of selectors that define which operations to match and responses that define what actions to take.
  3. Validating and refining the policy based on observed workload behavior.

Deployment methods

Defend for Containers is delivered as an Elastic Agent integration and relies on Elastic Agent to collect and forward container runtime telemetry into your Elastic Stack. For Kubernetes workloads, you install the integration via the Elastic Security UI and then enroll agents on your cluster nodes.

The basic deployment flow is:

In the Elastic Security UI, navigate to Fleet and create a new Agent Policy (or add the integration to an existing one). Once the Agent Policy is created, we can add the “Defend for Containers” integration to the policy.

Give the integration a name and optionally adjust the default selectors and responses (we will look into the available options further down in this publication). Once “Add integration” is selected, a new Agent Policy with the correct integration should be available.

For this demonstration, we will leverage the Kubernetes deployment method. To deploy this policy to a workload, we can navigate to Actions → Add agent → Kubernetes. Here, we see instructions for copying or downloading the Kubernetes manifest.

An important note to be aware of is: “Note that the following manifest contains resource limits that may not be appropriate for a production environment. Review our guide on Scaling Elastic Agent on Kubernetes before deploying this manifest.

You will need to include the following capabilities under securityContext in your Kubernetes YAML for the service to work:

securityContext:
    runAsUser: 0
    capabilities:
      add:
        - BPF ## Enables both BPF & eBPF
        - PERFMON
        - SYS_RESOURCE

After copying or downloading the provided elastic-agent-managed-kubernetes.yml manifest, you can edit the manifest as needed, and apply the manifest with:

kubectl apply -f elastic-agent-managed-kubernetes.yml

As also mentioned in the manifest, review the guide “Run Elastic Agent on Kubernetes managed by Fleet” for more deployment information.

Wait for the Elastic Agent pods to schedule and for data to begin flowing into Elasticsearch.

Once deployed, Elastic Agent will establish a connection to Fleet, enroll under the selected policy, and begin emitting Defend for Containers telemetry that Elastic Security can consume.

In the next section, we will take a look at the integration configuration options and explore which features are available to use.

Defend for Containers policies

At the heart of Defend for Containers' configuration is the policy. Policies determine what activity to observe and how to respond when matching events occur. Policies are composed of two fundamental building blocks:

  • Selectors: define which events are of interest by specifying operations and conditions;
  • Responses: define what actions to take when a selector’s conditions are met.

Defend for Containers policies can be edited before deployment or modified post-deployment via the Elastic Security UI’s policy editor.

Policy structure

Each policy must contain at least one selector and at least one response. A typical selector specifies one or more operations (such as process events or file activities) and uses conditions (like container image name, namespace, or pod label) to narrow the scope. Responses reference selectors and indicate what action to take when events match.

The default Defend for Containers policy includes two selector-response pairs: “Threat Detection” and “Drift Detection & Prevention”.

Threat detection: A selector named allProcesses matches all fork and exec events from containers.

And the associated response has the action set to Log, ensuring that events are ingested and can be analyzed.

Drift detection & prevention: A selector named executableChanges matches createExecutable and modifyExecutable operations.

And the response is configured to create alerts (and can be modified to block those operations).

These can be modified via the UI, but under the hood, these policies are simple YAML configuration files that can be easily modified and used in any CI|CD flows:

process:
  selectors:
    - name: allProcesses
      operation:
        - fork
        - exec
  responses:
    - match:
        - allProcesses
      actions:
        - log
file:
  selectors:
    - name: executableChanges
      operation:
        - createExecutable
        - modifyExecutable
  responses:
    - match:
        - executableChanges
      actions:
        - alert

Next, we will take a look at some example selectors and responses and discuss the options you have for setting up the integration to your liking.

Example selector snippet

Selectors allow fine-grained matching using conditions on fields such as:

  • containerImageFullName: full image names like docker.io/nginx;
  • containerImageName: partial image names;
  • containerImageTag: specific tags like latest;
  • kubernetesClusterId: Kubernetes cluster IDs;
  • kubernetesClusterName: Kubernetes cluster names;
  • kubernetesNamespace: namespaces where the workload runs;
  • kubernetesPodName: pod names, with support for trailing wildcards;
  • kubernetesPodLabel: label key/value pairs, with wildcard support.
selectors:
  - name: nodeExports
    file:
      operations:
        - createExecutable
        - modifyExecutable
      containerImageName:
        - "nginx"
      kubernetesNamespace:
        - "prod-*"

In this example, the selector named nodeExports matches file events that create or modify executables within containers whose image names contain “nginx” and whose Kubernetes namespace begins with “prod-”.

Example response snippet

Responses determine what happens when selector conditions are met. Common actions include:

  • log: send the event as telemetry for analysis;
  • alert: create an alert in Elastic Security;
  • block: prevent the operation (for supported types).
responses:
  - name: alertAndBlockNodeExports
    matchSelectors:
      - nodeExports
    actions:
      - alert
      - block

Here, the response named alertAndBlockNodeExports references the previously defined nodeExports selector and will both generate an alert and block the operation.

Wildcards and matching

Selectors in Defend for Containers support trailing wildcards in string-based conditions (such as pod names or image tags). This allows broad matching without enumerating every possible value. For example, a pod selector of backend-* will match all pods whose names begin with backend-, while a label condition such as role:api* matches label values that start with api.

This wildcarding is essential in dynamic environments where workloads scale and shift rapidly.

In addition to simple string matching, Defend for Containers selectors also support path-based wildcard semantics when matching file paths. Consider the following selector example:

- name:
  targetFilePath:
    - /usr/bin/echo
    - /usr/sbin/*
    - /usr/local/**

In this example:

  • /usr/bin/echo matches only the echo binary at that exact path.
  • /usr/sbin/* matches everything that is a direct child of /usr/sbin.
  • /usr/local/** matches everything recursively under /usr/local, including paths such as /usr/local/bin/something.

These distinctions make it possible to precisely scope file-based selectors, balancing coverage and noise. In practice, they allow detection engineers to target specific binaries, entire directories, or deep directory trees, depending on the use case, without resorting to overly permissive rules.

Tying it all together

Up to this point, we have looked at Defend for Containers selectors, wildcard semantics, event types, and how they surface attacker behavior at runtime. The final step is to understand how these pieces come together within a policy to express real detection logic.

Consider the following policy fragment:

file:
  selectors:
    - name: binDirExeMods
      operation:
        - createExecutable
        - modifyExecutable
      targetFilePath:
        - /usr/bin/**
    - name: etcFileChanges
      operation:
        - createFile
        - modifyFile
        - deleteFile
      targetFilePath:
        - /etc/**
    - name: nginx
      containerImageName:
        - nginx

  responses:
    - match:
        - binDirExeMods
        - etcFileChanges
      exclude:
        - nginx
      actions:
        - alert
        - block

This policy defines three selectors. Two selectors (binDirExeMods and etcFileChanges) describe file system activity of interest, while the third selector (nginx) describes a container context to exclude.

The response section ties these selectors together. The selectors listed under match are logically OR’d, meaning that either condition is sufficient to trigger the response. The selector listed under exclude acts as a logical NOT, removing matching events when the container image is nginx.

Read in plain language, the policy expresses the following logic:

If an executable is created or modified anywhere under /usr/bin, or a file is created, modified, or deleted under /etc, and the activity does not originate from an nginx container, then generate an alert and block the action.

In Boolean form, this can be expressed as:

IF (binDirExeMods OR etcFileChanges) AND NOT nginx
→ alert + block

This is where Defend for Containers policies become powerful. Rather than writing complex detection logic in a query language, selectors let you decompose behavior into small, reusable building blocks and then combine them declaratively. By mixing path-based selectors, operation types, container context, and exclusions, you can express nuanced detection logic that remains readable and maintainable.

In practice, this model allows detection engineers to translate threat hypotheses directly into policy logic: what behavior matters, where it occurs, in which workloads, and what should happen when it does.

Policy validation and refinement

Once a policy is deployed, it is critical to validate it against real workload behavior before enabling aggressive responses such as blocking. Policies that are too restrictive can disrupt normal container operations; policies that are too permissive may let unwanted activity go unnoticed.

A recommended workflow is:

  1. Deploy the default policy in monitoring mode (e.g., with selectors logging events).
  2. Observe the events that appear in Elasticsearch to understand normal workload patterns.
  3. Incrementally tighten selectors and responses, moving from log onlyalertblock, testing at each stage.
  4. Use a staging or test cluster to validate blocking behaviors before applying them in production.

Defend for Containers Beta limitations

As of writing, Defend for Containers is available as a Beta integration, and its current capabilities and platform support reflect that status.

Defend for Containers formally supports Amazon EKS and Google GKE. While the integration can be deployed on Azure AKS, this configuration is not officially supported. In particular, AKS deployments currently lack file event telemetry, which limits detection coverage for file-based attack techniques in those environments.

The current Beta also does not capture network events. As a result, detections related to outbound connections, lateral network movement, or data exfiltration must rely on complementary data sources, such as the Network Packet Capture integration or Packetbeat integrations, rather than on Defend for Containers telemetry alone.

For file activity, Defend for Containers intentionally logs file open events only when opened with write intent. This design choice reduces noise and focuses on behavior that modifies the system state. However, it also means that read-only access to sensitive files, such as secret discovery, configuration scraping, or failed access attempts, is not currently observable.

This limitation impacts detection use cases such as:

  • Searching and reading Kubernetes service account tokens,
  • Scanning for .env files or credential material.

These are areas where future Defend for Containers iterations may provide more granular telemetry to support advanced detection engineering use cases.

Enabling the Defend for Containers pre-built detection rules

Defend for Containers ships with a set of pre-built detection rules that provide baseline coverage for common container attack techniques. Once the integration is enabled, these rules can be activated directly from Elastic Security without additional configuration.

Enabling the pre-built rules is recommended as a starting point, as they are designed to align with Defend for Containers' runtime telemetry and cover execution, file modification, persistence, and post-compromise behavior inside containers. From there, the rules can be extended or refined to match environment-specific workloads and threat models.

By filtering for “Data Source: Elastic Defend for Containers”, you can find all rules associated with this integration.

Note: if you do not see any rules pop up, make sure your stack is running version 9.3.0, as these rules are deployed only on 9.3.0+.

With all important Beta limitations mapped, the integration deployed, the pre-built detection rules installed and enabled, and a working policy in place, the next step is to explore the event semantics Defend for Containers produces, including fields commonly used in detection logic, performance considerations, and how these events differ from Elastic Defend events.

Analyzing Defend for Containers events

Now that Defend for Containers is deployed and policies are in place, the next step is understanding the events it generates. Similar to working with Elastic Defend or Auditd Manager, Defend for Containers telemetry becomes far more valuable once you develop a mental model of how events are structured and which fields are most relevant for detection engineering.

Defend for Containers produces multiple event types, most notably process events and file events, each enriched with container, host, and orchestration context. While the underlying signals remain rooted in Linux behavior, the additional Kubernetes and container metadata enable you to reason about activity in ways not possible with host-only telemetry.

The following sections walk through the most important field groups and event types, using real Defend for Containers events as reference points.

Common fields

Before diving into specific event categories, it is useful to understand the fields that consistently appear across Defend for Containers telemetry. These fields provide the contextual glue that ties individual runtime actions back to policies, selectors, and the underlying execution points inside the kernel.

While process and file events differ in their details, the fields described below are present across Defend for Containers data streams and are often the first place to look when validating detections or troubleshooting policy behavior.

Defend for Containers-specific context

Defend for Containers adds several fields specific to how events are collected and policies are applied.

The cloud_defend.hook_point field indicates where in the kernel the event was captured. In the example shown, values such as tracepoint__sched_process_fork and tracepoint__sched_process_exec reveal that the event was generated from kernel tracepoints associated with process creation and execution.

The cloud_defend.matched_selectors field shows which selectors in the active policy matched the event. In the example, the value allProcesses indicates that this event matched a broad selector that captures all process activity. When tuning policies or investigating alerts, this field is essential for understanding why an event was captured.

The cloud_defend.package_policy_id and cloud_defend.package_policy_revision fields tie the event back to a specific Elastic Agent policy and its revision. This makes it possible to correlate events with configuration changes over time and to verify which version of a policy was active when the event occurred.

Event metadata

Defend for Containers events follow the Elastic Common Schema conventions and include standard event metadata that describes the activity's type and lifecycle.

The event.category field identifies the high-level type of activity, such as process or file, and is typically the first field used when filtering Defend for Containers data. The event.action field describes what occurred, for example, fork or exec for process activity, or open, creation, modification, and deletion for file events.

The event.type field adds lifecycle context, such as start for process execution, and is often used together with event.action to distinguish different phases of activity. The event.dataset field indicates the originating Defend for Containers data stream, such as cloud_defend.process, which is useful when building dataset-scoped queries or detections.

Additional metadata fields like event.id, event.ingested, and event.kind are primarily used for correlation, ordering, and troubleshooting rather than detection logic.

Host information

Defend for Containers events include full host context, similar to Elastic Defend and Auditd Manager. This makes it possible to correlate container runtime activity back to the underlying Kubernetes node.

The host.name field identifies the node on which the container is running, while host.os.* provides operating system details such as distribution and kernel version. The host.architecture field indicates the CPU architecture, which can be relevant when analyzing binary execution or kernel-specific behavior.

One particularly useful field is host.pid_ns_ino, which identifies the PID namespace. This field allows container activity to be correlated with host-level process and kernel telemetry, and is especially valuable when investigating container escape attempts or node-level impact.

This host context is critical when analyzing cloud-native attacks, as multiple containers often share the same host and kernel, and a container's runtime behavior can have implications beyond its boundaries.

Container and orchestrator context

Defend for Containers' primary strength lies in its container awareness. Every runtime event is enriched with container and orchestration metadata, allowing activity to be analyzed in the context of what is running, where it is running, and with which privileges.

At the container level, fields such as container.id and container.name uniquely identify the running container, while container.image.name, container.image.tag, and the image hash provide visibility into the workload’s origin and version. This is especially useful for distinguishing between expected utility images and unexpected or ad hoc workloads.

A key field for risk assessment is container.security_context.privileged. This field explicitly indicates whether a container is running in privileged mode. When privileged execution is combined with other signals such as interactive shells or broad Linux capabilities, the risk profile of any detected activity increases significantly.

Defend for Containers also enriches events with orchestration context. Fields such as orchestrator.cluster.name, orchestrator.namespace, and orchestrator.resource.name (typically the Pod name) tie runtime behavior back to Kubernetes workloads. Labels exposed via orchestrator.resource.label further allow detections to incorporate workload intent and ownership.

For detection engineering, this context enables precise scoping of detections to:

  • specific namespaces (for example, kube-system),
  • privileged or high-risk containers,
  • workloads with sensitive labels,
  • or known utility images such as netshoot, kubectl, or curl.

This layer of enrichment allows container-aware detection logic to be expressed directly, without having to infer intent indirectly from filesystem paths, cgroups, or namespace identifiers.

Process events

Process execution is one of the most important signal types that Defend for Containers provides. Process events capture fork, exec, and end activities within containers and expose detailed lineage information critical to understanding how execution unfolds at runtime.

Several fields are particularly important for detection engineering. The combination of process.name and process.executable identifies what was executed and from where, while process.args provides insight into how it was invoked. Fields such as process.pid, process.start, process.end, and process.exit_code describe the process lifecycle and are useful for timing analysis and execution-flow reconstruction. The process.entity_id provides a stable identifier that allows processes to be tracked across multiple related events.

Defend for Containers also captures rich ancestry information. Fields under process.parent.* describe the immediate parent process, making it possible to detect suspicious parent–child relationships such as shells spawned by unexpected binaries. In addition, process.entry_leader.* and process.session_leader.* provide higher-level anchors within the process tree.

Much like Elastic Defend, Defend for Containers models processes as a graph rather than isolated events. The entry leader is especially useful in container environments, as it often represents the initial process launched by the container runtime (for example, containerd, runc, or a shell specified as the container entrypoint). Anchoring detections to the entry leader allows process trees to be interpreted consistently, even when containers spawn many short-lived child processes.

Session leader fields provide additional context about interactive execution and session boundaries, helping distinguish background services from interactive or attacker-driven activity.

Together, these fields make it possible to express detection logic that goes beyond single executions and instead reasons about execution chains, lineage, and intent, which is essential for detecting real-world container attack techniques.

Capabilities and privilege context

One of the more powerful aspects of the Defend for Containers process events is the inclusion of Linux capability information. For each process, Defend for Containers exposes both the effective and permitted capability sets via:

  • process.thread.capabilities.effective
  • process.thread.capabilities.permitted

These fields describe what a process is actually allowed to do at runtime, independent of its user ID or container boundary.

In privileged containers, processes often expose a broad set of effective capabilities, including highly sensitive ones such as CAP_SYS_ADMIN, CAP_SYS_MODULE, CAP_SYS_PTRACE, CAP_SYS_RAWIO, and CAP_BPF. The presence of these capabilities significantly changes the risk profile of any executed command, as they enable actions that can directly impact the host kernel or other workloads.

From a detection engineering perspective, this context is critical. It allows detections to move beyond simple process-name matching and instead reason about impact. The same binary execution can have vastly different implications depending on whether it runs with a minimal capability set or with near-host-level privileges.

In practice, capability data enables detection engineers to:

  • Identify suspicious tooling executed inside overly permissive containers.
  • Correlate runtime behavior with dangerous capability combinations.
  • Prioritize alerts based on actual exploitation potential rather than surface-level activity.

This becomes especially relevant to container breakout research, where the presence or absence of specific capabilities often determines whether an exploit is viable.

Interactive execution

The process.interactive field indicates whether a process is associated with an interactive session. In container environments, interactive execution is relatively rare for production workloads and often correlates strongly with post-compromise or hands-on-keyboard activity.

Defend for Containers exposes interactivity not only at the process level, but also across related execution contexts, including process.parent.interactive, process.entry_leader.interactive, and process.session_leader.interactive. This makes it possible to determine whether an entire execution chain is interactive, rather than relying on a single process flag in isolation.

Common examples of interactive execution within containers include spawning a bash or sh shell, running interactive utilities such as curl, kubectl, or busybox, or operator-driven reconnaissance within a compromised Pod. While these actions may be legitimate during debugging, they are uncommon in steady-state production workloads.

When combined with container image, namespace, and privilege context, interactive execution becomes a strong anomaly signal. It allows detection logic to distinguish between expected automated container behavior and activity more consistent with manual intervention or attacker-driven exploration.

File events

Defend for Containers file events capture filesystem activity inside containers, and are emitted for a variety of operations. Unlike traditional file integrity monitoring, these events are runtime-aware and scoped to container workloads, providing context about how and why file changes occur.

Defend for Containers can detect file activity such as file opens with write intent, content modifications, file creations, renames, permission changes, and deletions. By focusing on write-oriented operations, Defend for Containers emphasizes behavior that alters system state rather than passive file access.

This allows detection engineers to reason about file usage patterns at runtime, not just the result of a change.

Several fields are particularly important when building file-based detections. The file.path and file.name fields identify the affected file and its location, while file.extension can help distinguish binaries, scripts, and configuration files. The event.action and event.type fields describe what operation occurred and how it should be interpreted in the event lifecycle.

Together, these fields allow Defend for Containers to distinguish benign file access from suspicious modification patterns, such as writing binaries or changing permissions within sensitive directories.

Bringing it together

As with any other data source, Defend for Containers telemetry becomes truly valuable once you understand how to combine fields across the process, file, container, and orchestration domains. Rather than relying on static indicators, Defend for Containers enables detection engineering based on runtime behavior, privilege context, and workload identity.

Conclusion

Defend for Containers in Elastic Stack 9.3.0 includes container runtime detection as a core component of Linux detection engineering. It features a clear scope, a policy-driven configuration model, and runtime telemetry designed specifically for containerized workloads.

In this post, we examined how to deploy Defend for Containers, how its policy model is structured, and how runtime events are generated and enriched with container and orchestration context. We explored the structure of process and file events, capability metadata, interactive execution signals, and container-specific fields that allow detections to be expressed in a workload-aware manner.

The key takeaway is that effective container detection requires reasoning about runtime behavior in context: processes, file modifications, privileges, and workload identity must be evaluated together. Defend for Containers provides the necessary telemetry to make that possible.

In the next article, we will build on this foundation by walking through a realistic container attack scenario and demonstrating how Defend for Containers telemetry surfaces each stage of compromise in practice.

Share this article