first commit
This commit is contained in:
287
internal/containerruntime/amicontained.go
Normal file
287
internal/containerruntime/amicontained.go
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Jessica Frazelle
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The code for amicontained.go is forked from
|
||||
https://github.com/genuinetools/bpfd/blob/434b609b3d4a5aeb461109b1167b68e000b72f69/proc/proc.go
|
||||
|
||||
The code was forked when the latest details are as "Latest commit 871fc34 on Sep 18, 2018"
|
||||
|
||||
*/
|
||||
|
||||
// Adding IBM Copyright since the forked code had to be modified to remove deprecated ioutil package
|
||||
/*
|
||||
© Copyright IBM Corporation 2023
|
||||
*/
|
||||
|
||||
package containerruntime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// ContainerRuntime is the type for the various container runtime strings.
|
||||
type ContainerRuntime string
|
||||
|
||||
// SeccompMode is the type for the various seccomp mode strings.
|
||||
type SeccompMode 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"
|
||||
// RuntimeNotFound is the string for when no container runtime is found.
|
||||
RuntimeNotFound ContainerRuntime = "not-found"
|
||||
|
||||
// SeccompModeDisabled is equivalent to "0" in the /proc/{pid}/status file.
|
||||
SeccompModeDisabled SeccompMode = "disabled"
|
||||
// SeccompModeStrict is equivalent to "1" in the /proc/{pid}/status file.
|
||||
SeccompModeStrict SeccompMode = "strict"
|
||||
// SeccompModeFiltering is equivalent to "2" in the /proc/{pid}/status file.
|
||||
SeccompModeFiltering SeccompMode = "filtering"
|
||||
|
||||
apparmorUnconfined = "unconfined"
|
||||
|
||||
uint32Max = 4294967295
|
||||
|
||||
statusFileValue = ":(.*)"
|
||||
)
|
||||
|
||||
var (
|
||||
// ContainerRuntimes contains all the container runtimes.
|
||||
ContainerRuntimes = []ContainerRuntime{
|
||||
RuntimeDocker,
|
||||
RuntimeRkt,
|
||||
RuntimeNspawn,
|
||||
RuntimeLXC,
|
||||
RuntimeLXCLibvirt,
|
||||
RuntimeOpenVZ,
|
||||
RuntimeKubernetes,
|
||||
RuntimeGarden,
|
||||
RuntimePodman,
|
||||
}
|
||||
|
||||
seccompModes = map[string]SeccompMode{
|
||||
"0": SeccompModeDisabled,
|
||||
"1": SeccompModeStrict,
|
||||
"2": SeccompModeFiltering,
|
||||
}
|
||||
|
||||
statusFileValueRegex = regexp.MustCompile(statusFileValue)
|
||||
)
|
||||
|
||||
// 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 fileExists("/proc/vz") && !fileExists("/proc/bc") {
|
||||
return RuntimeOpenVZ
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 fileExists(file string) bool {
|
||||
if _, err := os.Stat(file); !os.IsNotExist(err) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func readFile(file string) []byte {
|
||||
if !fileExists(file) {
|
||||
return nil
|
||||
}
|
||||
// filepath.clean was added to resolve the gosec build failure
|
||||
// with error "Potential file inclusion via variable"
|
||||
// IBM Modified the below line to remove the deprecated ioutil dependency
|
||||
b, err := os.ReadFile(filepath.Clean(file))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// GetCapabilities returns the allowed capabilities for the process.
|
||||
// If pid is less than one, it returns the capabilities for "self".
|
||||
func GetCapabilities(pid int) (map[string][]string, error) {
|
||||
allCaps := capability.List()
|
||||
|
||||
caps, err := capability.NewPid(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allowedCaps := map[string][]string{}
|
||||
allowedCaps["EFFECTIVE | PERMITTED | INHERITABLE"] = []string{}
|
||||
allowedCaps["BOUNDING"] = []string{}
|
||||
allowedCaps["AMBIENT"] = []string{}
|
||||
|
||||
for _, cap := range allCaps {
|
||||
if caps.Get(capability.CAPS, cap) {
|
||||
allowedCaps["EFFECTIVE | PERMITTED | INHERITABLE"] = append(allowedCaps["EFFECTIVE | PERMITTED | INHERITABLE"], cap.String())
|
||||
}
|
||||
if caps.Get(capability.BOUNDING, cap) {
|
||||
allowedCaps["BOUNDING"] = append(allowedCaps["BOUNDING"], cap.String())
|
||||
}
|
||||
if caps.Get(capability.AMBIENT, cap) {
|
||||
allowedCaps["AMBIENT"] = append(allowedCaps["AMBIENT"], cap.String())
|
||||
}
|
||||
}
|
||||
|
||||
return allowedCaps, nil
|
||||
}
|
||||
|
||||
// GetSeccompEnforcingMode returns the seccomp enforcing level (disabled, filtering, strict)
|
||||
// for a process.
|
||||
// If pid is less than one, it returns the seccomp enforcing mode for "self".
|
||||
func GetSeccompEnforcingMode(pid int) SeccompMode {
|
||||
file := "/proc/self/status"
|
||||
if pid > 0 {
|
||||
file = fmt.Sprintf("/proc/%d/status", pid)
|
||||
}
|
||||
|
||||
return getSeccompEnforcingMode(readFileString(file))
|
||||
}
|
||||
|
||||
func getSeccompEnforcingMode(input string) SeccompMode {
|
||||
mode := getStatusEntry(input, "Seccomp:")
|
||||
sm, ok := seccompModes[mode]
|
||||
if ok {
|
||||
return sm
|
||||
}
|
||||
|
||||
// Pre linux 3.8, check if Seccomp is supported, via CONFIG_SECCOMP.
|
||||
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
|
||||
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
||||
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
|
||||
return SeccompModeStrict
|
||||
}
|
||||
}
|
||||
|
||||
return SeccompModeDisabled
|
||||
}
|
||||
|
||||
// TODO: make this function more efficient and read the file line by line.
|
||||
func getStatusEntry(input, find string) string {
|
||||
// Split status file string by line
|
||||
statusMappings := strings.Split(input, "\n")
|
||||
statusMappings = deleteEmpty(statusMappings)
|
||||
|
||||
for _, line := range statusMappings {
|
||||
if strings.Contains(line, find) {
|
||||
matches := statusFileValueRegex.FindStringSubmatch(line)
|
||||
if len(matches) > 1 {
|
||||
return strings.TrimSpace(matches[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func deleteEmpty(s []string) []string {
|
||||
var r []string
|
||||
for _, str := range s {
|
||||
if strings.TrimSpace(str) != "" {
|
||||
r = append(r, strings.TrimSpace(str))
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func readFileString(file string) string {
|
||||
b := readFile(file)
|
||||
if b == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(b))
|
||||
}
|
||||
124
internal/containerruntime/runtime.go
Normal file
124
internal/containerruntime/runtime.go
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2019,2023
|
||||
|
||||
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 containerruntime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func DetectContainerRuntime() ContainerRuntime {
|
||||
return GetContainerRuntime(0, 1)
|
||||
}
|
||||
|
||||
func GetBaseImage() (string, error) {
|
||||
buf, err := os.ReadFile("/etc/os-release")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to read /etc/os-release: %v", err)
|
||||
}
|
||||
lines := strings.Split(string(buf), "\n")
|
||||
for _, l := range lines {
|
||||
if strings.HasPrefix(l, "PRETTY_NAME=") {
|
||||
words := strings.Split(l, "\"")
|
||||
if len(words) >= 2 {
|
||||
return words[1], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "unknown", nil
|
||||
}
|
||||
|
||||
// GetCapabilities gets the Linux capabilities (e.g. setuid, setgid). See https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
|
||||
func GetContainerCapabilities() (map[string][]string, error) {
|
||||
//passing the pid as 1 since runmqserver initializes, creates and starts a queue manager, as PID 1 in a container
|
||||
return GetCapabilities(1)
|
||||
}
|
||||
|
||||
// GetSeccomp gets the seccomp enforcing mode, which affects which kernel calls can be made
|
||||
func GetSeccomp() SeccompMode {
|
||||
//passing the pid as 1 since runmqserver initializes, creates and starts a queue manager, as PID 1 in a container
|
||||
return GetSeccompEnforcingMode(1)
|
||||
}
|
||||
|
||||
// GetSecurityAttributes gets the security attributes of the current process.
|
||||
// The security attributes indicate whether AppArmor or SELinux are being used,
|
||||
// and what the level of confinement is.
|
||||
func GetSecurityAttributes() string {
|
||||
a, err := readProc("/proc/self/attr/current")
|
||||
// On some systems, if AppArmor or SELinux are not installed, you get an
|
||||
// error when you try and read `/proc/self/attr/current`, even though the
|
||||
// file exists.
|
||||
if err != nil || a == "" {
|
||||
a = "none"
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func readProc(filename string) (value string, err error) {
|
||||
// #nosec G304
|
||||
buf, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(buf)), nil
|
||||
}
|
||||
|
||||
func GetMounts() (map[string]string, error) {
|
||||
all, err := readProc("/proc/mounts")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't read /proc/mounts")
|
||||
}
|
||||
result := make(map[string]string)
|
||||
lines := strings.Split(all, "\n")
|
||||
for i := range lines {
|
||||
parts := strings.Split(lines[i], " ")
|
||||
//dev := parts[0]
|
||||
mountPoint := parts[1]
|
||||
fsType := parts[2]
|
||||
if strings.Contains(mountPoint, "/mnt/mqm") {
|
||||
result[mountPoint] = fsType
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetKernelVersion() (string, error) {
|
||||
return readProc("/proc/sys/kernel/osrelease")
|
||||
}
|
||||
|
||||
func GetMaxFileHandles() (string, error) {
|
||||
return readProc("/proc/sys/fs/file-max")
|
||||
}
|
||||
|
||||
// SupportedFilesystem returns true if the supplied filesystem type is supported for MQ data
|
||||
func SupportedFilesystem(fsType string) bool {
|
||||
switch fsType {
|
||||
case "aufs", "overlayfs", "tmpfs":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ValidMultiInstanceFilesystem returns true if the supplied filesystem type is valid for a multi-instance queue manager
|
||||
func ValidMultiInstanceFilesystem(fsType string) bool {
|
||||
if !SupportedFilesystem(fsType) {
|
||||
return false
|
||||
}
|
||||
// TODO : check for non-shared filesystems & shared filesystems which are known not to work
|
||||
return true
|
||||
}
|
||||
115
internal/containerruntime/runtime_linux.go
Normal file
115
internal/containerruntime/runtime_linux.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
|
||||
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 containerruntime
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// fsTypes contains file system identifier codes.
|
||||
// This code will not compile on some operating systems - Linux only.
|
||||
var fsTypes = map[int64]string{
|
||||
0x61756673: "aufs",
|
||||
0xef53: "ext",
|
||||
0x6969: "nfs",
|
||||
0x65735546: "fuse",
|
||||
0x9123683e: "btrfs",
|
||||
0x01021994: "tmpfs",
|
||||
0x794c7630: "overlayfs",
|
||||
0x58465342: "xfs",
|
||||
// less popular codes
|
||||
0xadf5: "adfs",
|
||||
0xadff: "affs",
|
||||
0x5346414F: "afs",
|
||||
0x0187: "autofs",
|
||||
0x73757245: "coda",
|
||||
0x28cd3d45: "cramfs",
|
||||
0x453dcd28: "cramfs",
|
||||
0x64626720: "debugfs",
|
||||
0x73636673: "securityfs",
|
||||
0xf97cff8c: "selinux",
|
||||
0x43415d53: "smack",
|
||||
0x858458f6: "ramfs",
|
||||
0x958458f6: "hugetlbfs",
|
||||
0x73717368: "squashfs",
|
||||
0xf15f: "ecryptfs",
|
||||
0x414A53: "efs",
|
||||
0xabba1974: "xenfs",
|
||||
0x3434: "nilfs",
|
||||
0xF2F52010: "f2fs",
|
||||
0xf995e849: "hpfs",
|
||||
0x9660: "isofs",
|
||||
0x72b6: "jffs2",
|
||||
0x6165676C: "pstorefs",
|
||||
0xde5e81e4: "efivarfs",
|
||||
0x00c0ffee: "hostfs",
|
||||
0x137F: "minix_14", // minix v1 fs, 14 char names
|
||||
0x138F: "minix_30", // minix v1 fs, 30 char names
|
||||
0x2468: "minix2_14", // minix v2 fs, 14 char names
|
||||
0x2478: "minix2_30", // minix v2 fs, 30 char names
|
||||
0x4d5a: "minix3_60", // minix v3 fs, 60 char names
|
||||
0x4d44: "msdos",
|
||||
0x564c: "ncp",
|
||||
0x7461636f: "ocfs2",
|
||||
0x9fa1: "openprom",
|
||||
0x002f: "qnx4",
|
||||
0x68191122: "qnx6",
|
||||
0x6B414653: "afs_fs",
|
||||
0x52654973: "reiserfs",
|
||||
0x517B: "smb",
|
||||
0x27e0eb: "cgroup",
|
||||
0x63677270: "cgroup2",
|
||||
0x7655821: "rdtgroup",
|
||||
0x57AC6E9D: "stack_end",
|
||||
0x74726163: "tracefs",
|
||||
0x01021997: "v9fs",
|
||||
0x62646576: "bdevfs",
|
||||
0x64646178: "daxfs",
|
||||
0x42494e4d: "binfmtfs",
|
||||
0x1cd1: "devpts",
|
||||
0xBAD1DEA: "futexfs",
|
||||
0x50495045: "pipefs",
|
||||
0x9fa0: "proc",
|
||||
0x534F434B: "sockfs",
|
||||
0x62656572: "sysfs",
|
||||
0x9fa2: "usbdevice",
|
||||
0x11307854: "mtd_inode",
|
||||
0x09041934: "anon_inode",
|
||||
0x73727279: "btrfs",
|
||||
0x6e736673: "nsfs",
|
||||
0xcafe4a11: "bpf",
|
||||
0x5a3c69f0: "aafs",
|
||||
0x15013346: "udf",
|
||||
0x13661366: "balloon_kvm",
|
||||
0x58295829: "zsmalloc",
|
||||
}
|
||||
|
||||
// GetFilesystem returns the filesystem type for the specified path
|
||||
func GetFilesystem(path string) (string, error) {
|
||||
statfs := &unix.Statfs_t{}
|
||||
err := unix.Statfs(path, statfs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Use a type conversion to make type an int64. On s390x it's a uint32.
|
||||
t, ok := fsTypes[int64(statfs.Type)]
|
||||
if !ok {
|
||||
return "unknown", nil
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
24
internal/containerruntime/runtime_other.go
Normal file
24
internal/containerruntime/runtime_other.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
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 containerruntime
|
||||
|
||||
// Dummy version of this function, only for non-Linux systems.
|
||||
// Having this allows unit tests to be run on other platforms (e.g. macOS)
|
||||
func checkFS(path string) error {
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user