From 4b5b788918b1d10ec644cbb23ec57de2f4e3f3d1 Mon Sep 17 00:00:00 2001
From: Antoine Gaillard <antoine.gaillard@datadoghq.com>
Date: Wed, 5 Feb 2025 16:59:45 +0100
Subject: [PATCH] agent: Use init subcgroup for process attachment in DinD

cgroups v2 enforces stricter delegation rules, preventing operations on
cgroups outside our ownership boundary. When running Docker-in-Docker (DinD),
processes must be attached to an "init" subcgroup within the systemd unit.
This fix detects and uses the init subcgroup when proxying process attachment.

Fixes #10733

Signed-off-by: Antoine Gaillard <antoine.gaillard@datadoghq.com>
---
 src/agent/rustjail/src/cgroups/fs/mod.rs        | 17 +++++++++++++++++
 .../rustjail/src/cgroups/systemd/dbus_client.rs |  7 +++----
 .../rustjail/src/cgroups/systemd/manager.rs     |  3 ++-
 3 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/src/agent/rustjail/src/cgroups/fs/mod.rs b/src/agent/rustjail/src/cgroups/fs/mod.rs
index 741c9ce2f6..bc9b2b090b 100644
--- a/src/agent/rustjail/src/cgroups/fs/mod.rs
+++ b/src/agent/rustjail/src/cgroups/fs/mod.rs
@@ -1170,6 +1170,23 @@ impl Manager {
         })
     }
 
+    pub fn subcgroup(&self) -> &str {
+        // Check if we're in a Docker-in-Docker setup by verifying:
+        // 1. We're using cgroups v2 (which restricts direct process control)
+        // 2. An "init" subdirectory exists (used by DinD for process delegation)
+        let is_dind = cgroups::hierarchies::is_cgroup2_unified_mode()
+            && cgroups::hierarchies::auto()
+                .root()
+                .join(&self.cpath)
+                .join("init")
+                .exists();
+        if is_dind {
+            "/init/"
+        } else {
+            "/"
+        }
+    }
+
     fn get_paths_and_mounts(
         cpath: &str,
     ) -> Result<(HashMap<String, String>, HashMap<String, String>)> {
diff --git a/src/agent/rustjail/src/cgroups/systemd/dbus_client.rs b/src/agent/rustjail/src/cgroups/systemd/dbus_client.rs
index 3e1e3275c7..0c3294b444 100644
--- a/src/agent/rustjail/src/cgroups/systemd/dbus_client.rs
+++ b/src/agent/rustjail/src/cgroups/systemd/dbus_client.rs
@@ -19,7 +19,7 @@ pub trait SystemdInterface {
     fn kill_unit(&self) -> Result<()>;
     fn freeze_unit(&self) -> Result<()>;
     fn thaw_unit(&self) -> Result<()>;
-    fn add_process(&self, pid: i32) -> Result<()>;
+    fn add_process(&self, pid: i32, subcgroup: &str) -> Result<()>;
     fn get_version(&self) -> Result<String>;
     fn unit_exists(&self) -> Result<bool>;
 }
@@ -151,11 +151,10 @@ impl SystemdInterface for DBusClient {
         }
     }
 
-    fn add_process(&self, pid: i32) -> Result<()> {
+    fn add_process(&self, pid: i32, subcgroup: &str) -> Result<()> {
         let proxy = self.build_proxy()?;
-
         proxy
-            .attach_processes_to_unit(&self.unit_name, "/", &[pid as u32])
+            .attach_processes_to_unit(&self.unit_name, subcgroup, &[pid as u32])
             .context(format!(
                 "failed to add process into unit {}",
                 self.unit_name
diff --git a/src/agent/rustjail/src/cgroups/systemd/manager.rs b/src/agent/rustjail/src/cgroups/systemd/manager.rs
index c3158bdf61..6d0408c2ce 100644
--- a/src/agent/rustjail/src/cgroups/systemd/manager.rs
+++ b/src/agent/rustjail/src/cgroups/systemd/manager.rs
@@ -41,7 +41,8 @@ pub struct Manager {
 impl CgroupManager for Manager {
     fn apply(&self, pid: pid_t) -> Result<()> {
         if self.dbus_client.unit_exists()? {
-            self.dbus_client.add_process(pid)?;
+            let subcgroup = self.fs_manager.subcgroup();
+            self.dbus_client.add_process(pid, subcgroup)?;
         } else {
             self.dbus_client.start_unit(
                 (pid as u32).try_into().unwrap(),