@@ -64,66 +64,84 @@ func (cd *ContainerDetector) IsProcessInContainer(pid int32) (*ContainerInfo, er
6464// checkCgroup examines the process cgroup to detect container runtimes
6565func (cd * ContainerDetector ) checkCgroup (pid int32 ) (* ContainerInfo , error ) {
6666 cgroupPath := fmt .Sprintf ("/proc/%d/cgroup" , pid )
67- file , err := os .Open (cgroupPath )
67+ data , err := os .ReadFile (cgroupPath )
6868 if err != nil {
69- return nil , err
69+ return & ContainerInfo { IsContainer : false } , err
7070 }
71- defer file .Close ()
7271
73- scanner := bufio .NewScanner (file )
74- for scanner .Scan () {
75- line := scanner .Text ()
72+ content := string (data )
73+ if content == "" {
74+ return & ContainerInfo {IsContainer : false }, nil
75+ }
7676
77- // Docker patterns
78- if strings .Contains (line , "/docker/" ) {
79- containerID := cd .extractDockerIDFromCgroup (line )
80- return & ContainerInfo {
81- IsContainer : true ,
82- ContainerID : containerID ,
83- Runtime : "docker" ,
84- }, nil
85- }
77+ // 1. DOCKER & CONTAINERD
78+ // v1: /docker/<ID> or /containerd/<ID>
79+ // v2: /system.slice/docker-<ID>.scope or /system.slice/containerd-<ID>.scope
80+ dockerRegex := regexp .MustCompile (`(?:docker-|containerd-|/docker/|/containerd/)([a-fA-F0-9]{64})` )
81+ if matches := dockerRegex .FindStringSubmatch (content ); len (matches ) > 1 {
82+ return & ContainerInfo {
83+ IsContainer : true ,
84+ ContainerID : matches [1 ],
85+ Runtime : "docker/containerd" ,
86+ }, nil
87+ }
8688
87- // Podman patterns
88- if strings .Contains (line , "/libpod-" ) || strings .Contains (line , "machine.slice/libpod-" ) {
89- containerID := cd .extractPodmanIDFromCgroup (line )
90- return & ContainerInfo {
91- IsContainer : true ,
92- ContainerID : containerID ,
93- Runtime : "podman" ,
94- }, nil
95- }
89+ // 2. KUBERNETES (CRI-O / Containerd)
90+ // Matches: cri-containerd-<ID>.scope, crio-<ID>.scope, or kubepods paths
91+ k8sRegex := regexp .MustCompile (`(?:cri-containerd-|crio-|/kubepods.*/pod.*/)([a-fA-F0-9]{64})` )
92+ if matches := k8sRegex .FindStringSubmatch (content ); len (matches ) > 1 {
93+ return & ContainerInfo {
94+ IsContainer : true ,
95+ ContainerID : matches [1 ],
96+ Runtime : "kubernetes" ,
97+ }, nil
98+ }
9699
97- // Containerd patterns
98- if strings .Contains (line , "/containerd/" ) {
99- containerID := cd .extractContainerdIDFromCgroup (line )
100- return & ContainerInfo {
101- IsContainer : true ,
102- ContainerID : containerID ,
103- Runtime : "containerd" ,
104- }, nil
105- }
100+ // 3. PODMAN
101+ // Matches: libpod-<ID>.scope or /libpod-<ID>
102+ podmanRegex := regexp .MustCompile (`(?:libpod-|/libpod-)([a-fA-F0-9]{64})` )
103+ if matches := podmanRegex .FindStringSubmatch (content ); len (matches ) > 1 {
104+ return & ContainerInfo {
105+ IsContainer : true ,
106+ ContainerID : matches [1 ],
107+ Runtime : "podman" ,
108+ }, nil
109+ }
106110
107- // systemd-nspawn patterns
108- if strings .Contains (line , "machine-" ) && strings .Contains (line , ".scope" ) {
109- return & ContainerInfo {
110- IsContainer : true ,
111- Runtime : "systemd-nspawn" ,
112- }, nil
113- }
111+ // 4. LXC
112+ lxcRegex := regexp .MustCompile (`/lxc/([^/\n]+)` )
113+ if matches := lxcRegex .FindStringSubmatch (content ); len (matches ) > 1 {
114+ return & ContainerInfo {
115+ IsContainer : true ,
116+ ContainerID : matches [1 ],
117+ Runtime : "lxc" ,
118+ }, nil
119+ }
114120
115- // LXC patterns
116- if strings .Contains (line , "/lxc/" ) {
117- containerID := cd .extractLXCIDFromCgroup (line )
118- return & ContainerInfo {
119- IsContainer : true ,
120- ContainerID : containerID ,
121- Runtime : "lxc" ,
122- }, nil
123- }
121+ // 5. GENERIC FALLBACK (Systemd Scopes)
122+ // If it's in a .scope but not the init.scope or user.slice, it's likely a container
123+ if strings .Contains (content , ".scope" ) && ! strings .Contains (content , "init.scope" ) {
124+ return & ContainerInfo {
125+ IsContainer : true ,
126+ Runtime : "generic-container" ,
127+ }, nil
124128 }
125129
126- return & ContainerInfo {IsContainer : false }, scanner .Err ()
130+ return & ContainerInfo {IsContainer : false }, nil
131+ }
132+
133+ func (cd * ContainerDetector ) checkNamespaceFallback (pid int32 , info * ContainerInfo ) {
134+ if info .IsContainer {
135+ return // Already detected
136+ }
137+
138+ hostNs , _ := os .Readlink ("/proc/1/ns/mnt" )
139+ procNs , _ := os .Readlink (fmt .Sprintf ("/proc/%d/ns/mnt" , pid ))
140+
141+ if hostNs != "" && procNs != "" && hostNs != procNs {
142+ info .IsContainer = true
143+ info .Runtime = "namespace-isolated" // Container detected via isolation
144+ }
127145}
128146
129147// checkMountNamespace checks if the process is in a different mount namespace
@@ -351,6 +369,8 @@ func (cd *ContainerDetector) GetContainerNameByID(containerID, runtime string) s
351369 switch runtime {
352370 case "docker" :
353371 return cd .getDockerContainerName (containerID )
372+ case "docker/containerd" :
373+ return cd .getDockerContainerName (containerID )
354374 case "podman" :
355375 return cd .getPodmanContainerName (containerID )
356376 default :
0 commit comments