Move and fix GetContainerRuntime check from bpfd proc (#1996)

* Moved genuinetools/bpfd proc

* Removed unused code from util/proc, added genuinetools/bpfd proc test, fixed naming

* refactor: adjust bpfd container runtime detection

* Moved util/proc.go to util/proc/proc.go, added boilerplate

Co-authored-by: ejose19 <8742215+ejose19@users.noreply.github.com>
This commit is contained in:
Igor Scheller
2022-03-23 11:09:01 -04:00
committed by GitHub
co-authored by ejose19
parent cf5ca26aa4
commit 7b161105a5
8 changed files with 341 additions and 586 deletions
+204
View File
@@ -0,0 +1,204 @@
/*
Copyright 2022 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Ported from https://github.com/genuinetools/bpfd/blob/a4bfa5e3e9d1bfdbc56268a36a0714911ae9b6bf/proc/proc.go
package proc
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
// ContainerRuntime is the type for the various container runtime strings.
type ContainerRuntime string
const (
// RuntimeDocker is the string for the docker runtime.
RuntimeDocker ContainerRuntime = "docker"
// RuntimeRkt is the string for the rkt runtime.
RuntimeRkt ContainerRuntime = "rkt"
// RuntimeNspawn is the string for the systemd-nspawn runtime.
RuntimeNspawn ContainerRuntime = "systemd-nspawn"
// RuntimeLXC is the string for the lxc runtime.
RuntimeLXC ContainerRuntime = "lxc"
// RuntimeLXCLibvirt is the string for the lxc-libvirt runtime.
RuntimeLXCLibvirt ContainerRuntime = "lxc-libvirt"
// RuntimeOpenVZ is the string for the openvz runtime.
RuntimeOpenVZ ContainerRuntime = "openvz"
// RuntimeKubernetes is the string for the kubernetes runtime.
RuntimeKubernetes ContainerRuntime = "kube"
// RuntimeGarden is the string for the garden runtime.
RuntimeGarden ContainerRuntime = "garden"
// RuntimePodman is the string for the podman runtime.
RuntimePodman ContainerRuntime = "podman"
// RuntimeGVisor is the string for the gVisor (runsc) runtime.
RuntimeGVisor ContainerRuntime = "gvisor"
// RuntimeFirejail is the string for the firejail runtime.
RuntimeFirejail ContainerRuntime = "firejail"
// RuntimeWSL is the string for the Windows Subsystem for Linux runtime.
RuntimeWSL ContainerRuntime = "wsl"
// RuntimeNotFound is the string for when no container runtime is found.
RuntimeNotFound ContainerRuntime = "not-found"
)
var (
// ContainerRuntimes contains all the container runtimes.
ContainerRuntimes = []ContainerRuntime{
RuntimeDocker,
RuntimeRkt,
RuntimeNspawn,
RuntimeLXC,
RuntimeLXCLibvirt,
RuntimeOpenVZ,
RuntimeKubernetes,
RuntimeGarden,
RuntimePodman,
RuntimeGVisor,
RuntimeFirejail,
RuntimeWSL,
}
)
// GetContainerRuntime returns the container runtime the process is running in.
// If pid is less than one, it returns the runtime for "self".
func GetContainerRuntime(tgid, pid int) ContainerRuntime {
file := "/proc/self/cgroup"
if pid > 0 {
if tgid > 0 {
file = fmt.Sprintf("/proc/%d/task/%d/cgroup", tgid, pid)
} else {
file = fmt.Sprintf("/proc/%d/cgroup", pid)
}
}
// read the cgroups file
a := readFileString(file)
runtime := getContainerRuntime(a)
if runtime != RuntimeNotFound {
return runtime
}
// /proc/vz exists in container and outside of the container, /proc/bc only outside of the container.
if osFileExists("/proc/vz") && !osFileExists("/proc/bc") {
return RuntimeOpenVZ
}
// /__runsc_containers__ directory is present in gVisor containers.
if osFileExists("/__runsc_containers__") {
return RuntimeGVisor
}
// firejail runs with `firejail` as pid 1.
// As firejail binary cannot be run with argv[0] != "firejail"
// it's okay to rely on cmdline.
a = readFileString("/proc/1/cmdline")
runtime = getContainerRuntime(a)
if runtime != RuntimeNotFound {
return runtime
}
// WSL has /proc/version_signature starting with "Microsoft".
a = readFileString("/proc/version_signature")
if strings.HasPrefix(a, "Microsoft") {
return RuntimeWSL
}
a = os.Getenv("container")
runtime = getContainerRuntime(a)
if runtime != RuntimeNotFound {
return runtime
}
// PID 1 might have dropped this information into a file in /run.
// Read from /run/systemd/container since it is better than accessing /proc/1/environ,
// which needs CAP_SYS_PTRACE
a = readFileString("/run/systemd/container")
runtime = getContainerRuntime(a)
if runtime != RuntimeNotFound {
return runtime
}
// Check for container specific files
runtime = detectContainerFiles()
if runtime != RuntimeNotFound {
return runtime
}
return RuntimeNotFound
}
// Related implementation: https://github.com/systemd/systemd/blob/6604fb0207ee10e8dc05d67f6fe45de0b193b5c4/src/basic/virt.c#L523-L549
func detectContainerFiles() ContainerRuntime {
files := []struct {
runtime ContainerRuntime
location string
}{
// https://github.com/containers/podman/issues/6192
// https://github.com/containers/podman/issues/3586#issuecomment-661918679
{RuntimePodman, "/run/.containerenv"},
// https://github.com/moby/moby/issues/18355
{RuntimeDocker, "/.dockerenv"},
}
for i := range files {
if osFileExists(files[i].location) {
return files[i].runtime
}
}
return RuntimeNotFound
}
func getContainerRuntime(input string) ContainerRuntime {
if len(strings.TrimSpace(input)) < 1 {
return RuntimeNotFound
}
for _, runtime := range ContainerRuntimes {
if strings.Contains(input, string(runtime)) {
return runtime
}
}
return RuntimeNotFound
}
func osFileExists(file string) bool {
if _, err := os.Stat(file); !os.IsNotExist(err) {
return true
}
return false
}
func readFile(file string) []byte {
if !osFileExists(file) {
return nil
}
b, _ := ioutil.ReadFile(file)
return b
}
func readFileString(file string) string {
b := readFile(file)
if b == nil {
return ""
}
return strings.TrimSpace(string(b))
}
+136
View File
@@ -0,0 +1,136 @@
/*
Copyright 2022 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package proc
import (
"testing"
)
func TestGetContainerRuntime(t *testing.T) {
testcases := map[string]struct {
expectedRuntime ContainerRuntime
input string
}{
"empty": {
expectedRuntime: RuntimeNotFound,
},
"typical docker": {
expectedRuntime: RuntimeDocker,
input: `11:pids:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
10:devices:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
9:freezer:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
8:net_cls,net_prio:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
7:perf_event:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
6:cpuset:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
5:memory:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
4:blkio:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
3:cpu,cpuacct:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
2:hugetlb:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
1:name=systemd:/docker/68fad1f9e0985989408aff30e7b83e7dada1d235ff46a22c5465ca193ddf0fac
0::/system.slice/containerd.service`,
},
"uncontainerized process": {
expectedRuntime: RuntimeNotFound,
input: `11:pids:/system.slice/ssh.service
10:devices:/system.slice/ssh.service
9:freezer:/
8:net_cls,net_prio:/
7:perf_event:/
6:cpuset:/
5:memory:/system.slice/ssh.service
4:blkio:/system.slice/ssh.service
3:cpu,cpuacct:/system.slice/ssh.service
2:hugetlb:/
1:name=systemd:/system.slice/ssh.service
0::/system.slice/ssh.service`,
},
"kubernetes": {
expectedRuntime: RuntimeKubernetes,
input: `12:perf_event:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
11:freezer:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
10:pids:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
9:net_cls,net_prio:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
8:memory:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
7:cpuset:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
6:devices:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
5:blkio:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
4:rdma:/
3:hugetlb:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
2:cpu,cpuacct:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47
1:name=systemd:/kubepods/burstable/pod98051bd2-a5fa-11e8-9bb9-0a58ac1f31f2/74998d19bd3c7423744214c344c6e814d19b7908a92f165f9d58243073a27a47`,
},
"lxc": {
expectedRuntime: RuntimeLXC, // this is usually in $container for lxc
input: `10:cpuset:/lxc/debian2
9:pids:/lxc/debian2
8:devices:/lxc/debian2
7:net_cls,net_prio:/lxc/debian2
6:freezer:/lxc/debian2
5:blkio:/lxc/debian2
4:memory:/lxc/debian2
3:cpu,cpuacct:/lxc/debian2
2:perf_event:/lxc/debian2
1:name=systemd:/lxc/debian2`,
},
"nspawn": {
expectedRuntime: RuntimeNotFound, // since this variable is in $container
input: `10:cpuset:/
9:pids:/machine.slice/machine-nspawntest.scope
8:devices:/machine.slice/machine-nspawntest.scope
7:net_cls,net_prio:/
6:freezer:/user/root/0
5:blkio:/machine.slice/machine-nspawntest.scope
4:memory:/machine.slice/machine-nspawntest.scope
3:cpu,cpuacct:/machine.slice/machine-nspawntest.scope
2:perf_event:/
1:name=systemd:/machine.slice/machine-nspawntest.scope`,
},
"rkt": {
expectedRuntime: RuntimeRkt,
input: `10:cpuset:/
9:pids:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice/debian.service
8:devices:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice/debian.service
7:net_cls,net_prio:/
6:freezer:/user/root/0
5:blkio:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice
4:memory:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice
3:cpu,cpuacct:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice
2:perf_event:/
1:name=systemd:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice/debian.service`,
},
"rkt host": {
expectedRuntime: RuntimeRkt,
input: `10:cpuset:/
9:pids:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice/debian.service
8:devices:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice/debian.service
7:net_cls,net_prio:/
6:freezer:/user/root/0
5:blkio:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice
4:memory:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice
3:cpu,cpuacct:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice
2:perf_event:/
1:name=systemd:/machine.slice/machine-rkt\x2dbfb7d57e\x2d80ff\x2d4ef8\x2db602\x2d9b907b3f3a38.scope/system.slice/debian.service`,
},
}
for key, tc := range testcases {
runtime := getContainerRuntime(tc.input)
if runtime != tc.expectedRuntime {
t.Errorf("[%s]: expected runtime %q, got %q", key, tc.expectedRuntime, runtime)
}
}
}