From 136d2300cdff9a872d1cdf9b3c216a99d2461d3d Mon Sep 17 00:00:00 2001 From: Yuval Leibovich Date: Sun, 17 Dec 2023 13:24:55 +0200 Subject: [PATCH 1/7] adding dockerfile chnages + host-scanner deployment --- build/Dockerfile | 22 ++++++++++- deployment/host-scanner.yaml | 75 ++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 deployment/host-scanner.yaml diff --git a/build/Dockerfile b/build/Dockerfile index 82795e4c..f1e14b5d 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,20 +1,40 @@ +# Builder stage FROM --platform=$BUILDPLATFORM golang:1.21-bullseye as builder +# Set environment variables ENV GO111MODULE=on CGO_ENABLED=0 WORKDIR /work ARG TARGETOS TARGETARCH +ARG BUILD_VERSION +# Build node-agent +COPY . /work/node-agent RUN --mount=target=. \ --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg \ GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/node-agent . +# Build kube-host-sensor +COPY . /work/kube-host-sensor +RUN --mount=target=. \ + --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg \ + GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/kube-host-sensor --ldflags "-w -s -X main.BuildVersion=$BUILD_VERSION" . + +# Final stage FROM gcr.io/distroless/static-debian11:latest +# Copy built applications COPY --from=builder /out/node-agent /usr/bin/node-agent +COPY --from=builder /out/kube-host-sensor /usr/bin/kube-host-sensor +# Set environment variables ARG image_version ENV RELEASE=$image_version +# Set up working directory WORKDIR /root -ENTRYPOINT ["node-agent"] + +# Define entrypoint +# NOTE: This needs to be adjusted if both applications can't run simultaneously +ENTRYPOINT ["/usr/bin/node-agent"] \ No newline at end of file diff --git a/deployment/host-scanner.yaml b/deployment/host-scanner.yaml new file mode 100644 index 00000000..661d31e9 --- /dev/null +++ b/deployment/host-scanner.yaml @@ -0,0 +1,75 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + app: kubescape-host-scanner + k8s-app: kubescape-host-scanner + kubernetes.io/metadata.name: kubescape-host-scanner + tier: kubescape-host-scanner-control-plane + name: kubescape +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: host-scanner + namespace: kubescape + labels: + app: host-scanner + k8s-app: kubescape-host-scanner + otel: enabled +spec: + selector: + matchLabels: + name: host-scanner + template: + metadata: + labels: + name: host-scanner + spec: + tolerations: + # this toleration is to have the DaemonSet runnable on all nodes (including masters) + # remove it if your masters can't run pods + - operator: Exists + containers: + - name: host-sensor + image: quay.io/kubescape/host-scanner:test + securityContext: + allowPrivilegeEscalation: true + privileged: true + readOnlyRootFilesystem: true + procMount: Unmasked + ports: + - name: scanner # Do not change port name + containerPort: 7888 + protocol: TCP + resources: + limits: + cpu: 0.1m + memory: 200Mi + requests: + cpu: 1m + memory: 200Mi + volumeMounts: + - mountPath: /host_fs + name: host-filesystem + startupProbe: + httpGet: + path: /readyz + port: 7888 + failureThreshold: 30 + periodSeconds: 1 + livenessProbe: + httpGet: + path: /healthz + port: 7888 + periodSeconds: 10 + terminationGracePeriodSeconds: 120 + dnsPolicy: ClusterFirstWithHostNet + automountServiceAccountToken: false + volumes: + - hostPath: + path: / + type: Directory + name: host-filesystem + hostPID: true + hostIPC: true \ No newline at end of file From 354b47db7bb25b8e10b99a3b8fe3b22f32e0690e Mon Sep 17 00:00:00 2001 From: Yuval Leibovich Date: Sun, 17 Dec 2023 13:39:40 +0200 Subject: [PATCH 2/7] adding sensor folder --- go.mod | 3 + go.sum | 6 + sensor/CNI.go | 102 ++++ sensor/CNI_test.go | 30 + sensor/cloudProvider.go | 61 ++ sensor/cloudProvider_test.go | 45 ++ sensor/controlplane.go | 321 ++++++++++ sensor/controlplane_test.go | 86 +++ sensor/datastructures/fileinfo.go | 34 ++ .../linuxsecurityhardeningstatus.go | 6 + sensor/error.go | 34 ++ sensor/globals.go | 5 + sensor/internal/utils/containerruntime.go | 413 +++++++++++++ .../internal/utils/containerruntime_test.go | 533 ++++++++++++++++ sensor/internal/utils/globals.go | 29 + sensor/internal/utils/osuserswrapper.go | 142 +++++ sensor/internal/utils/osuserswrapper_test.go | 181 ++++++ sensor/internal/utils/process.go | 127 ++++ sensor/internal/utils/process_test.go | 76 +++ sensor/internal/utils/service.go | 111 ++++ sensor/internal/utils/testdata/etc/group | 4 + sensor/internal/utils/testdata/etc/passwd | 5 + .../utils/testdata/testCNI/containerd.toml | 71 +++ .../testdata/testCNI/containerd_noparams.toml | 71 +++ .../internal/utils/testdata/testCNI/crio.conf | 570 ++++++++++++++++++ .../testdata/testCNI/crio.d/01_crio.conf | 570 ++++++++++++++++++ .../testdata/testCNI/crio.d/03_crio.conf | 570 ++++++++++++++++++ .../testdata/testCNI/crio.d/05_crio.conf | 570 ++++++++++++++++++ .../testdata/testCNI/crio.d/06_crio.conf | 570 ++++++++++++++++++ .../testCNI/crio.d_noparams/05_crio.conf | 570 ++++++++++++++++++ .../testCNI/crio.d_noparams/06_crio.conf | 570 ++++++++++++++++++ .../utils/testdata/testCNI/crio_noparams.conf | 570 ++++++++++++++++++ sensor/internal/utils/testdata/test_1 | 1 + sensor/internal/utils/testdata/test_2 | 0 sensor/internal/utils/utils.go | 145 +++++ sensor/internal/utils/utils_test.go | 139 +++++ sensor/kernelvariables.go | 109 ++++ sensor/kernelvariables_test.go | 31 + sensor/kubelet.go | 153 +++++ sensor/kubelet_test.go | 52 ++ sensor/kubeproxy.go | 47 ++ sensor/network.go | 69 +++ sensor/network_test.go | 13 + sensor/osrelease.go | 89 +++ sensor/testdata/clientCAKubeletConf.yaml | 19 + sensor/testdata/clientCAKubeletConf_2.yaml | 17 + sensor/testdata/clientCAKubeletConf_3.yaml | 11 + .../testmakehostfiles/dir/placeholder.json | 0 sensor/testdata/testmakehostfiles/file1.yaml | 0 sensor/testdata/testmakehostfiles/file2.yaml | 0 sensor/testdata/testmakehostfiles/file3.yaml | 0 sensor/verboseutils.go | 128 ++++ sensor/verboseutils_test.go | 38 ++ 53 files changed, 8117 insertions(+) create mode 100644 sensor/CNI.go create mode 100644 sensor/CNI_test.go create mode 100644 sensor/cloudProvider.go create mode 100644 sensor/cloudProvider_test.go create mode 100644 sensor/controlplane.go create mode 100644 sensor/controlplane_test.go create mode 100644 sensor/datastructures/fileinfo.go create mode 100644 sensor/datastructures/linuxsecurityhardeningstatus.go create mode 100644 sensor/error.go create mode 100644 sensor/globals.go create mode 100644 sensor/internal/utils/containerruntime.go create mode 100644 sensor/internal/utils/containerruntime_test.go create mode 100644 sensor/internal/utils/globals.go create mode 100644 sensor/internal/utils/osuserswrapper.go create mode 100644 sensor/internal/utils/osuserswrapper_test.go create mode 100644 sensor/internal/utils/process.go create mode 100644 sensor/internal/utils/process_test.go create mode 100644 sensor/internal/utils/service.go create mode 100644 sensor/internal/utils/testdata/etc/group create mode 100644 sensor/internal/utils/testdata/etc/passwd create mode 100644 sensor/internal/utils/testdata/testCNI/containerd.toml create mode 100644 sensor/internal/utils/testdata/testCNI/containerd_noparams.toml create mode 100644 sensor/internal/utils/testdata/testCNI/crio.conf create mode 100644 sensor/internal/utils/testdata/testCNI/crio.d/01_crio.conf create mode 100644 sensor/internal/utils/testdata/testCNI/crio.d/03_crio.conf create mode 100644 sensor/internal/utils/testdata/testCNI/crio.d/05_crio.conf create mode 100644 sensor/internal/utils/testdata/testCNI/crio.d/06_crio.conf create mode 100644 sensor/internal/utils/testdata/testCNI/crio.d_noparams/05_crio.conf create mode 100644 sensor/internal/utils/testdata/testCNI/crio.d_noparams/06_crio.conf create mode 100644 sensor/internal/utils/testdata/testCNI/crio_noparams.conf create mode 100644 sensor/internal/utils/testdata/test_1 create mode 100755 sensor/internal/utils/testdata/test_2 create mode 100644 sensor/internal/utils/utils.go create mode 100644 sensor/internal/utils/utils_test.go create mode 100644 sensor/kernelvariables.go create mode 100644 sensor/kernelvariables_test.go create mode 100644 sensor/kubelet.go create mode 100644 sensor/kubelet_test.go create mode 100644 sensor/kubeproxy.go create mode 100644 sensor/network.go create mode 100644 sensor/network_test.go create mode 100644 sensor/osrelease.go create mode 100644 sensor/testdata/clientCAKubeletConf.yaml create mode 100644 sensor/testdata/clientCAKubeletConf_2.yaml create mode 100644 sensor/testdata/clientCAKubeletConf_3.yaml create mode 100644 sensor/testdata/testmakehostfiles/dir/placeholder.json create mode 100644 sensor/testdata/testmakehostfiles/file1.yaml create mode 100644 sensor/testdata/testmakehostfiles/file2.yaml create mode 100644 sensor/testdata/testmakehostfiles/file3.yaml create mode 100644 sensor/verboseutils.go create mode 100644 sensor/verboseutils_test.go diff --git a/go.mod b/go.mod index 902de341..a4c08296 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect + github.com/BurntSushi/toml v1.3.2 github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/hcsshim v0.12.0-rc.1 // indirect github.com/armosec/armoapi-go v0.0.254 // indirect @@ -83,6 +84,7 @@ require ( github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jarcoal/httpmock v1.3.1 github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.3 // indirect @@ -130,6 +132,7 @@ require ( github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.2 // indirect github.com/uptrace/uptrace-go v1.18.0 // indirect github.com/vishvananda/netns v0.0.4 // indirect + github.com/weaveworks/procspy v0.0.0-20150706124340-cb970aa190c3 github.com/xlab/treeprint v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/runtime v0.44.0 // indirect diff --git a/go.sum b/go.sum index 5de34ac8..0504fe91 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= @@ -287,6 +289,8 @@ github.com/inspektor-gadget/inspektor-gadget v0.23.1 h1:lAo+6I79E7qYVkeoi1IhDviL github.com/inspektor-gadget/inspektor-gadget v0.23.1/go.mod h1:ciFeohyRWA1ZFnv2SdDVUAg3KclJsa1ZGiXqhL2rC5g= github.com/inspektor-gadget/netns v0.0.5-0.20230524185006-155d84c555d6 h1:fQqkJ+WkYfzy6BoUh32fr9uYrXfOGtsfw0skMQkfOic= github.com/inspektor-gadget/netns v0.0.5-0.20230524185006-155d84c555d6/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= +github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -489,6 +493,8 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/weaveworks/procspy v0.0.0-20150706124340-cb970aa190c3 h1:UC4iN/yCDCObTBhKzo34/R2U6qptTPmqbzG6UiQVMUQ= +github.com/weaveworks/procspy v0.0.0-20150706124340-cb970aa190c3/go.mod h1:cJTfuBcxkdbj8Mabk4PPdaf0AXv9TYEJmkFxKcWxYY4= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/sensor/CNI.go b/sensor/CNI.go new file mode 100644 index 00000000..dc976240 --- /dev/null +++ b/sensor/CNI.go @@ -0,0 +1,102 @@ +package sensor + +import ( + "context" + "fmt" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" + ds "github.com/kubescape/node-agent/sensor/datastructures" + "github.com/kubescape/node-agent/sensor/internal/utils" +) + +// KubeProxyInfo holds information about kube-proxy process +type CNIInfo struct { + CNIConfigFiles []*ds.FileInfo `json:"CNIConfigFiles,omitempty"` + + // The name of the running CNI + CNINames []string `json:"CNINames,omitempty"` +} + +// SenseCNIInfo return `CNIInfo` +func SenseCNIInfo(ctx context.Context) (*CNIInfo, error) { + var err error + ret := CNIInfo{} + + // make cni config files + CNIConfigInfo, err := makeCNIConfigFilesInfo(ctx) + + if err != nil { + logger.L().Ctx(ctx).Warning("SenseCNIInfo", helpers.Error(err)) + } else { + ret.CNIConfigFiles = CNIConfigInfo + } + + // get CNI name + ret.CNINames = getCNINames(ctx) + + return &ret, nil +} + +// makeCNIConfigFilesInfo - returns a list of FileInfos of cni config files. +func makeCNIConfigFilesInfo(ctx context.Context) ([]*ds.FileInfo, error) { + // *** Start handling CNI Files + kubeletProc, err := LocateKubeletProcess() + if err != nil { + return nil, err + } + + CNIConfigDir := utils.GetCNIConfigPath(ctx, kubeletProc) + + if CNIConfigDir == "" { + return nil, fmt.Errorf("no CNI Config dir found in getCNIConfigPath") + } + + //Getting CNI config files + CNIConfigInfo, err := makeHostDirFilesInfoVerbose(ctx, CNIConfigDir, true, nil, 0) + + if err != nil { + return nil, fmt.Errorf("failed to makeHostDirFilesInfo for CNIConfigDir %s: %w", CNIConfigDir, err) + } + + if len(CNIConfigInfo) == 0 { + logger.L().Debug("SenseCNIInfo - no cni config files were found.", + helpers.String("path", CNIConfigDir)) + } + + return CNIConfigInfo, nil +} + +// getCNIName - looking for CNI process and return CNI name, or empty if not found. +func getCNINames(ctx context.Context) []string { + var CNIs []string + supportedCNIs := []struct { + name string + processSuffix string + }{ + {"aws", "aws-k8s-agent"}, // aws VPC CNI agent + // 'canal' CNI "sets up Calico to handle policy management and Flannel to manage the network itself". Therefore, we will first + // check "calico" (which supports network policies and indicates for either 'canal' or 'calico') and then flannel. + {"Calico", "calico-node"}, + {"Flannel", "flanneld"}, + {"Cilium", "cilium-agent"}, + {"WeaveNet", "weave-net"}, + {"Kindnet", "kindnetd"}, + {"Multus", "multus"}, + } + + for _, cni := range supportedCNIs { + p, _ := utils.LocateProcessByExecSuffix(cni.processSuffix) + + if p != nil { + logger.L().Debug("CNI process found", helpers.String("name", cni.name)) + CNIs = append(CNIs, cni.name) + } + } + + if len(CNIs) == 0 { + logger.L().Warning("No CNI found") + } + + return CNIs +} diff --git a/sensor/CNI_test.go b/sensor/CNI_test.go new file mode 100644 index 00000000..0f6fc8b3 --- /dev/null +++ b/sensor/CNI_test.go @@ -0,0 +1,30 @@ +package sensor + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_getCNINames(t *testing.T) { + uid_tests := []struct { + name string + expected []string + }{ + { + name: "no_cni", + expected: []string(nil), + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + cniList := getCNINames(ctx) + if !assert.Equal(t, cniList, tt.expected) { + t.Logf("%s has different value", tt.name) + } + }) + } +} diff --git a/sensor/cloudProvider.go b/sensor/cloudProvider.go new file mode 100644 index 00000000..39e85351 --- /dev/null +++ b/sensor/cloudProvider.go @@ -0,0 +1,61 @@ +package sensor + +import ( + "github.com/armosec/utils-go/httputils" + "github.com/kubescape/node-agent/sensor/internal/utils" +) + +// CloudProviderInfo holds information about the Cloud Provider +type CloudProviderInfo struct { + // Has access to cloud provider meta data API + ProviderMetaDataAPIAccess bool `json:"providerMetaDataAPIAccess,omitempty"` +} + +// APIsURLs - hold urls along with their headers. +type APIsURLs struct { + url string + headers map[string]string +} + +// CloudProviderMetaDataAPIs - hold information on major cloud providers meta data access urls. +var CloudProviderMetaDataAPIs = []APIsURLs{ + { + "http://169.254.169.254/computeMetadata/v1/?alt=json&recursive=true", + map[string]string{"Metadata-Flavor": "Google"}, + }, + { + "http://169.254.169.254/metadata/instance?api-version=2021-02-01", + map[string]string{"Metadata": "true"}, + }, + { + "http://169.254.169.254/latest/meta-data/local-hostname", + map[string]string{}, + }, +} + +// SenseCloudProviderInfo returns `CloudProviderInfo` +func SenseCloudProviderInfo() (*CloudProviderInfo, error) { + + ret := CloudProviderInfo{} + + ret.ProviderMetaDataAPIAccess = hasMetaDataAPIAccess() + + return &ret, nil +} + +// hasMetaDataAPIAccess - checks if there is an access to cloud provider meta data +func hasMetaDataAPIAccess() bool { + client := utils.GetHttpClient() + client.Timeout = 1000000000 + + for _, req := range CloudProviderMetaDataAPIs { + res, err := httputils.HttpGet(client, req.url, req.headers) + + if err == nil && res.StatusCode == 200 { + return true + } + } + + return false + +} diff --git a/sensor/cloudProvider_test.go b/sensor/cloudProvider_test.go new file mode 100644 index 00000000..58f06b02 --- /dev/null +++ b/sensor/cloudProvider_test.go @@ -0,0 +1,45 @@ +package sensor + +import ( + "testing" + + "github.com/jarcoal/httpmock" + "github.com/stretchr/testify/assert" +) + +func Test_hasMetaDataAPIAccess(t *testing.T) { + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + // Exact URL match + httpmock.RegisterResponder("GET", "http://www.mygoodurl.com", + httpmock.NewStringResponder(200, `[{"id": 1, "name": "My Good URL"}]`)) + + CloudProviderMetaDataAPIs = []APIsURLs{ + { + "http://www.mygoodurl.com", + map[string]string{}, + }, + { + "http://10.20.30.100", + map[string]string{}, + }, + } + + t.Run("Has Access", func(t *testing.T) { + res := hasMetaDataAPIAccess() + assert.Equal(t, true, res) + }) + + CloudProviderMetaDataAPIs = []APIsURLs{ + { + "http://10.20.30.100", + map[string]string{}, + }, + } + + t.Run("No Access", func(t *testing.T) { + res := hasMetaDataAPIAccess() + assert.Equal(t, false, res) + }) +} diff --git a/sensor/controlplane.go b/sensor/controlplane.go new file mode 100644 index 00000000..e35c8ed7 --- /dev/null +++ b/sensor/controlplane.go @@ -0,0 +1,321 @@ +package sensor + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" + "gopkg.in/yaml.v3" + + ds "github.com/kubescape/node-agent/sensor/datastructures" + "github.com/kubescape/node-agent/sensor/internal/utils" +) + +const ( + apiServerExe = "/kube-apiserver" + controllerManagerExe = "/kube-controller-manager" + schedulerExe = "/kube-scheduler" + etcdExe = "/etcd" + etcdDataDirArg = "--data-dir" + apiEncryptionProviderConfigArg = "--encryption-provider-config" + auditPolicyFileArg = "--audit-policy-file" + + // Default files paths according to https://workbench.cisecurity.org/benchmarks/8973/sections/1126652 + apiServerSpecsPath = "/etc/kubernetes/manifests/kube-apiserver.yaml" + controllerManagerSpecsPath = "/etc/kubernetes/manifests/kube-controller-manager.yaml" + controllerManagerConfigPath = "/etc/kubernetes/controller-manager.conf" + schedulerSpecsPath = "/etc/kubernetes/manifests/kube-scheduler.yaml" + schedulerConfigPath = "/etc/kubernetes/scheduler.conf" + etcdConfigPath = "/etc/kubernetes/manifests/etcd.yaml" + adminConfigPath = "/etc/kubernetes/admin.conf" + pkiDir = "/etc/kubernetes/pki" + + // TODO: cni +) + +var ( + ErrDataDirNotFound = errors.New("failed to find etcd data-dir") +) + +// KubeProxyInfo holds information about kube-proxy process +type ControlPlaneInfo struct { + APIServerInfo *ApiServerInfo `json:"APIServerInfo,omitempty"` + ControllerManagerInfo *K8sProcessInfo `json:"controllerManagerInfo,omitempty"` + SchedulerInfo *K8sProcessInfo `json:"schedulerInfo,omitempty"` + EtcdConfigFile *ds.FileInfo `json:"etcdConfigFile,omitempty"` + EtcdDataDir *ds.FileInfo `json:"etcdDataDir,omitempty"` + AdminConfigFile *ds.FileInfo `json:"adminConfigFile,omitempty"` + PKIDIr *ds.FileInfo `json:"PKIDir,omitempty"` + PKIFiles []*ds.FileInfo `json:"PKIFiles,omitempty"` +} + +// K8sProcessInfo holds information about a k8s process +type K8sProcessInfo struct { + // Information about the process specs file (if relevant) + SpecsFile *ds.FileInfo `json:"specsFile,omitempty"` + + // Information about the process config file (if relevant) + ConfigFile *ds.FileInfo `json:"configFile,omitempty"` + + // Information about the process kubeconfig file (if relevant) + KubeConfigFile *ds.FileInfo `json:"kubeConfigFile,omitempty"` + + // Information about the process client ca file (if relevant) + ClientCAFile *ds.FileInfo `json:"clientCAFile,omitempty"` + + // Raw cmd line of the process + CmdLine string `json:"cmdLine"` +} + +type ApiServerInfo struct { + EncryptionProviderConfigFile *ds.FileInfo `json:"encryptionProviderConfigFile,omitempty"` + AuditPolicyFile *ds.FileInfo `json:"auditPolicyFile,omitempty"` + *K8sProcessInfo `json:",inline"` +} + +// getEtcdDataDir find the `data-dir` path of etcd k8s component +func getEtcdDataDir() (string, error) { + + proc, err := utils.LocateProcessByExecSuffix(etcdExe) + if err != nil { + return "", fmt.Errorf("failed to locate etcd process: %w", err) + } + + dataDir, ok := proc.GetArg(etcdDataDirArg) + if !ok || dataDir == "" { + return "", ErrDataDirNotFound + } + + return dataDir, nil +} + +func makeProcessInfoVerbose(ctx context.Context, p *utils.ProcessDetails, specsPath, configPath, kubeConfigPath, clientCaPath string) *K8sProcessInfo { + ret := K8sProcessInfo{} + + // init files + files := []struct { + data **ds.FileInfo + path string + file string + }{ + {&ret.SpecsFile, specsPath, "specs"}, + {&ret.ConfigFile, configPath, "config"}, + {&ret.KubeConfigFile, kubeConfigPath, "kubeconfig"}, + {&ret.ClientCAFile, clientCaPath, "client ca certificate"}, + } + + // get data + for i := range files { + file := &files[i] + if file.path == "" { + continue + } + + *file.data = makeHostFileInfoVerbose(ctx, file.path, false, + helpers.String("in", "makeProcessInfoVerbose"), + helpers.String("path", file.path), + ) + } + + if p != nil { + ret.CmdLine = p.RawCmd() + } + + // Return `nil` if wasn't able to find any data + if ret == (K8sProcessInfo{}) { + return nil + } + + return &ret +} + +// makeAPIserverEncryptionProviderConfigFile returns a ds.FileInfo object for the encryption provider config file of the API server. Required for https://workbench.cisecurity.org/sections/1126663/recommendations/1838675 +func makeAPIserverEncryptionProviderConfigFile(ctx context.Context, p *utils.ProcessDetails) *ds.FileInfo { + encryptionProviderConfigPath, ok := p.GetArg(apiEncryptionProviderConfigArg) + if !ok { + logger.L().Ctx(ctx).Warning("failed to find encryption provider config path", helpers.String("in", "makeAPIserverEncryptionProviderConfigFile")) + return nil + } + + fi, err := utils.MakeContaineredFileInfo(ctx, p, encryptionProviderConfigPath, true) + if err != nil { + logger.L().Ctx(ctx).Warning("failed to create encryption provider config file info", helpers.Error(err)) + return nil + } + + // remove sensitive data + data := map[string]interface{}{} + + if errYaml := yaml.Unmarshal(fi.Content, &data); errYaml != nil { + if errJson := json.Unmarshal(fi.Content, &data); errJson != nil { + logger.L().Ctx(ctx).Warning("failed to unmarshal encryption provider config file", helpers.Error(errJson), helpers.Error(errYaml)) + return nil + } + } + + removeEncryptionProviderConfigSecrets(data) + + // marshal back to yaml + fi.Content, err = yaml.Marshal(data) + if err != nil { + logger.L().Ctx(ctx).Warning("failed to marshal encryption provider config file", helpers.Error(err)) + return nil + } + + return fi +} + +func removeEncryptionProviderConfigSecrets(data map[string]interface{}) { + resources, ok := data["resources"].([]interface{}) + if !ok { + return + } + + for i := range resources { + resource, ok := resources[i].(map[string]interface{}) + if !ok { + continue + } + + providers, ok := resource["providers"].([]interface{}) + if !ok { + continue + } + + for j := range providers { + provider, ok := providers[j].(map[string]interface{}) + if !ok { + continue + } + + for key := range provider { + object, ok := provider[key].(map[string]interface{}) + if !ok { + continue + } + keys, ok := object["keys"].([]interface{}) + if !ok { + continue + } + for k := range keys { + key, ok := keys[k].(map[string]interface{}) + if !ok { + continue + } + key["secret"] = "" + keys[k] = key + } + object["keys"] = keys + provider[key] = object + } + providers[j] = provider + } + resource["providers"] = providers + resources[i] = resource + } + data["resources"] = resources +} + +// makeAPIserverAuditPolicyFile returns a ds.FileInfo object for an audit policy file of the API server. Required for https://workbench.cisecurity.org/sections/1126663/recommendations/1838675 +func makeAPIserverAuditPolicyFile(ctx context.Context, p *utils.ProcessDetails) *ds.FileInfo { + auditPolicyFilePath, ok := p.GetArg(auditPolicyFileArg) + if !ok { + logger.L().Info("audit-policy-file argument was not set ", helpers.String("in", "makeAPIserverAuditPolicyFile")) + return nil + } + + return makeContaineredFileInfoVerbose(ctx, p, auditPolicyFilePath, true, + helpers.String("in", "makeAPIserverAuditPolicyFile"), + ) +} + +// SenseControlPlaneInfo return `ControlPlaneInfo` +func SenseControlPlaneInfo(ctx context.Context) (*ControlPlaneInfo, error) { + var err error + ret := ControlPlaneInfo{} + + debugInfo := helpers.String("in", "SenseControlPlaneInfo") + + apiProc, err := utils.LocateProcessByExecSuffix(apiServerExe) + if err == nil { + ret.APIServerInfo = &ApiServerInfo{} + ret.APIServerInfo.K8sProcessInfo = makeProcessInfoVerbose(ctx, apiProc, apiServerSpecsPath, "", "", "") + ret.APIServerInfo.EncryptionProviderConfigFile = makeAPIserverEncryptionProviderConfigFile(ctx, apiProc) + ret.APIServerInfo.AuditPolicyFile = makeAPIserverAuditPolicyFile(ctx, apiProc) + } else { + logger.L().Ctx(ctx).Warning("SenseControlPlaneInfo", helpers.Error(err)) + } + + controllerMangerProc, err := utils.LocateProcessByExecSuffix(controllerManagerExe) + if err == nil { + ret.ControllerManagerInfo = makeProcessInfoVerbose(ctx, controllerMangerProc, controllerManagerSpecsPath, controllerManagerConfigPath, "", "") + } else { + logger.L().Ctx(ctx).Warning("SenseControlPlaneInfo", helpers.Error(err)) + } + + SchedulerProc, err := utils.LocateProcessByExecSuffix(schedulerExe) + if err == nil { + ret.SchedulerInfo = makeProcessInfoVerbose(ctx, SchedulerProc, schedulerSpecsPath, schedulerConfigPath, "", "") + } else { + logger.L().Ctx(ctx).Warning("SenseControlPlaneInfo", helpers.Error(err)) + } + + // EtcdConfigFile + ret.EtcdConfigFile = makeHostFileInfoVerbose(ctx, etcdConfigPath, + false, + debugInfo, + helpers.String("component", "EtcdConfigFile"), + ) + + // AdminConfigFile + ret.AdminConfigFile = makeHostFileInfoVerbose(ctx, adminConfigPath, + false, + debugInfo, + helpers.String("component", "AdminConfigFile"), + ) + + // PKIDIr + ret.PKIDIr = makeHostFileInfoVerbose(ctx, pkiDir, + false, + debugInfo, + helpers.String("component", "PKIDIr"), + ) + + // PKIFiles + ret.PKIFiles, err = makeHostDirFilesInfoVerbose(ctx, pkiDir, true, nil, 0) + if err != nil { + logger.L().Ctx(ctx).Warning("SenseControlPlaneInfo failed to get PKIFiles info", helpers.Error(err)) + } + + // etcd data-dir + etcdDataDir, err := getEtcdDataDir() + if err != nil { + logger.L().Ctx(ctx).Warning("SenseControlPlaneInfo", helpers.Error(ErrDataDirNotFound)) + } else { + ret.EtcdDataDir = makeHostFileInfoVerbose(ctx, etcdDataDir, + false, + debugInfo, + helpers.String("component", "EtcdDataDir"), + ) + } + + // If wasn't able to find any data - this is not a control plane + if ret.APIServerInfo == nil && + ret.ControllerManagerInfo == nil && + ret.SchedulerInfo == nil && + ret.EtcdConfigFile == nil && + ret.EtcdDataDir == nil && + ret.AdminConfigFile == nil { + return nil, &SenseError{ + Massage: "not a control plane node", + Function: "SenseControlPlaneInfo", + Code: http.StatusOK, + } + } + + return &ret, nil +} diff --git a/sensor/controlplane_test.go b/sensor/controlplane_test.go new file mode 100644 index 00000000..e3983367 --- /dev/null +++ b/sensor/controlplane_test.go @@ -0,0 +1,86 @@ +package sensor + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_removeEncryptionProviderConfigSecrets(t *testing.T) { + type args struct { + data string + } + tests := []struct { + name string + args args + output string + }{ + { + name: "test", + args: args{ + data: `{ + "apiVersion": "apiserver.config.k8s.io/v1", + "kind": "EncryptionConfiguration", + "resources": [ + { + "providers": [ + { + "aescbc": { + "keys": [ + { + "name": "key1", + "secret": "" + } + ] + } + }, + { + "identity": {} + } + ], + "resources": [ + "secrets" + ] + } + ] + }`, + }, + output: `{ + "apiVersion": "apiserver.config.k8s.io/v1", + "kind": "EncryptionConfiguration", + "resources": [ + { + "providers": [ + { + "aescbc": { + "keys": [ + { + "name": "key1", + "secret": "" + } + ] + } + }, + { + "identity": {} + } + ], + "resources": [ + "secrets" + ] + } + ] + }`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var data map[string]interface{} + json.Unmarshal([]byte(tt.args.data), &data) + removeEncryptionProviderConfigSecrets(data) + v, _ := json.Marshal(data) + require.JSONEq(t, tt.output, string(v)) + }) + } +} diff --git a/sensor/datastructures/fileinfo.go b/sensor/datastructures/fileinfo.go new file mode 100644 index 00000000..25c098b1 --- /dev/null +++ b/sensor/datastructures/fileinfo.go @@ -0,0 +1,34 @@ +package datastructures + +// FileInfo holds information about a file +type FileInfo struct { + // Ownership information + Ownership *FileOwnership `json:"ownership"` + + // The path of the file + // Example: /etc/kubernetes/manifests/kube-apiserver.yaml + Path string `json:"path"` + + // Content of the file + Content []byte `json:"content,omitempty"` + Permissions int `json:"permissions"` +} + +// User +// FileOwnership holds the ownership of a file +type FileOwnership struct { + // Error if couldn't get owner's file or uid/gid + Err string `json:"err,omitempty"` + + // UID owner of the files + UID int64 `json:"uid"` + + // GID of the file + GID int64 `json:"gid"` + + // username extracted by UID from {root}/etc/passwd + Username string `json:"username"` + + // group name extracted by GID from {root}/etc/group + Groupname string `json:"groupname"` +} diff --git a/sensor/datastructures/linuxsecurityhardeningstatus.go b/sensor/datastructures/linuxsecurityhardeningstatus.go new file mode 100644 index 00000000..2f9127fc --- /dev/null +++ b/sensor/datastructures/linuxsecurityhardeningstatus.go @@ -0,0 +1,6 @@ +package datastructures + +type LinuxSecurityHardeningStatus struct { + AppArmor string `json:"appArmor"` + SeLinux string `json:"seLinux"` +} diff --git a/sensor/error.go b/sensor/error.go new file mode 100644 index 00000000..d2a72038 --- /dev/null +++ b/sensor/error.go @@ -0,0 +1,34 @@ +package sensor + +import ( + "fmt" +) + +// SenseError is informative sensor error +type SenseError struct { + err error // The wrapped error + Massage string `json:"error"` // The error message + Function string `json:"-"` // The function where the error occurred + Code int `json:"-"` // The error code (for HTTP response codes) +} + +// Error implements error interface +func (err *SenseError) Error() string { + internalErr := "" + if err.err != nil { + internalErr = err.err.Error() + } + return fmt.Sprintf("%s %s", err.Massage, internalErr) +} + +// Unwrap implementation for errors.Unwrap +func (err *SenseError) Unwrap() error { return err.err } + +// Is implementation for errors.Is +func (err *SenseError) Is(target error) bool { + sensErrTarget, ok := target.(*SenseError) + if !ok { + return false + } + return err.Massage == sensErrTarget.Massage && err.Code == sensErrTarget.Code +} diff --git a/sensor/globals.go b/sensor/globals.go new file mode 100644 index 00000000..24281f14 --- /dev/null +++ b/sensor/globals.go @@ -0,0 +1,5 @@ +package sensor + +const ( + kubeConfigArgName = "--kubeconfig" +) diff --git a/sensor/internal/utils/containerruntime.go b/sensor/internal/utils/containerruntime.go new file mode 100644 index 00000000..d6f9b951 --- /dev/null +++ b/sensor/internal/utils/containerruntime.go @@ -0,0 +1,413 @@ +package utils + +import ( + "context" + "errors" + "fmt" + "os" + "path" + "sort" + "strings" + + "github.com/BurntSushi/toml" + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" +) + +// CNI default constants +const ( + CNIDefaultConfigDir string = "/etc/cni/" + + // kubelet flags for container runtime and cni configuration dir. + kubeletContainerRuntime = "--container-runtime" + kubeletContainerRuntimeEndPoint = "--container-runtime-endpoint" + kubeletCNIConfigDir = "--cni-conf-dir" +) + +// Types of supported container runtime processes. +const ( + // container runtimes names + containerdContainerRuntimeName = "containerd" + crioContainerRuntimeName = "crio" + + containerdConfigSection = "io.containerd.grpc.v1.cri" + + // container runtime processes suffix + crioSock = "/crio.sock" + containerdSock = "/containerd.sock" + cridockerdSock = "/cri-dockerd.sock" +) + +// Errors +var ( + // ErrDockershimRT means the used container runtime is dockershim. + // no kubelet flags or --container-runtime not 'remote' means dockershim.sock which is not supported + ErrDockershimRT = errors.New("dockershim runtime is not supported") + + // ErrCRINotFound means no container runtime was found. + ErrCRINotFound = errors.New("no container runtime was found") +) + +// A containerRuntimeProperties holds properties of a container runtime. +type containerRuntimeProperties struct { + Name string + + // Process for cuflag stom config file. + ConfigArgName string + + // Process flag for custom configuration directory. + ConfigDirArgName string + + // Default config path. + DefaultConfigPath string + + // default configuration directory. + DefaultConfigDir string + + // suffix of container runtime process. + ProcessSuffix string + + // the socket suffix - used to identify the container runtime from kubelet. + Socket string + + // process pararm for CNI configuration directory. + CNIConfigDirArgName string + + // extract CNI info function + ParseCNIFromConfigFunc func(string) (string, error) +} + +// A ContainerRuntimeInfo holds a container runtime properties and process info. +type ContainerRuntimeInfo struct { + // container runtime properties + properties *containerRuntimeProperties + + // process pointer + process *ProcessDetails + + // root + rootDir string +} + +// getCNIConfigPath returns CNI config dir from a running container runtime. Flow: +// 1. Find CNI config dir through kubelet flag (--container-runtime-endpoint). If not found: +// 2. Find CNI config dir through process of supported container runtimes. If not found: +// 3. return CNI config dir default that is defined in the container runtime properties. +func GetCNIConfigPath(ctx context.Context, kubeletProc *ProcessDetails) string { + + // Attempting to find CR from kubelet. + CNIConfigDir, err := CNIConfigDirFromKubelet(ctx, kubeletProc) + + if err == nil { + return CNIConfigDir + } + + // Could construct container runtime from kubelet + logger.L().Debug("getCNIConfigPath - failed to get CNI config dir through kubelete flags.") + + // Attempting to find CR through process. + cr, err := getContainerRuntimeFromProcess() + + if err != nil { + //Failed to get container runtime from process + logger.L().Debug("getCNIConfigPath - failed to get container runtime from process, return cni config dir default", + helpers.Error(err)) + + return CNIDefaultConfigDir + } + + CNIConfigDir = cr.getCNIConfigDir(ctx) + if CNIConfigDir == "" { + return CNIDefaultConfigDir + } + return CNIConfigDir + +} + +// getConfigDirPath - returns container runtime config directory through process flag. If not found returns default config directory from container runtime properties. +func (cr *ContainerRuntimeInfo) getConfigDirPath() string { + configDirPath, _ := cr.process.GetArg(cr.properties.ConfigDirArgName) + + if configDirPath == "" { + configDirPath = path.Join(cr.rootDir, cr.properties.DefaultConfigDir) + } + + return configDirPath +} + +// getConfigPath - returns container runtime config path through process flag. If not found returns default. +func (cr *ContainerRuntimeInfo) getConfigPath() string { + configPath, _ := cr.process.GetArg(cr.properties.ConfigArgName) + if configPath == "" { + logger.L().Debug("getConfigPath - container runtime config file wasn't found through process flags, return default path", + helpers.String("Container Runtime Name", cr.properties.Name), + helpers.String("defaultConfigPath", cr.properties.DefaultConfigPath)) + configPath = cr.properties.DefaultConfigPath + + } else { + logger.L().Debug("getConfigPath - container runtime config file found through process flags", + helpers.String("Container Runtime Name", cr.properties.Name), + helpers.String("configPath", configPath)) + } + + return path.Join(cr.rootDir, configPath) +} + +// getCNIConfigDirFromConfig - returns CNI Config dir from the container runtime config file if exist. +// flow: +// 1. Getting container runtime configs directory path and container runtime config path. +// 2. Build a decending ordered list of configs from configs directory and adding the config path as last. This is the order of precedence for configuration. +// 3. Get CNI config path from ordered list. If not found, return empty string. +func (cr *ContainerRuntimeInfo) getCNIConfigDirFromConfig(ctx context.Context) string { + + var configDirFilesFullPath []string + + // Getting all config files in drop in folder if exist. + configDirPath := cr.getConfigDirPath() + + // Call ReadDir to get all files. + outputDirFiles, err := os.ReadDir(configDirPath) + + if err != nil { + logger.L().Ctx(ctx).Warning("getCNIConfigDirFromConfig- Failed to Call ReadDir", + helpers.String("configDirPath", configDirPath), + helpers.Error(err)) + } else { + configDirFilesFullPath = make([]string, len(outputDirFiles)) + + // construct and reverse sort config dir files full path + for i, filename := range outputDirFiles { + configDirFilesFullPath[i] = path.Join(configDirPath, filename.Name()) + } + + sort.Sort(sort.Reverse(sort.StringSlice(configDirFilesFullPath))) + } + + configPath := cr.getConfigPath() + + //appending config file to the end of the list as it always has the lowest priority. + if configPath != "" { + configDirFilesFullPath = append(configDirFilesFullPath, configPath) + } + + CNIConfigDir := cr.getCNIConfigDirFromConfigPaths(configDirFilesFullPath) + + if CNIConfigDir == "" { + logger.L().Debug("getCNIConfigDirFromConfig didn't find CNI Config dir in container runtime configs", helpers.String("Container Runtime Name", cr.properties.Name)) + } + + return CNIConfigDir + +} + +// getCNIConfigDirFromConfigPaths - Get a list of configpaths, run through the paths by order, parse the CNI config dir and return once found. If not found, return empty string. +func (cr *ContainerRuntimeInfo) getCNIConfigDirFromConfigPaths(configPaths []string) string { + + for _, configPath := range configPaths { + CNIConfigDir, err := cr.properties.ParseCNIFromConfigFunc(configPath) + + if err != nil { + logger.L().Debug("getCNIConfigDirFromConfigPaths - Failed to parse config file", helpers.String("configPath", configPath), helpers.Error(err)) + continue + } + + if CNIConfigDir != "" { + return CNIConfigDir + } + + } + + return "" + +} + +// getCNIConfigDirFromProcess - returns CNI config dir from process cmdline flags if defined, otherwise returns empty string. +func (cr *ContainerRuntimeInfo) getCNIConfigDirFromProcess() string { + + if cr.properties.CNIConfigDirArgName != "" { + CNIConfigDir, _ := cr.process.GetArg(cr.properties.CNIConfigDirArgName) + if CNIConfigDir != "" { + logger.L().Debug("getCNIConfigDir found CNI Config Dir in process", helpers.String("Container Runtime Name", cr.properties.Name)) + } + + return CNIConfigDir + } + + return "" + +} + +// getCNIConfigDir - returns CNI config dir of the container runtime. +// 1. Get dir from container runtime process flags. If not found: +// 2. Get dir from container runtime config file(s). If not found: +// 3. return default CNI config dir +func (cr *ContainerRuntimeInfo) getCNIConfigDir(ctx context.Context) string { + + CNIConfigDir := cr.getCNIConfigDirFromProcess() + + if CNIConfigDir != "" { + return CNIConfigDir + } + + CNIConfigDir = cr.getCNIConfigDirFromConfig(ctx) + + return CNIConfigDir +} + +// containerdProps - returns container runtime "containerd" properties. +func containerdProps() *containerRuntimeProperties { + return &containerRuntimeProperties{Name: containerdContainerRuntimeName, + DefaultConfigPath: "/etc/containerd/config.toml", + ProcessSuffix: "/containerd", + Socket: "/containerd.sock", + ConfigArgName: "--config", + ConfigDirArgName: "", + DefaultConfigDir: "/etc/containerd/containerd.conf.d", + CNIConfigDirArgName: "", + ParseCNIFromConfigFunc: parseCNIConfigDirFromConfigContainerd} + +} + +// crioProps - returns container runtime "cri-o" properties. +func crioProps() *containerRuntimeProperties { + return &containerRuntimeProperties{Name: crioContainerRuntimeName, + DefaultConfigPath: "/etc/crio/crio.conf", + ProcessSuffix: "/crio", + Socket: "/crio.sock", + ConfigArgName: "--config", + ConfigDirArgName: "--config-dir", + DefaultConfigDir: "/etc/crio/crio.conf.d", + CNIConfigDirArgName: "--cni-config-dir", + ParseCNIFromConfigFunc: parseCNIConfigDirFromConfigCrio} + +} + +// newContainerRuntime is a constructor for ContainerRuntime object. Constructor will fail if process wasn't found for container runtime. +// Constructor accept CRIKind as parameter which can be either a container runtime name or container runtime process suffix. +func newContainerRuntime(CRIKind string) (*ContainerRuntimeInfo, error) { + + cr := &ContainerRuntimeInfo{} + + switch CRIKind { + case containerdContainerRuntimeName, containerdSock: + cr.properties = containerdProps() + case crioContainerRuntimeName, crioSock: + cr.properties = crioProps() + + default: + return nil, fmt.Errorf("newContainerRuntime of kind '%s' is not supported", CRIKind) + + } + p, err := LocateProcessByExecSuffix(cr.properties.ProcessSuffix) + + // if process wasn't find, fail to construct object + if err != nil || p == nil { + return nil, fmt.Errorf("newContainerRuntime - Failed to locate process for CRIKind %s", CRIKind) + } + + cr.process = p + cr.rootDir = HostFileSystemDefaultLocation + + return cr, nil + +} + +// getContainerRuntimeFromProcess - returns first container runtime found by process. +func getContainerRuntimeFromProcess() (*ContainerRuntimeInfo, error) { + + crObj, err := newContainerRuntime(containerdContainerRuntimeName) + + if err != nil { + crObj, err = newContainerRuntime(crioContainerRuntimeName) + + if err != nil { + return nil, fmt.Errorf("getContainerRuntimeFromProcess didnt find Container Runtime process") + } + } + + return crObj, nil + +} + +// parseCNIConfigDirFromConfigContainerd - parses and returns cni config dir from a containerd config structure. If not found returns empty string. +func parseCNIConfigDirFromConfigContainerd(configPath string) (string, error) { + + cniConfig := struct { + Plugins struct { + IoContainerdGrpcV1CRI struct { + CNI struct { + CNIConfigDir string `toml:"conf_dir"` + } `toml:"cni"` + } `toml:"io.containerd.grpc.v1.cri"` + } `toml:"plugins"` + }{} + + _, err := toml.DecodeFile(configPath, &cniConfig) + + if err != nil { + return "", err + } + + return cniConfig.Plugins.IoContainerdGrpcV1CRI.CNI.CNIConfigDir, nil +} + +// parseCNIConfigDirFromConfigCrio - parses and returns cni config dir from a cri-o config structure. If not found returns empty string. +func parseCNIConfigDirFromConfigCrio(configPath string) (string, error) { + cniConfig := struct { + Crio struct { + Network struct { + NetworkDir string `toml:"network_dir"` + } `toml:"network"` + } `toml:"crio"` + }{} + + _, err := toml.DecodeFile(configPath, &cniConfig) + + if err != nil { + return "", err + } + + return cniConfig.Crio.Network.NetworkDir, nil +} + +// CNIConfigDirFromKubelet - returns cni config dir by kubelet --container-runtime-endpoint flag. Returns empty string if not found. +// A specific case is cri-dockerd.sock process which it's container runtime is determined by kubernetes docs. +func CNIConfigDirFromKubelet(ctx context.Context, proc *ProcessDetails) (string, error) { + + // Try from kubelet process flags + CNIConfigDir, _ := proc.GetArg(kubeletCNIConfigDir) + if CNIConfigDir != "" { + return CNIConfigDir, nil + } + + // Try from kubelet process CRI socket + crEndpoint, crEndPointOK := proc.GetArg(kubeletContainerRuntimeEndPoint) + if crEndpoint == "" { + cr, crOK := proc.GetArg(kubeletContainerRuntime) + if (!crEndPointOK && !crOK) || (cr != "remote") { + // From k8s docs (https://kubernetes.io/docs/tasks/administer-cluster/migrating-from-dockershim/find-out-runtime-you-use/#which-endpoint): + // " + // If your nodes use Kubernetes v1.23 and earlier and these flags aren't present + // or if the --container-runtime flag is not remote, you use the dockershim socket with Docker Engine. + // " + return "", ErrDockershimRT + } + // Unknown + return "", ErrCRINotFound + } + + containerProcessSock := crEndpoint + if strings.HasSuffix(crEndpoint, cridockerdSock) { + // Check specific case where the end point is cri-dockerd. If so, then in the absence of cni paths configuration for cri-dockerd process, + // we check containerd (which is using cri-dockerd as a CRI plugin) + containerProcessSock = containerdSock + } + + crObj, err := newContainerRuntime(containerProcessSock) + + if err != nil { + return "", err + } + + return crObj.getCNIConfigDir(ctx), nil +} diff --git a/sensor/internal/utils/containerruntime_test.go b/sensor/internal/utils/containerruntime_test.go new file mode 100644 index 00000000..c7602b82 --- /dev/null +++ b/sensor/internal/utils/containerruntime_test.go @@ -0,0 +1,533 @@ +package utils + +import ( + "context" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_GetCNIConfigPath(t *testing.T) { + uid_tests := []struct { + name string + process string + pid int32 + expected string + }{ + { + name: "kubelet_kind", + process: "/usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --node-ip=172.18.0.2 --node-labels= --pod-infra-container-image=registry.k8s.io/pause:3.8 --provider-id=kind://docker/cis-test/cis-test-control-plane --fail-swap-on=false --cgroup-root=/kubelet", + pid: 15, + expected: "/etc/cni/", + }, + { + name: "kubelet_manual_installation", + process: "/usr/local/bin/kubelet --config=/var/lib/kubelet/kubelet-config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --image-pull-progress-deadline=2m --kubeconfig=/var/lib/kubelet/kubeconfig --network-plugin=cni --register-node=true --v=2", + pid: 15, + expected: "/etc/cni/", + }, + { + name: "kubelet_manual_installation_with_custom_runtime_endpoint", + process: "/usr/local/bin/kubelet --config=/var/lib/kubelet/kubelet-config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --image-pull-progress-deadline=2m --kubeconfig=/var/lib/kubelet/kubeconfig --network-plugin=cni --container-runtime-endpoint=/run/containerd/containerd.sock", + pid: 15, + expected: "/etc/cni/", + //expected: "/run/containerd/", + }, + { + name: "kubelet_manual_installation_with_custom_cni_dir", + process: "/usr/local/bin/kubelet --config=/var/lib/kubelet/kubelet-config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --image-pull-progress-deadline=2m --kubeconfig=/var/lib/kubelet/kubeconfig --network-plugin=cni --container-runtime-endpoint=/run/containerd/containerd.sock --cni-conf-dir=/var/lib/cni/", + pid: 15, + expected: "/var/lib/cni/", + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + // create ProcessDetails object to pass it to GetCNIConfigPath + proc := &ProcessDetails{ + CmdLine: strings.Split(tt.process, " "), + PID: tt.pid, + } + cniConfigPath := GetCNIConfigPath(ctx, proc) + + if !assert.Equal(t, tt.expected, cniConfigPath) { + t.Logf("%s has different output\n", tt.name) + } + }) + } +} + +func Test_getConfigDirPath(t *testing.T) { + uid_tests := []struct { + name string + cmdline string + defaultConfigDir string + configDirArgName string + expected string + }{ + { + name: "crio", + cmdline: "/usr/bin/crio --config-dir /etc/crio/crio.d", + defaultConfigDir: "/etc/crio/crio.conf.d/", + configDirArgName: "--config-dir", + expected: "/etc/crio/crio.d", + }, + { + name: "crio_default", + cmdline: "/usr/bin/crio", + defaultConfigDir: "/etc/crio/crio.conf.d", + configDirArgName: "--config-dir", + expected: "/etc/crio/crio.conf.d", + }, + { + name: "containerd", + cmdline: "/usr/bin/containerd", + defaultConfigDir: "/etc/containerd/containerd.conf.d/", + configDirArgName: "", + expected: "/etc/containerd/containerd.conf.d", + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + // create ContainerRuntimeInfo + cr := &ContainerRuntimeInfo{ + properties: &containerRuntimeProperties{ + DefaultConfigDir: tt.defaultConfigDir, + ConfigDirArgName: tt.configDirArgName, + }, + process: &ProcessDetails{ + CmdLine: strings.Split(tt.cmdline, " "), + }, + rootDir: "", + } + + configDirPath := cr.getConfigDirPath() + + if !assert.Equal(t, tt.expected, configDirPath) { + t.Logf("%s has different output\n", tt.name) + } + }) + } +} + +func Test_getConfigPath(t *testing.T) { + uid_tests := []struct { + name string + cmdLine string + defaultConfigPath string + configArgName string + expected string + }{ + { + name: "crio", + cmdLine: "/usr/bin/crio --config /etc/personaldir/crio.conf", + configArgName: "--config", + defaultConfigPath: "/etc/crio/crio.conf", + expected: "/etc/personaldir/crio.conf", + }, + { + name: "crio_default", + cmdLine: "/usr/bin/crio", + configArgName: "--config", + defaultConfigPath: "/etc/crio/crio.conf", + expected: "/etc/crio/crio.conf", + }, + { + name: "containerd", + cmdLine: "/usr/bin/containerd --config /etc/personaldir/containerd.toml", + configArgName: "--config", + defaultConfigPath: "/etc/containerd/config.toml", + expected: "/etc/personaldir/containerd.toml", + }, + { + name: "containerd_default", + cmdLine: "/usr/bin/containerd", + configArgName: "--config", + defaultConfigPath: "/etc/containerd/config.toml", + expected: "/etc/containerd/config.toml", + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + // create ContainerRuntimeInfo + cr := &ContainerRuntimeInfo{ + properties: &containerRuntimeProperties{ + DefaultConfigPath: tt.defaultConfigPath, + ConfigArgName: tt.configArgName, + }, + process: &ProcessDetails{ + CmdLine: strings.Split(tt.cmdLine, " "), + }, + rootDir: "", + } + + configPath := cr.getConfigPath() + + if !assert.Equal(t, tt.expected, configPath) { + t.Logf("%s has different output\n", tt.name) + } + }) + } +} + +func Test_getCNIConfigDirFromConfig(t *testing.T) { + uid_tests := []struct { + name string + cmdLine string + defaultConfigDir string + defaultConfigPath string + parseCNIFromConfigFunc func(string) (string, error) + expected string + }{ + { + name: "containerd", + cmdLine: "/usr/bin/containerd", + defaultConfigDir: "testdata/testCNI/", + defaultConfigPath: "testdata/testCNI/containerd.toml", + parseCNIFromConfigFunc: parseCNIConfigDirFromConfigContainerd, + expected: "/etc/cni/net.mk", + }, + { + name: "crio", + cmdLine: "/usr/bin/crio", + defaultConfigDir: "testdata/testCNI/crio.d", + defaultConfigPath: "testdata/testCNI/crio.conf", + parseCNIFromConfigFunc: parseCNIConfigDirFromConfigCrio, + expected: "/etc/cni/net.d/03", + }, + { + name: "crio_with_wrong_config_dir", + cmdLine: "/usr/bin/crio", + defaultConfigDir: "testdata/testCNI/crio.doesnt/exists/", + defaultConfigPath: "testdata/testCNI/crio.conf", + parseCNIFromConfigFunc: parseCNIConfigDirFromConfigCrio, + expected: "/etc/cni/net.d/", + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + // create ContainerRuntimeInfo + cr := &ContainerRuntimeInfo{ + properties: &containerRuntimeProperties{ + DefaultConfigDir: tt.defaultConfigDir, + DefaultConfigPath: tt.defaultConfigPath, + ParseCNIFromConfigFunc: tt.parseCNIFromConfigFunc, + }, + process: &ProcessDetails{ + CmdLine: strings.Split(tt.cmdLine, " "), + }, + rootDir: "", + } + + cniConfigDirFormFile := cr.getCNIConfigDirFromConfig(ctx) + + if !assert.Equal(t, tt.expected, cniConfigDirFormFile) { + t.Logf("%s has different output\n", tt.name) + } + }) + } +} + +func Test_getCNIConfigDirFromProcess(t *testing.T) { + uid_tests := []struct { + name string + cmdLine string + cniConfigDirArgName string + expected string + }{ + { + name: "containerd", + cmdLine: "/usr/bin/containerd", + cniConfigDirArgName: "", + expected: "", + }, + { + name: "crio_with_flag", + cmdLine: "/usr/bin/crio --cni-config-dir /etc/crio/cni/", + cniConfigDirArgName: "--cni-config-dir", + expected: "/etc/crio/cni/", + }, + { + name: "crio_without_flag", + cmdLine: "/usr/bin/crio", + cniConfigDirArgName: "--cni-config-dir", + expected: "", + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + // create ContainerRuntimeInfo + cr := &ContainerRuntimeInfo{ + properties: &containerRuntimeProperties{ + CNIConfigDirArgName: tt.cniConfigDirArgName, + }, + process: &ProcessDetails{ + CmdLine: strings.Split(tt.cmdLine, " "), + }, + rootDir: "", + } + cniConfig := cr.getCNIConfigDirFromProcess() + + if !assert.Equal(t, tt.expected, cniConfig) { + t.Logf("%s has different output\n", tt.name) + } + }) + } +} + +func Test_parseCNIPathsFromConfigContainerd(t *testing.T) { + uid_tests := []struct { + name string + path string + expectedRes string + wantErr bool + }{ + { + name: "fileexists_paramsexist", + path: "testdata/testCNI/containerd.toml", + expectedRes: "/etc/cni/net.mk", + wantErr: false, + }, + { + name: "file_not_exit", + path: "testdata/testCNI/bla.toml", + expectedRes: "", + wantErr: true, + }, + { + name: "fileexists_noparams", + path: "testdata/testCNI/containerd_noparams.toml", + expectedRes: "", + wantErr: false, + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + CNIConfigDir, err := parseCNIConfigDirFromConfigContainerd(tt.path) + + if err != nil { + if !tt.wantErr { + assert.NoError(t, err) + } + + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedRes, CNIConfigDir) + } + + }) + } + +} + +func Test_parseCNIPathsFromConfigCrio(t *testing.T) { + uid_tests := []struct { + name string + path string + expectedRes string + wantErr bool + }{ + { + name: "fileexists_paramsexist", + path: "testdata/testCNI/crio.conf", + expectedRes: "/etc/cni/net.d/", + wantErr: false, + }, + { + name: "file_not_exit", + path: "testdata/testCNI/bla.toml", + expectedRes: "", + wantErr: true, + }, + { + name: "fileexists_noparams", + path: "testdata/testCNI/crio_noparams.conf", + expectedRes: "", + wantErr: false, + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + CNIConfigDir, err := parseCNIConfigDirFromConfigCrio(tt.path) + + if err != nil { + if !tt.wantErr { + assert.NoError(t, err) + } + + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedRes, CNIConfigDir) + } + + }) + } + +} + +func Test_newContainerRuntime(t *testing.T) { + uid_tests := []struct { + name string + socket string + expectedError string + expected string + }{ + { + name: "containerd", + expectedError: "newContainerRuntime - Failed to locate process for CRIKind containerd", + expected: "containerd", + }, + { + name: "crio", + expectedError: "newContainerRuntime - Failed to locate process for CRIKind crio", + expected: "crio", + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + _, err := newContainerRuntime(tt.name) + if err != nil { + if !assert.EqualError(t, err, tt.expectedError) { + t.Log(err) + } + } + }) + } +} + +func Test_CNIConfigDirFromKubelet(t *testing.T) { + uid_tests := []struct { + name string + process string + pid int32 + expected string + }{ + { + name: "kubelet_kind", + process: "/usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --node-ip=172.18.0.2 --node-labels= --pod-infra-container-image=registry.k8s.io/pause:3.8 --provider-id=kind://docker/cis-test/cis-test-control-plane --fail-swap-on=false --cgroup-root=/kubelet", + pid: 15, + expected: "/etc/cni/", + }, + { + name: "kubelet_manual_installation", + process: "/usr/local/bin/kubelet --config=/var/lib/kubelet/kubelet-config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --image-pull-progress-deadline=2m --kubeconfig=/var/lib/kubelet/kubeconfig --network-plugin=cni --register-node=true --v=2", + pid: 15, + expected: "/etc/cni/", + }, + { + name: "kubelet_manual_installation_with_custom_runtime_endpoint", + process: "/usr/local/bin/kubelet --config=/var/lib/kubelet/kubelet-config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --image-pull-progress-deadline=2m --kubeconfig=/var/lib/kubelet/kubeconfig --network-plugin=cni", + pid: 15, + expected: "/etc/cni/", + }, + { + name: "kubelet_manual_installation_with_custom_cni_dir", + process: "/usr/local/bin/kubelet --config=/var/lib/kubelet/kubelet-config.yaml --container-runtime=remote --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --image-pull-progress-deadline=2m --kubeconfig=/var/lib/kubelet/kubeconfig --network-plugin=cni --cni-conf-dir=/var/lib/cni/", + pid: 15, + expected: "/var/lib/cni/", + }, + { + name: "kubelet_manual_installation_without_remote_and_endpoint", + process: "/usr/local/bin/kubelet --config=/var/lib/kubelet/kubelet-config.yaml --image-pull-progress-deadline=2m --kubeconfig=/var/lib/kubelet/kubeconfig --network-plugin=cni", + pid: 15, + expected: "/etc/cni/", + }, + { + name: "kubelet_manual_installation_with_remote", + process: "/usr/local/bin/kubelet --config=/var/lib/kubelet/kubelet-config.yaml --container-runtime=remote --image-pull-progress-deadline=2m --kubeconfig=/var/lib/kubelet/kubeconfig --network-plugin=cni", + pid: 15, + expected: "/etc/cni/", + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + // create ProcessDetails object to pass it to GetCNIConfigPath + proc := &ProcessDetails{ + CmdLine: strings.Split(tt.process, " "), + PID: tt.pid, + } + cniConfigPath := GetCNIConfigPath(ctx, proc) + + if !assert.Equal(t, tt.expected, cniConfigPath) { + t.Logf("%s has different output\n", tt.name) + } + }) + } +} + +func Test_getCNIConfigDir(t *testing.T) { + uid_tests := []struct { + name string + cmdLine string + defaultConfigDir string + defaultConfigPath string + cniConfigDirArgName string + parseCNIFromConfigFunc func(string) (string, error) + expected string + }{ + { + name: "containerd", + cmdLine: "/usr/bin/containerd", + defaultConfigDir: "testdata/testCNI/", + defaultConfigPath: "testdata/testCNI/containerd.toml", + parseCNIFromConfigFunc: parseCNIConfigDirFromConfigContainerd, + cniConfigDirArgName: "", + expected: "/etc/cni/net.mk", + }, + { + name: "crio", + cmdLine: "/usr/bin/crio", + defaultConfigDir: "testdata/testCNI/crio.d", + defaultConfigPath: "testdata/testCNI/crio.conf", + parseCNIFromConfigFunc: parseCNIConfigDirFromConfigCrio, + cniConfigDirArgName: "", + expected: "/etc/cni/net.d/03", + }, + { + name: "crio_cni_from_process", + cmdLine: "/usr/bin/crio --cni-config-dir=/var/lib/cni/", + defaultConfigDir: "", + defaultConfigPath: "", + parseCNIFromConfigFunc: parseCNIConfigDirFromConfigCrio, + cniConfigDirArgName: "--cni-config-dir", + expected: "/var/lib/cni/", + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + // create ContainerRuntimeInfo + cr := &ContainerRuntimeInfo{ + properties: &containerRuntimeProperties{ + DefaultConfigDir: tt.defaultConfigDir, + DefaultConfigPath: tt.defaultConfigPath, + ParseCNIFromConfigFunc: tt.parseCNIFromConfigFunc, + CNIConfigDirArgName: tt.cniConfigDirArgName, + }, + process: &ProcessDetails{ + CmdLine: strings.Split(tt.cmdLine, " "), + }, + rootDir: "", + } + + cniConfigDirFormFile := cr.getCNIConfigDir(ctx) + + if !assert.Equal(t, tt.expected, cniConfigDirFormFile) { + t.Logf("%s has different output\n", tt.name) + } + }) + } +} diff --git a/sensor/internal/utils/globals.go b/sensor/internal/utils/globals.go new file mode 100644 index 00000000..40bbd64f --- /dev/null +++ b/sensor/internal/utils/globals.go @@ -0,0 +1,29 @@ +package utils + +import ( + "net/http" + "sync" +) + +var ( + // Where the host sensor is expecting host fs to be mounted. + // Defined as var for testing purposes only + HostFileSystemDefaultLocation = "/host_fs" + + // global http.client instance to reduce object resource overuse. + httpClient *http.Client + + httpClientCreationlock = &sync.Mutex{} +) + +// GetHttpClient - instantiate http.client object +func GetHttpClient() *http.Client { + if httpClient == nil { + httpClientCreationlock.Lock() + defer httpClientCreationlock.Unlock() + if httpClient == nil { + httpClient = &http.Client{} + } + } + return httpClient +} diff --git a/sensor/internal/utils/osuserswrapper.go b/sensor/internal/utils/osuserswrapper.go new file mode 100644 index 00000000..09ff9900 --- /dev/null +++ b/sensor/internal/utils/osuserswrapper.go @@ -0,0 +1,142 @@ +package utils + +import ( + "io" + "os" + "os/user" + "strconv" + + _ "net" + _ "unsafe" +) + +// os/users package handles extracting information from users files (/etc/passwd, /etc/group) but limited to current user root only. +// Module utilizes unexported (private) functions (using go:linkname), expanding their use for custom root path. +// NOTE: code requires environment variable CGO_ENABLED = 0 + +//go:linkname readColonFile os/user.readColonFile +func readColonFile(r io.Reader, fn lineFunc, readCols int) (v any, err error) + +//go:linkname findUserId os/user.findUserId +func findUserId(uid string, r io.Reader) (*user.User, error) + +//go:linkname findGroupId os/user.findGroupId +func findGroupId(id string, r io.Reader) (*user.Group, error) + +// goLlinkname lineFunc os/user lineFunc +type lineFunc func(line []byte) (v any, err error) + +const userFile = "/etc/passwd" +const groupFile = "/etc/group" + +var ( + userGroupCache = map[string]userGroupCacheItem{} // map[rootDir]struct{users, groups} +) + +type userGroupCacheItem struct { + users map[string]string + groups map[string]string +} + +// getUserName checks if uid is cached, if not, it tries to find it in a users file {root}/etc/passwd. +func getUserName(uid int64, root string) (string, error) { + + // return from cache if exists + if users, ok := userGroupCache[root]; ok { + if username, ok := users.users[strconv.Itoa(int(uid))]; ok { + return username, nil + } + } + + // find username in a users file + username, err := lookupUsernameByUID(uid, root) + if err != nil { + return "", err + } + + // cache username + if _, ok := userGroupCache[root]; !ok { + userGroupCache[root] = userGroupCacheItem{ + users: map[string]string{}, + groups: map[string]string{}, + } + } + + userGroupCache[root].users[strconv.Itoa(int(uid))] = username + + return username, nil +} + +// getGroupName checks if gid is cached, if not, it tries to find it in a group file {root}/etc/group. +func getGroupName(gid int64, root string) (string, error) { + + // return from cache if exists + if users, ok := userGroupCache[root]; ok { + if groupname, ok := users.groups[strconv.Itoa(int(gid))]; ok { + return groupname, nil + } + } + + // find groupname in a group file + groupname, err := LookupGroupnameByGID(gid, root) + if err != nil { + return "", err + } + + // cache groupname + if _, ok := userGroupCache[root]; !ok { + userGroupCache[root] = userGroupCacheItem{ + users: map[string]string{}, + groups: map[string]string{}, + } + } + + userGroupCache[root].groups[strconv.Itoa(int(gid))] = groupname + + return groupname, nil +} + +// returns *Group object if gid was found in a group file {root}/etc/group, otherwise returns nil. +func lookupGroup(gid string, root string) (*user.Group, error) { + filePath := root + groupFile + f, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer f.Close() + return findGroupId(gid, f) +} + +// returns group name if gid was found in a group file {root}/etc/group, otherwise returns empty string. +func LookupGroupnameByGID(gid int64, root string) (string, error) { + groupData, err := lookupGroup(strconv.FormatInt(gid, 10), root) + + if err != nil { + return "", err + } + + return groupData.Name, nil + +} + +// returns *User object if uid was found in a users file {root}/etc/passwd, otherwise returns nil. +func lookupUser(uid string, root string) (*user.User, error) { + filePath := root + userFile + f, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer f.Close() + return findUserId(uid, f) +} + +// returns username if uid was found in a users file {root}/etc/passwd, otherwise returns empty string. +func lookupUsernameByUID(uid int64, root string) (string, error) { + userData, err := lookupUser(strconv.FormatInt(uid, 10), root) + + if err != nil { + return "", err + } + + return userData.Username, nil +} diff --git a/sensor/internal/utils/osuserswrapper_test.go b/sensor/internal/utils/osuserswrapper_test.go new file mode 100644 index 00000000..165c15be --- /dev/null +++ b/sensor/internal/utils/osuserswrapper_test.go @@ -0,0 +1,181 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// func is testing if the name return as wheel or root +func isValidaName(name string) bool { + return name == "root" || name == "wheel" || name == "daemon" +} + +func TestGetUserName(t *testing.T) { + userGroupCache = map[string]userGroupCacheItem{} + // regular + t.Run("regular", func(t *testing.T) { + name, _ := getUserName(0, "testdata") + if !isValidaName(name) { + t.Errorf("Wrong name '%s'", name) + } + assert.Contains(t, userGroupCache, "testdata") + assert.Contains(t, userGroupCache["testdata"].users, "0") + + groups := userGroupCache["testdata"].groups["0"] + + if len(groups) != 0 && !isValidaName(groups) { + t.Errorf("Wrong group '%s'", groups) + } + }) + + // cached + t.Run("cached", func(t *testing.T) { + userGroupCache["foo"] = userGroupCacheItem{ + users: map[string]string{"0": "bar"}, + groups: map[string]string{}, + } + name, _ := getUserName(0, "foo") + assert.Equal(t, "bar", name) + }) +} + +func TestGetGroupName(t *testing.T) { + userGroupCache = map[string]userGroupCacheItem{} + + // regular + t.Run("regular", func(t *testing.T) { + name, _ := getGroupName(0, "testdata") + if !isValidaName(name) { + t.Errorf("Wrong name '%s'", name) + } + + // assert.Equal(t, "root", name) + assert.Contains(t, userGroupCache, "testdata") + assert.Contains(t, userGroupCache["testdata"].groups, "0") + if !isValidaName(userGroupCache["testdata"].groups["0"]) { + t.Errorf("Wrong name '%s'", userGroupCache["testdata"].groups["0"]) + } + }) + + // cached + t.Run("cached", func(t *testing.T) { + userGroupCache["foo"] = userGroupCacheItem{ + users: map[string]string{}, + groups: map[string]string{"0": "bar"}, + } + name, _ := getGroupName(0, "foo") + assert.Equal(t, "bar", name) + }) +} + +func Test_LookupUsernameByUID(t *testing.T) { + uid_tests := []struct { + name string + root string + uid int64 + expectedRes string + wantErr bool + }{ + { + name: "testdata_uid_exists", + root: "testdata", + uid: 0, + wantErr: false, + }, + { + name: "testdata_uid_not_exists", + root: "testdata", + uid: 10, + wantErr: true, + }, + { + name: "testdata_file_not_exists", + root: "testdata/bla", + uid: 10, + wantErr: true, + }, + { + name: "root_uid_exists", + root: "/", + uid: 0, + wantErr: false, + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + username, err := lookupUsernameByUID(tt.uid, tt.root) + + if err != nil { + if !tt.wantErr { + assert.NoError(t, err) + } + + } else { + assert.NoError(t, err) + if !isValidaName(username) { + t.Errorf("Wrong name") + } + } + + }) + } +} + +func Test_LookupGroupByUID(t *testing.T) { + + uid_tests := []struct { + name string + root string + gid int64 + expectedRes string + wantErr bool + }{ + { + name: "testdata_uid_exists", + root: "testdata", + gid: 1, + expectedRes: "daemon", + wantErr: false, + }, + { + name: "testdata_uid_not_exists", + root: "testdata", + gid: 10, + wantErr: true, + }, + { + name: "testdata_file_not_exists", + root: "testdata/bla", + gid: 10, + wantErr: true, + }, + { + name: "root_uid_exists", + root: "/", + gid: 0, + wantErr: false, + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + groupname, err := LookupGroupnameByGID(tt.gid, tt.root) + + if err != nil { + if !tt.wantErr { + assert.NoError(t, err) + } + + } else { + assert.NoError(t, err) + if !isValidaName(groupname) { + t.Errorf("Wrong name '%s'", groupname) + } + } + + }) + } + +} diff --git a/sensor/internal/utils/process.go b/sensor/internal/utils/process.go new file mode 100644 index 00000000..3b826603 --- /dev/null +++ b/sensor/internal/utils/process.go @@ -0,0 +1,127 @@ +package utils + +import ( + "bytes" + "fmt" + "io" + "os" + "path" + "strconv" + "strings" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" +) + +const ( + procDirName = "/proc" +) + +type ProcessDetails struct { + CmdLine []string `json:"cmdline"` + PID int32 `json:"pid"` +} + +// LocateProcessByExecSuffix locates process with executable name ends with `processSuffix`. +// The first entry at `/proc` that matches the suffix is returned, other process are ignored. +// It returns a `ProcessDetails` object. +func LocateProcessByExecSuffix(processSuffix string) (*ProcessDetails, error) { + // TODO: consider taking the exec name from /proc/[pid]/exe instead of /proc/[pid]/cmdline + procDir, err := os.Open(procDirName) + if err != nil { + return nil, fmt.Errorf("failed to open processes dir: %v", err) + } + defer procDir.Close() + var pidDirs []string + for pidDirs, err = procDir.Readdirnames(100); err == nil; pidDirs, err = procDir.Readdirnames(100) { + for pidIdx := range pidDirs { + // since processes are about to die in the middle of the loop, we will ignore next errors + pid, err := strconv.ParseInt(pidDirs[pidIdx], 10, 0) + if err != nil { + continue + } + specificProcessCMD := path.Join(procDirName, pidDirs[pidIdx], "cmdline") + cmdLine, err := os.ReadFile(specificProcessCMD) + if err != nil { + continue + } + cmdLineSplitted := bytes.Split(cmdLine, []byte{00}) + + processNameFromCMD := cmdLineSplitted[0] + if len(processNameFromCMD) == 0 { + continue + } + // solve open shift kubelet not start with full path + if processNameFromCMD[0] != '/' && processNameFromCMD[0] != '[' { + processNameFromCMD = append([]byte{'/'}, processNameFromCMD...) + } + if bytes.HasSuffix(processNameFromCMD, []byte(processSuffix)) { + logger.L().Debug("process found", helpers.String("processSuffix", processSuffix), + helpers.Int("pid", int(pid))) + res := &ProcessDetails{PID: int32(pid), CmdLine: make([]string, 0, len(cmdLineSplitted))} + for splitIdx := range cmdLineSplitted { + res.CmdLine = append(res.CmdLine, string(cmdLineSplitted[splitIdx])) + } + return res, nil + } + } + } + if err != io.EOF { + return nil, fmt.Errorf("failed to read processes dir names: %v", err) + } + return nil, fmt.Errorf("no process with given suffix found") +} + +// GetArg returns argument value from the process cmdline, and an ok. +// If the argument does not exist, it returns an empty string and `false`. +// If the argument exists but has no value, it returns an empty string and `true`. +// TODO: support multiple options +func (p ProcessDetails) GetArg(argName string) (string, bool) { + for idx, arg := range p.CmdLine { + if !strings.HasPrefix(arg, argName) { + continue + } + + val := arg[len(argName):] + + if val != "" { + // Case `--foo=bar` + if strings.HasPrefix(val, "=") { + val = val[1:] + return val, true + } + + // argName != current arg + continue + } + + // Case `--foo bar` + next := idx + 1 + if next < len(p.CmdLine) { + val = p.CmdLine[next] + return val, true + } + + // Case `--foo` (flags without value) + return "", true + } + + return "", false +} + +// RawCmd returns the raw command used to start the process +func (p ProcessDetails) RawCmd() string { + return strings.Join(p.CmdLine, " ") +} + +// RootDir returns the root directory of a process. +// This is useful when dealing with processes that are running inside a container +func (p ProcessDetails) RootDir() string { + return fmt.Sprintf("/proc/%d/root", p.PID) +} + +// ContaineredPath returns path for the file that the process see. +// This is useful when dealing with processes that are running inside a container +func (p ProcessDetails) ContaineredPath(filePath string) string { + return path.Join(p.RootDir(), filePath) +} diff --git a/sensor/internal/utils/process_test.go b/sensor/internal/utils/process_test.go new file mode 100644 index 00000000..0da7277e --- /dev/null +++ b/sensor/internal/utils/process_test.go @@ -0,0 +1,76 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestProcessDetails_GetArg(t *testing.T) { + tests := []struct { + name string + arg string + wantVal string + p ProcessDetails + wantOK bool + }{ + { + name: "exist with =", + p: ProcessDetails{ + CmdLine: []string{"--foo=bar"}, + }, + arg: "--foo", + wantVal: "bar", + wantOK: true, + }, + { + name: "exist sapereted", + p: ProcessDetails{ + CmdLine: []string{"--foo", "bar"}, + }, + arg: "--foo", + wantVal: "bar", + wantOK: true, + }, + { + name: "exist no value", + p: ProcessDetails{ + CmdLine: []string{"--foo"}, + }, + arg: "--foo", + wantVal: "", + wantOK: true, + }, + { + name: "not exist", + p: ProcessDetails{ + CmdLine: []string{"--bar"}, + }, + arg: "--foo", + wantVal: "", + wantOK: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + val, ok := tt.p.GetArg(tt.arg) + if val != tt.wantVal { + t.Errorf("ProcessDetails.GetArg() val = %v, want %v", val, tt.wantVal) + } + if ok != tt.wantOK { + t.Errorf("ProcessDetails.GetArg() ok = %v, want %v", ok, tt.wantOK) + } + }) + } +} + +func TestProcessDetailsRawCmd(t *testing.T) { + p := ProcessDetails{CmdLine: []string{"/foo/bar baz", "--flag", "value", "-f", "-d", "--flag=value"}} + assert.Equal(t, p.RawCmd(), "/foo/bar baz --flag value -f -d --flag=value") +} + +func TestProcessDetailsContainerdPath(t *testing.T) { + p := ProcessDetails{PID: 1} + assert.Equal(t, p.ContaineredPath("/foo/bar"), "/proc/1/root/foo/bar") + assert.Equal(t, p.ContaineredPath("foo/bar"), "/proc/1/root/foo/bar") +} diff --git a/sensor/internal/utils/service.go b/sensor/internal/utils/service.go new file mode 100644 index 00000000..e4d81e0b --- /dev/null +++ b/sensor/internal/utils/service.go @@ -0,0 +1,111 @@ +package utils + +import ( + "context" + "errors" + "os" + "path" + + systemd_debus "github.com/coreos/go-systemd/v22/dbus" + "github.com/godbus/dbus/v5" + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" +) + +// This file contains utilities for the getting information about services + +const ( + // https://wiki.archlinux.org/title/Systemd + systemdPkgDir = "/usr/lib/systemd/system/" // units provided by installed packages + systemdAdminDir = "/etc/systemd/system/" // units installed by the system administrator + + // default service paths + kubeletSystemdServiceConfigDir = systemdAdminDir + "kubelet.service.d" +) + +var ( + ErrServicePathNotFound = errors.New("cannot locate service file path") +) + +func newSystemDbusConnection() (*dbus.Conn, error) { + systemBusPath := "unix:path=" + HostPath("/run/dbus/system_bus_socket") + d, err := dbus.Dial(systemBusPath) + if err != nil { + return d, err + } + err = d.Auth(nil) + if err != nil { + return d, err + } + err = d.Hello() + return d, err +} + +// GetKubeletServiceFiles all the service files associated with the kubelet service. +func GetKubeletServiceFiles(kubeletPid int) ([]string, error) { + + // First try to get the service files from systemd daemon + configDir, err := GetServiceFilesByPIDSystemd(kubeletPid) + if err != nil { + logger.L().Debug("failed to get service files by PID from systemd", helpers.Error(err)) + } + + // Fallback to the default location + // if can't find the service files path dynamically + if configDir == "" { + configDir = kubeletSystemdServiceConfigDir + } + + files, err := os.ReadDir(HostPath(configDir)) + if err != nil { + return nil, err + } + + ret := []string{} + for _, f := range files { + ret = append(ret, path.Join(configDir, f.Name())) + } + + return ret, nil +} + +// GetServiceFilesByPIDSystemd returns the serivce config directory for a given process id. +func GetServiceFilesByPIDSystemd(pid int) (string, error) { + conn, err := systemd_debus.NewConnection(newSystemDbusConnection) + if err != nil { + return "", err + } + defer conn.Close() + + unitName, err := conn.GetUnitNameByPID(context.Background(), uint32(pid)) + if err != nil { + return "", err + } + + // Get the service file path + // p, err := conn.GetUnitPropertyContext(context.Background(), unitName, "FragmentPath") + // if err != nil { + // return "", nil, err + // } + // servicePath := p.Value.String() + // servicePath = servicePath[1 : len(servicePath)-1] // remove quotes + + // Find the service override files path (if any) + unitDirName := unitName + ".d" + configDir := getExistsPath(HostFileSystemDefaultLocation, + path.Join(systemdPkgDir, unitDirName), + path.Join(systemdAdminDir, unitDirName), + ) + + return configDir, nil +} + +// getExistsPath return the first exists path from a list of `paths`, prefixing it with `rootDir`. +func getExistsPath(rootDir string, paths ...string) string { + for _, p := range paths { + if _, err := os.Stat(path.Join(rootDir, p)); err == nil { + return p + } + } + return "" +} diff --git a/sensor/internal/utils/testdata/etc/group b/sensor/internal/utils/testdata/etc/group new file mode 100644 index 00000000..4526b64b --- /dev/null +++ b/sensor/internal/utils/testdata/etc/group @@ -0,0 +1,4 @@ +root:x:0: +daemon:x:1: +bin:x:2: +sys:x:3: \ No newline at end of file diff --git a/sensor/internal/utils/testdata/etc/passwd b/sensor/internal/utils/testdata/etc/passwd new file mode 100644 index 00000000..ae04e7da --- /dev/null +++ b/sensor/internal/utils/testdata/etc/passwd @@ -0,0 +1,5 @@ +root:x:0:0:root:/root:/bin/bash +sys:x:3:3:sys:/dev:/usr/sbin/nologin +sync:x:4:65534:sync:/bin:/bin/sync +games:x:5:60:games:/usr/games:/usr/sbin/nologin +man:x:6:12:man:/var/cache/man:/usr/sbin/nologin \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/containerd.toml b/sensor/internal/utils/testdata/testCNI/containerd.toml new file mode 100644 index 00000000..0861b693 --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/containerd.toml @@ -0,0 +1,71 @@ +version = 2 +root = "/var/lib/containerd" +state = "/run/containerd" +oom_score = 0 +# imports + +[grpc] + address = "/run/containerd/containerd.sock" + uid = 0 + gid = 0 + max_recv_message_size = 16777216 + max_send_message_size = 16777216 + +[debug] + address = "" + uid = 0 + gid = 0 + level = "" + +[metrics] + address = "" + grpc_histogram = false + +[cgroup] + path = "" + +[plugins] + + [plugins."io.containerd.monitor.v1.cgroups"] + no_prometheus = false + [plugins."io.containerd.grpc.v1.cri"] + stream_server_address = "" + stream_server_port = "10010" + enable_selinux = false + sandbox_image = "k8s.gcr.io/pause:3.6" + stats_collect_period = 10 + enable_tls_streaming = false + max_container_log_line_size = 16384 + restrict_oom_score_adj = false + + [plugins."io.containerd.grpc.v1.cri".containerd] + discard_unpacked_layers = true + snapshotter = "overlayfs" + [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime] + runtime_type = "io.containerd.runc.v2" + [plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime] + runtime_type = "" + runtime_engine = "" + runtime_root = "" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] + runtime_type = "io.containerd.runc.v2" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] + SystemdCgroup = false + + [plugins."io.containerd.grpc.v1.cri".cni] + bin_dir = "/opt/cni/bin" + conf_dir = "/etc/cni/net.mk" + conf_template = "" + [plugins."io.containerd.grpc.v1.cri".registry] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] + endpoint = ["https://registry-1.docker.io"] + [plugins."io.containerd.service.v1.diff-service"] + default = ["walking"] + [plugins."io.containerd.gc.v1.scheduler"] + pause_threshold = 0.02 + deletion_threshold = 0 + mutation_threshold = 100 + schedule_delay = "0s" + startup_delay = "100ms" \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/containerd_noparams.toml b/sensor/internal/utils/testdata/testCNI/containerd_noparams.toml new file mode 100644 index 00000000..7b9152f1 --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/containerd_noparams.toml @@ -0,0 +1,71 @@ +version = 2 +root = "/var/lib/containerd" +state = "/run/containerd" +oom_score = 0 +# imports + +[grpc] + address = "/run/containerd/containerd.sock" + uid = 0 + gid = 0 + max_recv_message_size = 16777216 + max_send_message_size = 16777216 + +[debug] + address = "" + uid = 0 + gid = 0 + level = "" + +[metrics] + address = "" + grpc_histogram = false + +[cgroup] + path = "" + +[plugins] + + [plugins."io.containerd.monitor.v1.cgroups"] + no_prometheus = false + [plugins."io.containerd.grpc.v1.cri"] + stream_server_address = "" + stream_server_port = "10010" + enable_selinux = false + sandbox_image = "k8s.gcr.io/pause:3.6" + stats_collect_period = 10 + enable_tls_streaming = false + max_container_log_line_size = 16384 + restrict_oom_score_adj = false + + [plugins."io.containerd.grpc.v1.cri".containerd] + discard_unpacked_layers = true + snapshotter = "overlayfs" + [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime] + runtime_type = "io.containerd.runc.v2" + [plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime] + runtime_type = "" + runtime_engine = "" + runtime_root = "" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] + runtime_type = "io.containerd.runc.v2" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] + SystemdCgroup = false + + [plugins."io.containerd.grpc.v1.cri".cni] + bin_dir = "" + conf_dir = "" + conf_template = "" + [plugins."io.containerd.grpc.v1.cri".registry] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] + endpoint = ["https://registry-1.docker.io"] + [plugins."io.containerd.service.v1.diff-service"] + default = ["walking"] + [plugins."io.containerd.gc.v1.scheduler"] + pause_threshold = 0.02 + deletion_threshold = 0 + mutation_threshold = 100 + schedule_delay = "0s" + startup_delay = "100ms" \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/crio.conf b/sensor/internal/utils/testdata/testCNI/crio.conf new file mode 100644 index 00000000..125b332a --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/crio.conf @@ -0,0 +1,570 @@ +# The CRI-O configuration file specifies all of the available configuration +# options and command-line flags for the crio(8) OCI Kubernetes Container Runtime +# daemon, but in a TOML format that can be more easily modified and versioned. +# +# Please refer to crio.conf(5) for details of all configuration options. + +# CRI-O supports partial configuration reload during runtime, which can be +# done by sending SIGHUP to the running process. Currently supported options +# are explicitly mentioned with: 'This option supports live configuration +# reload'. + +# CRI-O reads its storage defaults from the containers-storage.conf(5) file +# located at /etc/containers/storage.conf. Modify this storage configuration if +# you want to change the system's defaults. If you want to modify storage just +# for CRI-O, you can change the storage configuration options here. +[crio] + +# Path to the "root directory". CRI-O stores all of its data, including +# containers images, in this directory. +# root = "/home/abuild/.local/share/containers/storage" + +# Path to the "run directory". CRI-O stores all of its state in this directory. +# runroot = "/tmp/containers-user-399/containers" + +# Storage driver used to manage the storage of images and containers. Please +# refer to containers-storage.conf(5) to see all available storage drivers. +# storage_driver = "vfs" + +# List to pass options to the storage driver. Please refer to +# containers-storage.conf(5) to see all available storage options. +# storage_option = [ +# ] + +# The default log directory where all logs will go unless directly specified by +# the kubelet. The log directory specified must be an absolute directory. +# log_dir = "/var/log/crio/pods" + +# Location for CRI-O to lay down the temporary version file. +# It is used to check if crio wipe should wipe containers, which should +# always happen on a node reboot +# version_file = "/var/run/crio/version" + +# Location for CRI-O to lay down the persistent version file. +# It is used to check if crio wipe should wipe images, which should +# only happen when CRI-O has been upgraded +# version_file_persist = "/var/lib/crio/version" + +# InternalWipe is whether CRI-O should wipe containers and images after a reboot when the server starts. +# If set to false, one must use the external command 'crio wipe' to wipe the containers and images in these situations. +# internal_wipe = true + +# Location for CRI-O to lay down the clean shutdown file. +# It is used to check whether crio had time to sync before shutting down. +# If not found, crio wipe will clear the storage directory. +# clean_shutdown_file = "/var/lib/crio/clean.shutdown" + +# The crio.api table contains settings for the kubelet/gRPC interface. +[crio.api] + +# Path to AF_LOCAL socket on which CRI-O will listen. +# listen = "/var/run/crio/crio.sock" + +# IP address on which the stream server will listen. +# stream_address = "127.0.0.1" + +# The port on which the stream server will listen. If the port is set to "0", then +# CRI-O will allocate a random free port number. +# stream_port = "0" + +# Enable encrypted TLS transport of the stream server. +# stream_enable_tls = false + +# Length of time until open streams terminate due to lack of activity +# stream_idle_timeout = "" + +# Path to the x509 certificate file used to serve the encrypted stream. This +# file can change, and CRI-O will automatically pick up the changes within 5 +# minutes. +# stream_tls_cert = "" + +# Path to the key file used to serve the encrypted stream. This file can +# change and CRI-O will automatically pick up the changes within 5 minutes. +# stream_tls_key = "" + +# Path to the x509 CA(s) file used to verify and authenticate client +# communication with the encrypted stream. This file can change and CRI-O will +# automatically pick up the changes within 5 minutes. +# stream_tls_ca = "" + +# Maximum grpc send message size in bytes. If not set or <=0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_send_msg_size = 83886080 + +# Maximum grpc receive message size. If not set or <= 0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_recv_msg_size = 83886080 + +# The crio.runtime table contains settings pertaining to the OCI runtime used +# and options for how to set up and manage the OCI runtime. +[crio.runtime] + +# A list of ulimits to be set in containers by default, specified as +# "=:", for example: +# "nofile=1024:2048" +# If nothing is set here, settings will be inherited from the CRI-O daemon +# default_ulimits = [ +# ] + +# If true, the runtime will not use pivot_root, but instead use MS_MOVE. +# no_pivot = false + +# decryption_keys_path is the path where the keys required for +# image decryption are stored. This option supports live configuration reload. +# decryption_keys_path = "/etc/crio/keys/" + +# Path to the conmon binary, used for monitoring the OCI runtime. +# Will be searched for using $PATH if empty. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon = "" + +# Cgroup setting for conmon +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorCgroup. +# conmon_cgroup = "" + +# Environment variable list for the conmon process, used for passing necessary +# environment variables to conmon or the runtime. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon_env = [ +# ] + +# Additional environment variables to set for all the +# containers. These are overridden if set in the +# container image spec or in the container runtime configuration. +# default_env = [ +# ] + +# If true, SELinux will be used for pod separation on the host. +# selinux = false + +# Path to the seccomp.json profile which is used as the default seccomp profile +# for the runtime. If not specified, then the internal default seccomp profile +# will be used. This option supports live configuration reload. +# seccomp_profile = "" + +# Changes the meaning of an empty seccomp profile. By default +# (and according to CRI spec), an empty profile means unconfined. +# This option tells CRI-O to treat an empty profile as the default profile, +# which might increase security. +# seccomp_use_default_when_empty = true + +# Used to change the name of the default AppArmor profile of CRI-O. The default +# profile name is "crio-default". This profile only takes effect if the user +# does not specify a profile via the Kubernetes Pod's metadata annotation. If +# the profile is set to "unconfined", then this equals to disabling AppArmor. +# This option supports live configuration reload. +# apparmor_profile = "crio-default" + +# Path to the blockio class configuration file for configuring +# the cgroup blockio controller. +# blockio_config_file = "" + +# Used to change irqbalance service config file path which is used for configuring +# irqbalance daemon. +# irqbalance_config_file = "/etc/sysconfig/irqbalance" + +# Path to the RDT configuration file for configuring the resctrl pseudo-filesystem. +# This option supports live configuration reload. +# rdt_config_file = "" + +# Cgroup management implementation used for the runtime. +# cgroup_manager = "systemd" + +# Specify whether the image pull must be performed in a separate cgroup. +# separate_pull_cgroup = "" + +# List of default capabilities for containers. If it is empty or commented out, +# only the capabilities defined in the containers json file by the user/kube +# will be added. +# default_capabilities = [ +# "CHOWN", +# "DAC_OVERRIDE", +# "FSETID", +# "FOWNER", +# "SETGID", +# "SETUID", +# "SETPCAP", +# "NET_BIND_SERVICE", +# "KILL", +# ] + +# List of default sysctls. If it is empty or commented out, only the sysctls +# defined in the container json file by the user/kube will be added. +# default_sysctls = [ +# ] + +# List of devices on the host that a +# user can specify with the "io.kubernetes.cri-o.Devices" allowed annotation. +# allowed_devices = [ +# "/dev/fuse", +# ] + +# List of additional devices. specified as +# "::", for example: "--device=/dev/sdc:/dev/xvdc:rwm". +# If it is empty or commented out, only the devices +# defined in the container json file by the user/kube will be added. +# additional_devices = [ +# ] + +# List of directories to scan for CDI Spec files. +# cdi_spec_dirs = [ +# "/etc/cdi", +# "/var/run/cdi", +# ] + +# Change the default behavior of setting container devices uid/gid from CRI's +# SecurityContext (RunAsUser/RunAsGroup) instead of taking host's uid/gid. +# Defaults to false. +# device_ownership_from_security_context = false + +# Path to OCI hooks directories for automatically executed hooks. If one of the +# directories does not exist, then CRI-O will automatically skip them. +# hooks_dir = [ +# "/usr/share/containers/oci/hooks.d", +# ] + +# Path to the file specifying the defaults mounts for each container. The +# format of the config is /SRC:/DST, one mount per line. Notice that CRI-O reads +# its default mounts from the following two files: +# +# 1) /etc/containers/mounts.conf (i.e., default_mounts_file): This is the +# override file, where users can either add in their own default mounts, or +# override the default mounts shipped with the package. +# +# 2) /usr/share/containers/mounts.conf: This is the default file read for +# mounts. If you want CRI-O to read from a different, specific mounts file, +# you can change the default_mounts_file. Note, if this is done, CRI-O will +# only add mounts it finds in this file. +# +# default_mounts_file = "" + +# Maximum number of processes allowed in a container. +# This option is deprecated. The Kubelet flag '--pod-pids-limit' should be used instead. +# pids_limit = 0 + +# Maximum sized allowed for the container log file. Negative numbers indicate +# that no size limit is imposed. If it is positive, it must be >= 8192 to +# match/exceed conmon's read buffer. The file is truncated and re-opened so the +# limit is never exceeded. This option is deprecated. The Kubelet flag '--container-log-max-size' should be used instead. +# log_size_max = -1 + +# Whether container output should be logged to journald in addition to the kuberentes log file +# log_to_journald = false + +# Path to directory in which container exit files are written to by conmon. +# container_exits_dir = "/var/run/crio/exits" + +# Path to directory for container attach sockets. +# container_attach_socket_dir = "/var/run/crio" + +# The prefix to use for the source of the bind mounts. +# bind_mount_prefix = "" + +# If set to true, all containers will run in read-only mode. +# read_only = false + +# Changes the verbosity of the logs based on the level it is set to. Options +# are fatal, panic, error, warn, info, debug and trace. This option supports +# live configuration reload. +# log_level = "info" + +# Filter the log messages by the provided regular expression. +# This option supports live configuration reload. +# log_filter = "" + +# The UID mappings for the user namespace of each container. A range is +# specified in the form containerUID:HostUID:Size. Multiple ranges must be +# separated by comma. +# uid_mappings = "" + +# The GID mappings for the user namespace of each container. A range is +# specified in the form containerGID:HostGID:Size. Multiple ranges must be +# separated by comma. +# gid_mappings = "" + +# If set, CRI-O will reject any attempt to map host UIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_uid = -1 + +# If set, CRI-O will reject any attempt to map host GIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_gid = -1 + +# The minimal amount of time in seconds to wait before issuing a timeout +# regarding the proper termination of the container. The lowest possible +# value is 30s, whereas lower values are not considered by CRI-O. +# ctr_stop_timeout = 30 + +# drop_infra_ctr determines whether CRI-O drops the infra container +# when a pod does not have a private PID namespace, and does not use +# a kernel separating runtime (like kata). +# It requires manage_ns_lifecycle to be true. +# drop_infra_ctr = true + +# infra_ctr_cpuset determines what CPUs will be used to run infra containers. +# You can use linux CPU list format to specify desired CPUs. +# To get better isolation for guaranteed pods, set this parameter to be equal to kubelet reserved-cpus. +# infra_ctr_cpuset = "" + +# The directory where the state of the managed namespaces gets tracked. +# Only used when manage_ns_lifecycle is true. +# namespaces_dir = "/var/run" + +# pinns_path is the path to find the pinns binary, which is needed to manage namespace lifecycle +# pinns_path = "" + +# default_runtime is the _name_ of the OCI runtime to be used as the default. +# The name is matched against the runtimes map below. If this value is changed, +# the corresponding existing entry from the runtimes map below will be ignored. +# default_runtime = "runc" + +# A list of paths that, when absent from the host, +# will cause a container creation to fail (as opposed to the current behavior being created as a directory). +# This option is to protect from source locations whose existence as a directory could jepordize the health of the node, and whose +# creation as a file is not desired either. +# An example is /etc/hostname, which will cause failures on reboot if it's created as a directory, but often doesn't exist because +# the hostname is being managed dynamically. +# absent_mount_sources_to_reject = [ +# ] + +# The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. +# The runtime to use is picked based on the runtime handler provided by the CRI. +# If no runtime handler is provided, the runtime will be picked based on the level +# of trust of the workload. Each entry in the table should follow the format: +# +#[crio.runtime.runtimes.runtime-handler] +# runtime_path = "/path/to/the/executable" +# runtime_type = "oci" +# runtime_root = "/path/to/the/root" +# privileged_without_host_devices = false +# allowed_annotations = [] +# Where: +# - runtime-handler: name used to identify the runtime +# - runtime_path (optional, string): absolute path to the runtime executable in +# the host filesystem. If omitted, the runtime-handler identifier should match +# the runtime executable name, and the runtime executable should be placed +# in $PATH. +# - runtime_type (optional, string): type of runtime, one of: "oci", "vm". If +# omitted, an "oci" runtime is assumed. +# - runtime_root (optional, string): root directory for storage of containers +# state. +# - runtime_config_path (optional, string): the path for the runtime configuration +# file. This can only be used with when using the VM runtime_type. +# - privileged_without_host_devices (optional, bool): an option for restricting +# host devices from being passed to privileged containers. +# - allowed_annotations (optional, array of strings): an option for specifying +# a list of experimental annotations that this runtime handler is allowed to process. +# The currently recognized values are: +# "io.kubernetes.cri-o.userns-mode" for configuring a user namespace for the pod. +# "io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw" for mounting cgroups writably when set to "true". +# "io.kubernetes.cri-o.Devices" for configuring devices for the pod. +# "io.kubernetes.cri-o.ShmSize" for configuring the size of /dev/shm. +# "io.kubernetes.cri-o.UnifiedCgroup.$CTR_NAME" for configuring the cgroup v2 unified block for a container. +# "io.containers.trace-syscall" for tracing syscalls via the OCI seccomp BPF hook. +# "io.kubernetes.cri.rdt-class" for setting the RDT class of a container +# - monitor_exec_cgroup (optional, string): if set to "container", indicates exec probes +# should be moved to the container's cgroup + + +# [crio.runtime.runtimes.runc] +# runtime_path = "" +# runtime_type = "oci" +# runtime_root = "/run/runc" +# runtime_config_path = "" + +# +# allowed_annotations = [ +# "io.containers.trace-syscall", +# ] +# +# monitor_path = "" +# +# monitor_env = [ +# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +# ] +# +# monitor_cgroup = "system.slice" +# monitor_exec_cgroup = "" +# + +# crun is a fast and lightweight fully featured OCI runtime and C library for +# running containers +#[crio.runtime.runtimes.crun] + +# Kata Containers is an OCI runtime, where containers are run inside lightweight +# VMs. Kata provides additional isolation towards the host, minimizing the host attack +# surface and mitigating the consequences of containers breakout. + +# Kata Containers with the default configured VMM +#[crio.runtime.runtimes.kata-runtime] + +# Kata Containers with the QEMU VMM +#[crio.runtime.runtimes.kata-qemu] + +# Kata Containers with the Firecracker VMM +#[crio.runtime.runtimes.kata-fc] + +# The workloads table defines ways to customize containers with different resources +# that work based on annotations, rather than the CRI. +# Note, the behavior of this table is EXPERIMENTAL and may change at any time. +# Each workload, has a name, activation_annotation, annotation_prefix and set of resources it supports mutating. +# The currently supported resources are "cpu" (to configure the cpu shares) and "cpuset" to configure the cpuset. +# Each resource can have a default value specified, or be empty. +# For a container to opt-into this workload, the pod should be configured with the annotation $activation_annotation (key only, value is ignored). +# To customize per-container, an annotation of the form $annotation_prefix.$resource/$ctrName = "value" can be specified +# signifying for that resource type to override the default value. +# If the annotation_prefix is not present, every container in the pod will be given the default values. +# Example: +# [crio.runtime.workloads.workload-type] +# activation_annotation = "io.crio/workload" +# annotation_prefix = "io.crio.workload-type" +# [crio.runtime.workloads.workload-type.resources] +# cpuset = 0 +# cpushares = "0-1" +# Where: +# The workload name is workload-type. +# To specify, the pod must have the "io.crio.workload" annotation (this is a precise string match). +# This workload supports setting cpuset and cpu resources. +# annotation_prefix is used to customize the different resources. +# To configure the cpu shares a container gets in the example above, the pod would have to have the following annotation: +# "io.crio.workload-type/$container_name = {"cpushares": "value"}" +# + +# The crio.image table contains settings pertaining to the management of OCI images. +# +# CRI-O reads its configured registries defaults from the system wide +# containers-registries.conf(5) located in /etc/containers/registries.conf. If +# you want to modify just CRI-O, you can change the registries configuration in +# this file. Otherwise, leave insecure_registries and registries commented out to +# use the system's defaults from /etc/containers/registries.conf. +[crio.image] + +# Default transport for pulling images from a remote container storage. +# default_transport = "docker://" + +# The path to a file containing credentials necessary for pulling images from +# secure registries. The file is similar to that of /var/lib/kubelet/config.json +# global_auth_file = "" + +# The image used to instantiate infra containers. +# This option supports live configuration reload. +# pause_image = "registry.k8s.io/pause:3.6" + +# The path to a file containing credentials specific for pulling the pause_image from +# above. The file is similar to that of /var/lib/kubelet/config.json +# This option supports live configuration reload. +# pause_image_auth_file = "" + +# The command to run to have a container stay in the paused state. +# When explicitly set to "", it will fallback to the entrypoint and command +# specified in the pause image. When commented out, it will fallback to the +# default: "/pause". This option supports live configuration reload. +# pause_command = "/pause" + +# Path to the file which decides what sort of policy we use when deciding +# whether or not to trust an image that we've pulled. It is not recommended that +# this option be used, as the default behavior of using the system-wide default +# policy (i.e., /etc/containers/policy.json) is most often preferred. Please +# refer to containers-policy.json(5) for more details. +# signature_policy = "" + +# List of registries to skip TLS verification for pulling images. Please +# consider configuring the registries via /etc/containers/registries.conf before +# changing them here. +# insecure_registries = [ +# ] + +# Controls how image volumes are handled. The valid values are mkdir, bind and +# ignore; the latter will ignore volumes entirely. +# image_volumes = "mkdir" + +# Temporary directory to use for storing big files +# big_files_temporary_dir = "" + +# The crio.network table containers settings pertaining to the management of +# CNI plugins. +[crio.network] + +# The default CNI network name to be selected. If not set or "", then +# CRI-O will pick-up the first one found in network_dir. +# cni_default_network = "" + +# Path to the directory where CNI configuration files are located. +network_dir = "/etc/cni/net.d/" + +# Paths to directories where CNI plugin binaries are located. +plugin_dirs = [ + "/opt/cni/bin/", "/rr/ff" +] + +# A necessary configuration for Prometheus based metrics retrieval +[crio.metrics] + +# Globally enable or disable metrics support. +# enable_metrics = false + +# Specify enabled metrics collectors. +# Per default all metrics are enabled. +# It is possible, to prefix the metrics with "container_runtime_" and "crio_". +# For example, the metrics collector "operations" would be treated in the same +# way as "crio_operations" and "container_runtime_crio_operations". +# metrics_collectors = [ +# "operations", +# "operations_latency_microseconds_total", +# "operations_latency_microseconds", +# "operations_errors", +# "image_pulls_by_digest", +# "image_pulls_by_name", +# "image_pulls_by_name_skipped", +# "image_pulls_failures", +# "image_pulls_successes", +# "image_pulls_layer_size", +# "image_layer_reuse", +# "containers_oom_total", +# "containers_oom", +# "processes_defunct", +# "operations_total", +# "operations_latency_seconds", +# "operations_latency_seconds_total", +# "operations_errors_total", +# "image_pulls_bytes_total", +# "image_pulls_skipped_bytes_total", +# "image_pulls_failure_total", +# "image_pulls_success_total", +# "image_layer_reuse_total", +# "containers_oom_count_total", +# ] +# The port on which the metrics server will listen. +# metrics_port = 9090 + +# Local socket path to bind the metrics server to +# metrics_socket = "" + +# The certificate for the secure metrics server. +# If the certificate is not available on disk, then CRI-O will generate a +# self-signed one. CRI-O also watches for changes of this path and reloads the +# certificate on any modification event. +# metrics_cert = "" + +# The certificate key for the secure metrics server. +# Behaves in the same way as the metrics_cert. +# metrics_key = "" + +# A necessary configuration for OpenTelemetry trace data exporting +[crio.tracing] + +# Globally enable or disable exporting OpenTelemetry traces. +# enable_tracing = false + +# Address on which the gRPC trace collector listens on. +# tracing_endpoint = "0.0.0.0:4317" + +# Number of samples to collect per million spans. +# tracing_sampling_rate_per_million = 0 + +# Necessary information pertaining to container and pod stats reporting. +[crio.stats] + +# The number of seconds between collecting pod and container stats. +# If set to 0, the stats are collected on-demand instead. +# stats_collection_period = 0 \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/crio.d/01_crio.conf b/sensor/internal/utils/testdata/testCNI/crio.d/01_crio.conf new file mode 100644 index 00000000..2ad1750b --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/crio.d/01_crio.conf @@ -0,0 +1,570 @@ +# The CRI-O configuration file specifies all of the available configuration +# options and command-line flags for the crio(8) OCI Kubernetes Container Runtime +# daemon, but in a TOML format that can be more easily modified and versioned. +# +# Please refer to crio.conf(5) for details of all configuration options. + +# CRI-O supports partial configuration reload during runtime, which can be +# done by sending SIGHUP to the running process. Currently supported options +# are explicitly mentioned with: 'This option supports live configuration +# reload'. + +# CRI-O reads its storage defaults from the containers-storage.conf(5) file +# located at /etc/containers/storage.conf. Modify this storage configuration if +# you want to change the system's defaults. If you want to modify storage just +# for CRI-O, you can change the storage configuration options here. +[crio] + +# Path to the "root directory". CRI-O stores all of its data, including +# containers images, in this directory. +# root = "/home/abuild/.local/share/containers/storage" + +# Path to the "run directory". CRI-O stores all of its state in this directory. +# runroot = "/tmp/containers-user-399/containers" + +# Storage driver used to manage the storage of images and containers. Please +# refer to containers-storage.conf(5) to see all available storage drivers. +# storage_driver = "vfs" + +# List to pass options to the storage driver. Please refer to +# containers-storage.conf(5) to see all available storage options. +# storage_option = [ +# ] + +# The default log directory where all logs will go unless directly specified by +# the kubelet. The log directory specified must be an absolute directory. +# log_dir = "/var/log/crio/pods" + +# Location for CRI-O to lay down the temporary version file. +# It is used to check if crio wipe should wipe containers, which should +# always happen on a node reboot +# version_file = "/var/run/crio/version" + +# Location for CRI-O to lay down the persistent version file. +# It is used to check if crio wipe should wipe images, which should +# only happen when CRI-O has been upgraded +# version_file_persist = "/var/lib/crio/version" + +# InternalWipe is whether CRI-O should wipe containers and images after a reboot when the server starts. +# If set to false, one must use the external command 'crio wipe' to wipe the containers and images in these situations. +# internal_wipe = true + +# Location for CRI-O to lay down the clean shutdown file. +# It is used to check whether crio had time to sync before shutting down. +# If not found, crio wipe will clear the storage directory. +# clean_shutdown_file = "/var/lib/crio/clean.shutdown" + +# The crio.api table contains settings for the kubelet/gRPC interface. +[crio.api] + +# Path to AF_LOCAL socket on which CRI-O will listen. +# listen = "/var/run/crio/crio.sock" + +# IP address on which the stream server will listen. +# stream_address = "127.0.0.1" + +# The port on which the stream server will listen. If the port is set to "0", then +# CRI-O will allocate a random free port number. +# stream_port = "0" + +# Enable encrypted TLS transport of the stream server. +# stream_enable_tls = false + +# Length of time until open streams terminate due to lack of activity +# stream_idle_timeout = "" + +# Path to the x509 certificate file used to serve the encrypted stream. This +# file can change, and CRI-O will automatically pick up the changes within 5 +# minutes. +# stream_tls_cert = "" + +# Path to the key file used to serve the encrypted stream. This file can +# change and CRI-O will automatically pick up the changes within 5 minutes. +# stream_tls_key = "" + +# Path to the x509 CA(s) file used to verify and authenticate client +# communication with the encrypted stream. This file can change and CRI-O will +# automatically pick up the changes within 5 minutes. +# stream_tls_ca = "" + +# Maximum grpc send message size in bytes. If not set or <=0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_send_msg_size = 83886080 + +# Maximum grpc receive message size. If not set or <= 0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_recv_msg_size = 83886080 + +# The crio.runtime table contains settings pertaining to the OCI runtime used +# and options for how to set up and manage the OCI runtime. +[crio.runtime] + +# A list of ulimits to be set in containers by default, specified as +# "=:", for example: +# "nofile=1024:2048" +# If nothing is set here, settings will be inherited from the CRI-O daemon +# default_ulimits = [ +# ] + +# If true, the runtime will not use pivot_root, but instead use MS_MOVE. +# no_pivot = false + +# decryption_keys_path is the path where the keys required for +# image decryption are stored. This option supports live configuration reload. +# decryption_keys_path = "/etc/crio/keys/" + +# Path to the conmon binary, used for monitoring the OCI runtime. +# Will be searched for using $PATH if empty. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon = "" + +# Cgroup setting for conmon +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorCgroup. +# conmon_cgroup = "" + +# Environment variable list for the conmon process, used for passing necessary +# environment variables to conmon or the runtime. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon_env = [ +# ] + +# Additional environment variables to set for all the +# containers. These are overridden if set in the +# container image spec or in the container runtime configuration. +# default_env = [ +# ] + +# If true, SELinux will be used for pod separation on the host. +# selinux = false + +# Path to the seccomp.json profile which is used as the default seccomp profile +# for the runtime. If not specified, then the internal default seccomp profile +# will be used. This option supports live configuration reload. +# seccomp_profile = "" + +# Changes the meaning of an empty seccomp profile. By default +# (and according to CRI spec), an empty profile means unconfined. +# This option tells CRI-O to treat an empty profile as the default profile, +# which might increase security. +# seccomp_use_default_when_empty = true + +# Used to change the name of the default AppArmor profile of CRI-O. The default +# profile name is "crio-default". This profile only takes effect if the user +# does not specify a profile via the Kubernetes Pod's metadata annotation. If +# the profile is set to "unconfined", then this equals to disabling AppArmor. +# This option supports live configuration reload. +# apparmor_profile = "crio-default" + +# Path to the blockio class configuration file for configuring +# the cgroup blockio controller. +# blockio_config_file = "" + +# Used to change irqbalance service config file path which is used for configuring +# irqbalance daemon. +# irqbalance_config_file = "/etc/sysconfig/irqbalance" + +# Path to the RDT configuration file for configuring the resctrl pseudo-filesystem. +# This option supports live configuration reload. +# rdt_config_file = "" + +# Cgroup management implementation used for the runtime. +# cgroup_manager = "systemd" + +# Specify whether the image pull must be performed in a separate cgroup. +# separate_pull_cgroup = "" + +# List of default capabilities for containers. If it is empty or commented out, +# only the capabilities defined in the containers json file by the user/kube +# will be added. +# default_capabilities = [ +# "CHOWN", +# "DAC_OVERRIDE", +# "FSETID", +# "FOWNER", +# "SETGID", +# "SETUID", +# "SETPCAP", +# "NET_BIND_SERVICE", +# "KILL", +# ] + +# List of default sysctls. If it is empty or commented out, only the sysctls +# defined in the container json file by the user/kube will be added. +# default_sysctls = [ +# ] + +# List of devices on the host that a +# user can specify with the "io.kubernetes.cri-o.Devices" allowed annotation. +# allowed_devices = [ +# "/dev/fuse", +# ] + +# List of additional devices. specified as +# "::", for example: "--device=/dev/sdc:/dev/xvdc:rwm". +# If it is empty or commented out, only the devices +# defined in the container json file by the user/kube will be added. +# additional_devices = [ +# ] + +# List of directories to scan for CDI Spec files. +# cdi_spec_dirs = [ +# "/etc/cdi", +# "/var/run/cdi", +# ] + +# Change the default behavior of setting container devices uid/gid from CRI's +# SecurityContext (RunAsUser/RunAsGroup) instead of taking host's uid/gid. +# Defaults to false. +# device_ownership_from_security_context = false + +# Path to OCI hooks directories for automatically executed hooks. If one of the +# directories does not exist, then CRI-O will automatically skip them. +# hooks_dir = [ +# "/usr/share/containers/oci/hooks.d", +# ] + +# Path to the file specifying the defaults mounts for each container. The +# format of the config is /SRC:/DST, one mount per line. Notice that CRI-O reads +# its default mounts from the following two files: +# +# 1) /etc/containers/mounts.conf (i.e., default_mounts_file): This is the +# override file, where users can either add in their own default mounts, or +# override the default mounts shipped with the package. +# +# 2) /usr/share/containers/mounts.conf: This is the default file read for +# mounts. If you want CRI-O to read from a different, specific mounts file, +# you can change the default_mounts_file. Note, if this is done, CRI-O will +# only add mounts it finds in this file. +# +# default_mounts_file = "" + +# Maximum number of processes allowed in a container. +# This option is deprecated. The Kubelet flag '--pod-pids-limit' should be used instead. +# pids_limit = 0 + +# Maximum sized allowed for the container log file. Negative numbers indicate +# that no size limit is imposed. If it is positive, it must be >= 8192 to +# match/exceed conmon's read buffer. The file is truncated and re-opened so the +# limit is never exceeded. This option is deprecated. The Kubelet flag '--container-log-max-size' should be used instead. +# log_size_max = -1 + +# Whether container output should be logged to journald in addition to the kuberentes log file +# log_to_journald = false + +# Path to directory in which container exit files are written to by conmon. +# container_exits_dir = "/var/run/crio/exits" + +# Path to directory for container attach sockets. +# container_attach_socket_dir = "/var/run/crio" + +# The prefix to use for the source of the bind mounts. +# bind_mount_prefix = "" + +# If set to true, all containers will run in read-only mode. +# read_only = false + +# Changes the verbosity of the logs based on the level it is set to. Options +# are fatal, panic, error, warn, info, debug and trace. This option supports +# live configuration reload. +# log_level = "info" + +# Filter the log messages by the provided regular expression. +# This option supports live configuration reload. +# log_filter = "" + +# The UID mappings for the user namespace of each container. A range is +# specified in the form containerUID:HostUID:Size. Multiple ranges must be +# separated by comma. +# uid_mappings = "" + +# The GID mappings for the user namespace of each container. A range is +# specified in the form containerGID:HostGID:Size. Multiple ranges must be +# separated by comma. +# gid_mappings = "" + +# If set, CRI-O will reject any attempt to map host UIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_uid = -1 + +# If set, CRI-O will reject any attempt to map host GIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_gid = -1 + +# The minimal amount of time in seconds to wait before issuing a timeout +# regarding the proper termination of the container. The lowest possible +# value is 30s, whereas lower values are not considered by CRI-O. +# ctr_stop_timeout = 30 + +# drop_infra_ctr determines whether CRI-O drops the infra container +# when a pod does not have a private PID namespace, and does not use +# a kernel separating runtime (like kata). +# It requires manage_ns_lifecycle to be true. +# drop_infra_ctr = true + +# infra_ctr_cpuset determines what CPUs will be used to run infra containers. +# You can use linux CPU list format to specify desired CPUs. +# To get better isolation for guaranteed pods, set this parameter to be equal to kubelet reserved-cpus. +# infra_ctr_cpuset = "" + +# The directory where the state of the managed namespaces gets tracked. +# Only used when manage_ns_lifecycle is true. +# namespaces_dir = "/var/run" + +# pinns_path is the path to find the pinns binary, which is needed to manage namespace lifecycle +# pinns_path = "" + +# default_runtime is the _name_ of the OCI runtime to be used as the default. +# The name is matched against the runtimes map below. If this value is changed, +# the corresponding existing entry from the runtimes map below will be ignored. +# default_runtime = "runc" + +# A list of paths that, when absent from the host, +# will cause a container creation to fail (as opposed to the current behavior being created as a directory). +# This option is to protect from source locations whose existence as a directory could jepordize the health of the node, and whose +# creation as a file is not desired either. +# An example is /etc/hostname, which will cause failures on reboot if it's created as a directory, but often doesn't exist because +# the hostname is being managed dynamically. +# absent_mount_sources_to_reject = [ +# ] + +# The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. +# The runtime to use is picked based on the runtime handler provided by the CRI. +# If no runtime handler is provided, the runtime will be picked based on the level +# of trust of the workload. Each entry in the table should follow the format: +# +#[crio.runtime.runtimes.runtime-handler] +# runtime_path = "/path/to/the/executable" +# runtime_type = "oci" +# runtime_root = "/path/to/the/root" +# privileged_without_host_devices = false +# allowed_annotations = [] +# Where: +# - runtime-handler: name used to identify the runtime +# - runtime_path (optional, string): absolute path to the runtime executable in +# the host filesystem. If omitted, the runtime-handler identifier should match +# the runtime executable name, and the runtime executable should be placed +# in $PATH. +# - runtime_type (optional, string): type of runtime, one of: "oci", "vm". If +# omitted, an "oci" runtime is assumed. +# - runtime_root (optional, string): root directory for storage of containers +# state. +# - runtime_config_path (optional, string): the path for the runtime configuration +# file. This can only be used with when using the VM runtime_type. +# - privileged_without_host_devices (optional, bool): an option for restricting +# host devices from being passed to privileged containers. +# - allowed_annotations (optional, array of strings): an option for specifying +# a list of experimental annotations that this runtime handler is allowed to process. +# The currently recognized values are: +# "io.kubernetes.cri-o.userns-mode" for configuring a user namespace for the pod. +# "io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw" for mounting cgroups writably when set to "true". +# "io.kubernetes.cri-o.Devices" for configuring devices for the pod. +# "io.kubernetes.cri-o.ShmSize" for configuring the size of /dev/shm. +# "io.kubernetes.cri-o.UnifiedCgroup.$CTR_NAME" for configuring the cgroup v2 unified block for a container. +# "io.containers.trace-syscall" for tracing syscalls via the OCI seccomp BPF hook. +# "io.kubernetes.cri.rdt-class" for setting the RDT class of a container +# - monitor_exec_cgroup (optional, string): if set to "container", indicates exec probes +# should be moved to the container's cgroup + + +# [crio.runtime.runtimes.runc] +# runtime_path = "" +# runtime_type = "oci" +# runtime_root = "/run/runc" +# runtime_config_path = "" + +# +# allowed_annotations = [ +# "io.containers.trace-syscall", +# ] +# +# monitor_path = "" +# +# monitor_env = [ +# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +# ] +# +# monitor_cgroup = "system.slice" +# monitor_exec_cgroup = "" +# + +# crun is a fast and lightweight fully featured OCI runtime and C library for +# running containers +#[crio.runtime.runtimes.crun] + +# Kata Containers is an OCI runtime, where containers are run inside lightweight +# VMs. Kata provides additional isolation towards the host, minimizing the host attack +# surface and mitigating the consequences of containers breakout. + +# Kata Containers with the default configured VMM +#[crio.runtime.runtimes.kata-runtime] + +# Kata Containers with the QEMU VMM +#[crio.runtime.runtimes.kata-qemu] + +# Kata Containers with the Firecracker VMM +#[crio.runtime.runtimes.kata-fc] + +# The workloads table defines ways to customize containers with different resources +# that work based on annotations, rather than the CRI. +# Note, the behavior of this table is EXPERIMENTAL and may change at any time. +# Each workload, has a name, activation_annotation, annotation_prefix and set of resources it supports mutating. +# The currently supported resources are "cpu" (to configure the cpu shares) and "cpuset" to configure the cpuset. +# Each resource can have a default value specified, or be empty. +# For a container to opt-into this workload, the pod should be configured with the annotation $activation_annotation (key only, value is ignored). +# To customize per-container, an annotation of the form $annotation_prefix.$resource/$ctrName = "value" can be specified +# signifying for that resource type to override the default value. +# If the annotation_prefix is not present, every container in the pod will be given the default values. +# Example: +# [crio.runtime.workloads.workload-type] +# activation_annotation = "io.crio/workload" +# annotation_prefix = "io.crio.workload-type" +# [crio.runtime.workloads.workload-type.resources] +# cpuset = 0 +# cpushares = "0-1" +# Where: +# The workload name is workload-type. +# To specify, the pod must have the "io.crio.workload" annotation (this is a precise string match). +# This workload supports setting cpuset and cpu resources. +# annotation_prefix is used to customize the different resources. +# To configure the cpu shares a container gets in the example above, the pod would have to have the following annotation: +# "io.crio.workload-type/$container_name = {"cpushares": "value"}" +# + +# The crio.image table contains settings pertaining to the management of OCI images. +# +# CRI-O reads its configured registries defaults from the system wide +# containers-registries.conf(5) located in /etc/containers/registries.conf. If +# you want to modify just CRI-O, you can change the registries configuration in +# this file. Otherwise, leave insecure_registries and registries commented out to +# use the system's defaults from /etc/containers/registries.conf. +[crio.image] + +# Default transport for pulling images from a remote container storage. +# default_transport = "docker://" + +# The path to a file containing credentials necessary for pulling images from +# secure registries. The file is similar to that of /var/lib/kubelet/config.json +# global_auth_file = "" + +# The image used to instantiate infra containers. +# This option supports live configuration reload. +# pause_image = "registry.k8s.io/pause:3.6" + +# The path to a file containing credentials specific for pulling the pause_image from +# above. The file is similar to that of /var/lib/kubelet/config.json +# This option supports live configuration reload. +# pause_image_auth_file = "" + +# The command to run to have a container stay in the paused state. +# When explicitly set to "", it will fallback to the entrypoint and command +# specified in the pause image. When commented out, it will fallback to the +# default: "/pause". This option supports live configuration reload. +# pause_command = "/pause" + +# Path to the file which decides what sort of policy we use when deciding +# whether or not to trust an image that we've pulled. It is not recommended that +# this option be used, as the default behavior of using the system-wide default +# policy (i.e., /etc/containers/policy.json) is most often preferred. Please +# refer to containers-policy.json(5) for more details. +# signature_policy = "" + +# List of registries to skip TLS verification for pulling images. Please +# consider configuring the registries via /etc/containers/registries.conf before +# changing them here. +# insecure_registries = [ +# ] + +# Controls how image volumes are handled. The valid values are mkdir, bind and +# ignore; the latter will ignore volumes entirely. +# image_volumes = "mkdir" + +# Temporary directory to use for storing big files +# big_files_temporary_dir = "" + +# The crio.network table containers settings pertaining to the management of +# CNI plugins. +[crio.network] + +# The default CNI network name to be selected. If not set or "", then +# CRI-O will pick-up the first one found in network_dir. +# cni_default_network = "" + +# Path to the directory where CNI configuration files are located. +network_dir = "/etc/cni/net.d/02" + +# Paths to directories where CNI plugin binaries are located. +plugin_dirs = [ + "/opt/cni/bin/02", "/rr/ff/02" +] + +# A necessary configuration for Prometheus based metrics retrieval +[crio.metrics] + +# Globally enable or disable metrics support. +# enable_metrics = false + +# Specify enabled metrics collectors. +# Per default all metrics are enabled. +# It is possible, to prefix the metrics with "container_runtime_" and "crio_". +# For example, the metrics collector "operations" would be treated in the same +# way as "crio_operations" and "container_runtime_crio_operations". +# metrics_collectors = [ +# "operations", +# "operations_latency_microseconds_total", +# "operations_latency_microseconds", +# "operations_errors", +# "image_pulls_by_digest", +# "image_pulls_by_name", +# "image_pulls_by_name_skipped", +# "image_pulls_failures", +# "image_pulls_successes", +# "image_pulls_layer_size", +# "image_layer_reuse", +# "containers_oom_total", +# "containers_oom", +# "processes_defunct", +# "operations_total", +# "operations_latency_seconds", +# "operations_latency_seconds_total", +# "operations_errors_total", +# "image_pulls_bytes_total", +# "image_pulls_skipped_bytes_total", +# "image_pulls_failure_total", +# "image_pulls_success_total", +# "image_layer_reuse_total", +# "containers_oom_count_total", +# ] +# The port on which the metrics server will listen. +# metrics_port = 9090 + +# Local socket path to bind the metrics server to +# metrics_socket = "" + +# The certificate for the secure metrics server. +# If the certificate is not available on disk, then CRI-O will generate a +# self-signed one. CRI-O also watches for changes of this path and reloads the +# certificate on any modification event. +# metrics_cert = "" + +# The certificate key for the secure metrics server. +# Behaves in the same way as the metrics_cert. +# metrics_key = "" + +# A necessary configuration for OpenTelemetry trace data exporting +[crio.tracing] + +# Globally enable or disable exporting OpenTelemetry traces. +# enable_tracing = false + +# Address on which the gRPC trace collector listens on. +# tracing_endpoint = "0.0.0.0:4317" + +# Number of samples to collect per million spans. +# tracing_sampling_rate_per_million = 0 + +# Necessary information pertaining to container and pod stats reporting. +[crio.stats] + +# The number of seconds between collecting pod and container stats. +# If set to 0, the stats are collected on-demand instead. +# stats_collection_period = 0 \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/crio.d/03_crio.conf b/sensor/internal/utils/testdata/testCNI/crio.d/03_crio.conf new file mode 100644 index 00000000..db0e206a --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/crio.d/03_crio.conf @@ -0,0 +1,570 @@ +# The CRI-O configuration file specifies all of the available configuration +# options and command-line flags for the crio(8) OCI Kubernetes Container Runtime +# daemon, but in a TOML format that can be more easily modified and versioned. +# +# Please refer to crio.conf(5) for details of all configuration options. + +# CRI-O supports partial configuration reload during runtime, which can be +# done by sending SIGHUP to the running process. Currently supported options +# are explicitly mentioned with: 'This option supports live configuration +# reload'. + +# CRI-O reads its storage defaults from the containers-storage.conf(5) file +# located at /etc/containers/storage.conf. Modify this storage configuration if +# you want to change the system's defaults. If you want to modify storage just +# for CRI-O, you can change the storage configuration options here. +[crio] + +# Path to the "root directory". CRI-O stores all of its data, including +# containers images, in this directory. +# root = "/home/abuild/.local/share/containers/storage" + +# Path to the "run directory". CRI-O stores all of its state in this directory. +# runroot = "/tmp/containers-user-399/containers" + +# Storage driver used to manage the storage of images and containers. Please +# refer to containers-storage.conf(5) to see all available storage drivers. +# storage_driver = "vfs" + +# List to pass options to the storage driver. Please refer to +# containers-storage.conf(5) to see all available storage options. +# storage_option = [ +# ] + +# The default log directory where all logs will go unless directly specified by +# the kubelet. The log directory specified must be an absolute directory. +# log_dir = "/var/log/crio/pods" + +# Location for CRI-O to lay down the temporary version file. +# It is used to check if crio wipe should wipe containers, which should +# always happen on a node reboot +# version_file = "/var/run/crio/version" + +# Location for CRI-O to lay down the persistent version file. +# It is used to check if crio wipe should wipe images, which should +# only happen when CRI-O has been upgraded +# version_file_persist = "/var/lib/crio/version" + +# InternalWipe is whether CRI-O should wipe containers and images after a reboot when the server starts. +# If set to false, one must use the external command 'crio wipe' to wipe the containers and images in these situations. +# internal_wipe = true + +# Location for CRI-O to lay down the clean shutdown file. +# It is used to check whether crio had time to sync before shutting down. +# If not found, crio wipe will clear the storage directory. +# clean_shutdown_file = "/var/lib/crio/clean.shutdown" + +# The crio.api table contains settings for the kubelet/gRPC interface. +[crio.api] + +# Path to AF_LOCAL socket on which CRI-O will listen. +# listen = "/var/run/crio/crio.sock" + +# IP address on which the stream server will listen. +# stream_address = "127.0.0.1" + +# The port on which the stream server will listen. If the port is set to "0", then +# CRI-O will allocate a random free port number. +# stream_port = "0" + +# Enable encrypted TLS transport of the stream server. +# stream_enable_tls = false + +# Length of time until open streams terminate due to lack of activity +# stream_idle_timeout = "" + +# Path to the x509 certificate file used to serve the encrypted stream. This +# file can change, and CRI-O will automatically pick up the changes within 5 +# minutes. +# stream_tls_cert = "" + +# Path to the key file used to serve the encrypted stream. This file can +# change and CRI-O will automatically pick up the changes within 5 minutes. +# stream_tls_key = "" + +# Path to the x509 CA(s) file used to verify and authenticate client +# communication with the encrypted stream. This file can change and CRI-O will +# automatically pick up the changes within 5 minutes. +# stream_tls_ca = "" + +# Maximum grpc send message size in bytes. If not set or <=0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_send_msg_size = 83886080 + +# Maximum grpc receive message size. If not set or <= 0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_recv_msg_size = 83886080 + +# The crio.runtime table contains settings pertaining to the OCI runtime used +# and options for how to set up and manage the OCI runtime. +[crio.runtime] + +# A list of ulimits to be set in containers by default, specified as +# "=:", for example: +# "nofile=1024:2048" +# If nothing is set here, settings will be inherited from the CRI-O daemon +# default_ulimits = [ +# ] + +# If true, the runtime will not use pivot_root, but instead use MS_MOVE. +# no_pivot = false + +# decryption_keys_path is the path where the keys required for +# image decryption are stored. This option supports live configuration reload. +# decryption_keys_path = "/etc/crio/keys/" + +# Path to the conmon binary, used for monitoring the OCI runtime. +# Will be searched for using $PATH if empty. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon = "" + +# Cgroup setting for conmon +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorCgroup. +# conmon_cgroup = "" + +# Environment variable list for the conmon process, used for passing necessary +# environment variables to conmon or the runtime. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon_env = [ +# ] + +# Additional environment variables to set for all the +# containers. These are overridden if set in the +# container image spec or in the container runtime configuration. +# default_env = [ +# ] + +# If true, SELinux will be used for pod separation on the host. +# selinux = false + +# Path to the seccomp.json profile which is used as the default seccomp profile +# for the runtime. If not specified, then the internal default seccomp profile +# will be used. This option supports live configuration reload. +# seccomp_profile = "" + +# Changes the meaning of an empty seccomp profile. By default +# (and according to CRI spec), an empty profile means unconfined. +# This option tells CRI-O to treat an empty profile as the default profile, +# which might increase security. +# seccomp_use_default_when_empty = true + +# Used to change the name of the default AppArmor profile of CRI-O. The default +# profile name is "crio-default". This profile only takes effect if the user +# does not specify a profile via the Kubernetes Pod's metadata annotation. If +# the profile is set to "unconfined", then this equals to disabling AppArmor. +# This option supports live configuration reload. +# apparmor_profile = "crio-default" + +# Path to the blockio class configuration file for configuring +# the cgroup blockio controller. +# blockio_config_file = "" + +# Used to change irqbalance service config file path which is used for configuring +# irqbalance daemon. +# irqbalance_config_file = "/etc/sysconfig/irqbalance" + +# Path to the RDT configuration file for configuring the resctrl pseudo-filesystem. +# This option supports live configuration reload. +# rdt_config_file = "" + +# Cgroup management implementation used for the runtime. +# cgroup_manager = "systemd" + +# Specify whether the image pull must be performed in a separate cgroup. +# separate_pull_cgroup = "" + +# List of default capabilities for containers. If it is empty or commented out, +# only the capabilities defined in the containers json file by the user/kube +# will be added. +# default_capabilities = [ +# "CHOWN", +# "DAC_OVERRIDE", +# "FSETID", +# "FOWNER", +# "SETGID", +# "SETUID", +# "SETPCAP", +# "NET_BIND_SERVICE", +# "KILL", +# ] + +# List of default sysctls. If it is empty or commented out, only the sysctls +# defined in the container json file by the user/kube will be added. +# default_sysctls = [ +# ] + +# List of devices on the host that a +# user can specify with the "io.kubernetes.cri-o.Devices" allowed annotation. +# allowed_devices = [ +# "/dev/fuse", +# ] + +# List of additional devices. specified as +# "::", for example: "--device=/dev/sdc:/dev/xvdc:rwm". +# If it is empty or commented out, only the devices +# defined in the container json file by the user/kube will be added. +# additional_devices = [ +# ] + +# List of directories to scan for CDI Spec files. +# cdi_spec_dirs = [ +# "/etc/cdi", +# "/var/run/cdi", +# ] + +# Change the default behavior of setting container devices uid/gid from CRI's +# SecurityContext (RunAsUser/RunAsGroup) instead of taking host's uid/gid. +# Defaults to false. +# device_ownership_from_security_context = false + +# Path to OCI hooks directories for automatically executed hooks. If one of the +# directories does not exist, then CRI-O will automatically skip them. +# hooks_dir = [ +# "/usr/share/containers/oci/hooks.d", +# ] + +# Path to the file specifying the defaults mounts for each container. The +# format of the config is /SRC:/DST, one mount per line. Notice that CRI-O reads +# its default mounts from the following two files: +# +# 1) /etc/containers/mounts.conf (i.e., default_mounts_file): This is the +# override file, where users can either add in their own default mounts, or +# override the default mounts shipped with the package. +# +# 2) /usr/share/containers/mounts.conf: This is the default file read for +# mounts. If you want CRI-O to read from a different, specific mounts file, +# you can change the default_mounts_file. Note, if this is done, CRI-O will +# only add mounts it finds in this file. +# +# default_mounts_file = "" + +# Maximum number of processes allowed in a container. +# This option is deprecated. The Kubelet flag '--pod-pids-limit' should be used instead. +# pids_limit = 0 + +# Maximum sized allowed for the container log file. Negative numbers indicate +# that no size limit is imposed. If it is positive, it must be >= 8192 to +# match/exceed conmon's read buffer. The file is truncated and re-opened so the +# limit is never exceeded. This option is deprecated. The Kubelet flag '--container-log-max-size' should be used instead. +# log_size_max = -1 + +# Whether container output should be logged to journald in addition to the kuberentes log file +# log_to_journald = false + +# Path to directory in which container exit files are written to by conmon. +# container_exits_dir = "/var/run/crio/exits" + +# Path to directory for container attach sockets. +# container_attach_socket_dir = "/var/run/crio" + +# The prefix to use for the source of the bind mounts. +# bind_mount_prefix = "" + +# If set to true, all containers will run in read-only mode. +# read_only = false + +# Changes the verbosity of the logs based on the level it is set to. Options +# are fatal, panic, error, warn, info, debug and trace. This option supports +# live configuration reload. +# log_level = "info" + +# Filter the log messages by the provided regular expression. +# This option supports live configuration reload. +# log_filter = "" + +# The UID mappings for the user namespace of each container. A range is +# specified in the form containerUID:HostUID:Size. Multiple ranges must be +# separated by comma. +# uid_mappings = "" + +# The GID mappings for the user namespace of each container. A range is +# specified in the form containerGID:HostGID:Size. Multiple ranges must be +# separated by comma. +# gid_mappings = "" + +# If set, CRI-O will reject any attempt to map host UIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_uid = -1 + +# If set, CRI-O will reject any attempt to map host GIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_gid = -1 + +# The minimal amount of time in seconds to wait before issuing a timeout +# regarding the proper termination of the container. The lowest possible +# value is 30s, whereas lower values are not considered by CRI-O. +# ctr_stop_timeout = 30 + +# drop_infra_ctr determines whether CRI-O drops the infra container +# when a pod does not have a private PID namespace, and does not use +# a kernel separating runtime (like kata). +# It requires manage_ns_lifecycle to be true. +# drop_infra_ctr = true + +# infra_ctr_cpuset determines what CPUs will be used to run infra containers. +# You can use linux CPU list format to specify desired CPUs. +# To get better isolation for guaranteed pods, set this parameter to be equal to kubelet reserved-cpus. +# infra_ctr_cpuset = "" + +# The directory where the state of the managed namespaces gets tracked. +# Only used when manage_ns_lifecycle is true. +# namespaces_dir = "/var/run" + +# pinns_path is the path to find the pinns binary, which is needed to manage namespace lifecycle +# pinns_path = "" + +# default_runtime is the _name_ of the OCI runtime to be used as the default. +# The name is matched against the runtimes map below. If this value is changed, +# the corresponding existing entry from the runtimes map below will be ignored. +# default_runtime = "runc" + +# A list of paths that, when absent from the host, +# will cause a container creation to fail (as opposed to the current behavior being created as a directory). +# This option is to protect from source locations whose existence as a directory could jepordize the health of the node, and whose +# creation as a file is not desired either. +# An example is /etc/hostname, which will cause failures on reboot if it's created as a directory, but often doesn't exist because +# the hostname is being managed dynamically. +# absent_mount_sources_to_reject = [ +# ] + +# The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. +# The runtime to use is picked based on the runtime handler provided by the CRI. +# If no runtime handler is provided, the runtime will be picked based on the level +# of trust of the workload. Each entry in the table should follow the format: +# +#[crio.runtime.runtimes.runtime-handler] +# runtime_path = "/path/to/the/executable" +# runtime_type = "oci" +# runtime_root = "/path/to/the/root" +# privileged_without_host_devices = false +# allowed_annotations = [] +# Where: +# - runtime-handler: name used to identify the runtime +# - runtime_path (optional, string): absolute path to the runtime executable in +# the host filesystem. If omitted, the runtime-handler identifier should match +# the runtime executable name, and the runtime executable should be placed +# in $PATH. +# - runtime_type (optional, string): type of runtime, one of: "oci", "vm". If +# omitted, an "oci" runtime is assumed. +# - runtime_root (optional, string): root directory for storage of containers +# state. +# - runtime_config_path (optional, string): the path for the runtime configuration +# file. This can only be used with when using the VM runtime_type. +# - privileged_without_host_devices (optional, bool): an option for restricting +# host devices from being passed to privileged containers. +# - allowed_annotations (optional, array of strings): an option for specifying +# a list of experimental annotations that this runtime handler is allowed to process. +# The currently recognized values are: +# "io.kubernetes.cri-o.userns-mode" for configuring a user namespace for the pod. +# "io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw" for mounting cgroups writably when set to "true". +# "io.kubernetes.cri-o.Devices" for configuring devices for the pod. +# "io.kubernetes.cri-o.ShmSize" for configuring the size of /dev/shm. +# "io.kubernetes.cri-o.UnifiedCgroup.$CTR_NAME" for configuring the cgroup v2 unified block for a container. +# "io.containers.trace-syscall" for tracing syscalls via the OCI seccomp BPF hook. +# "io.kubernetes.cri.rdt-class" for setting the RDT class of a container +# - monitor_exec_cgroup (optional, string): if set to "container", indicates exec probes +# should be moved to the container's cgroup + + +# [crio.runtime.runtimes.runc] +# runtime_path = "" +# runtime_type = "oci" +# runtime_root = "/run/runc" +# runtime_config_path = "" + +# +# allowed_annotations = [ +# "io.containers.trace-syscall", +# ] +# +# monitor_path = "" +# +# monitor_env = [ +# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +# ] +# +# monitor_cgroup = "system.slice" +# monitor_exec_cgroup = "" +# + +# crun is a fast and lightweight fully featured OCI runtime and C library for +# running containers +#[crio.runtime.runtimes.crun] + +# Kata Containers is an OCI runtime, where containers are run inside lightweight +# VMs. Kata provides additional isolation towards the host, minimizing the host attack +# surface and mitigating the consequences of containers breakout. + +# Kata Containers with the default configured VMM +#[crio.runtime.runtimes.kata-runtime] + +# Kata Containers with the QEMU VMM +#[crio.runtime.runtimes.kata-qemu] + +# Kata Containers with the Firecracker VMM +#[crio.runtime.runtimes.kata-fc] + +# The workloads table defines ways to customize containers with different resources +# that work based on annotations, rather than the CRI. +# Note, the behavior of this table is EXPERIMENTAL and may change at any time. +# Each workload, has a name, activation_annotation, annotation_prefix and set of resources it supports mutating. +# The currently supported resources are "cpu" (to configure the cpu shares) and "cpuset" to configure the cpuset. +# Each resource can have a default value specified, or be empty. +# For a container to opt-into this workload, the pod should be configured with the annotation $activation_annotation (key only, value is ignored). +# To customize per-container, an annotation of the form $annotation_prefix.$resource/$ctrName = "value" can be specified +# signifying for that resource type to override the default value. +# If the annotation_prefix is not present, every container in the pod will be given the default values. +# Example: +# [crio.runtime.workloads.workload-type] +# activation_annotation = "io.crio/workload" +# annotation_prefix = "io.crio.workload-type" +# [crio.runtime.workloads.workload-type.resources] +# cpuset = 0 +# cpushares = "0-1" +# Where: +# The workload name is workload-type. +# To specify, the pod must have the "io.crio.workload" annotation (this is a precise string match). +# This workload supports setting cpuset and cpu resources. +# annotation_prefix is used to customize the different resources. +# To configure the cpu shares a container gets in the example above, the pod would have to have the following annotation: +# "io.crio.workload-type/$container_name = {"cpushares": "value"}" +# + +# The crio.image table contains settings pertaining to the management of OCI images. +# +# CRI-O reads its configured registries defaults from the system wide +# containers-registries.conf(5) located in /etc/containers/registries.conf. If +# you want to modify just CRI-O, you can change the registries configuration in +# this file. Otherwise, leave insecure_registries and registries commented out to +# use the system's defaults from /etc/containers/registries.conf. +[crio.image] + +# Default transport for pulling images from a remote container storage. +# default_transport = "docker://" + +# The path to a file containing credentials necessary for pulling images from +# secure registries. The file is similar to that of /var/lib/kubelet/config.json +# global_auth_file = "" + +# The image used to instantiate infra containers. +# This option supports live configuration reload. +# pause_image = "registry.k8s.io/pause:3.6" + +# The path to a file containing credentials specific for pulling the pause_image from +# above. The file is similar to that of /var/lib/kubelet/config.json +# This option supports live configuration reload. +# pause_image_auth_file = "" + +# The command to run to have a container stay in the paused state. +# When explicitly set to "", it will fallback to the entrypoint and command +# specified in the pause image. When commented out, it will fallback to the +# default: "/pause". This option supports live configuration reload. +# pause_command = "/pause" + +# Path to the file which decides what sort of policy we use when deciding +# whether or not to trust an image that we've pulled. It is not recommended that +# this option be used, as the default behavior of using the system-wide default +# policy (i.e., /etc/containers/policy.json) is most often preferred. Please +# refer to containers-policy.json(5) for more details. +# signature_policy = "" + +# List of registries to skip TLS verification for pulling images. Please +# consider configuring the registries via /etc/containers/registries.conf before +# changing them here. +# insecure_registries = [ +# ] + +# Controls how image volumes are handled. The valid values are mkdir, bind and +# ignore; the latter will ignore volumes entirely. +# image_volumes = "mkdir" + +# Temporary directory to use for storing big files +# big_files_temporary_dir = "" + +# The crio.network table containers settings pertaining to the management of +# CNI plugins. +[crio.network] + +# The default CNI network name to be selected. If not set or "", then +# CRI-O will pick-up the first one found in network_dir. +# cni_default_network = "" + +# Path to the directory where CNI configuration files are located. +network_dir = "/etc/cni/net.d/03" + +# Paths to directories where CNI plugin binaries are located. +plugin_dirs = [ + +] + +# A necessary configuration for Prometheus based metrics retrieval +[crio.metrics] + +# Globally enable or disable metrics support. +# enable_metrics = false + +# Specify enabled metrics collectors. +# Per default all metrics are enabled. +# It is possible, to prefix the metrics with "container_runtime_" and "crio_". +# For example, the metrics collector "operations" would be treated in the same +# way as "crio_operations" and "container_runtime_crio_operations". +# metrics_collectors = [ +# "operations", +# "operations_latency_microseconds_total", +# "operations_latency_microseconds", +# "operations_errors", +# "image_pulls_by_digest", +# "image_pulls_by_name", +# "image_pulls_by_name_skipped", +# "image_pulls_failures", +# "image_pulls_successes", +# "image_pulls_layer_size", +# "image_layer_reuse", +# "containers_oom_total", +# "containers_oom", +# "processes_defunct", +# "operations_total", +# "operations_latency_seconds", +# "operations_latency_seconds_total", +# "operations_errors_total", +# "image_pulls_bytes_total", +# "image_pulls_skipped_bytes_total", +# "image_pulls_failure_total", +# "image_pulls_success_total", +# "image_layer_reuse_total", +# "containers_oom_count_total", +# ] +# The port on which the metrics server will listen. +# metrics_port = 9090 + +# Local socket path to bind the metrics server to +# metrics_socket = "" + +# The certificate for the secure metrics server. +# If the certificate is not available on disk, then CRI-O will generate a +# self-signed one. CRI-O also watches for changes of this path and reloads the +# certificate on any modification event. +# metrics_cert = "" + +# The certificate key for the secure metrics server. +# Behaves in the same way as the metrics_cert. +# metrics_key = "" + +# A necessary configuration for OpenTelemetry trace data exporting +[crio.tracing] + +# Globally enable or disable exporting OpenTelemetry traces. +# enable_tracing = false + +# Address on which the gRPC trace collector listens on. +# tracing_endpoint = "0.0.0.0:4317" + +# Number of samples to collect per million spans. +# tracing_sampling_rate_per_million = 0 + +# Necessary information pertaining to container and pod stats reporting. +[crio.stats] + +# The number of seconds between collecting pod and container stats. +# If set to 0, the stats are collected on-demand instead. +# stats_collection_period = 0 \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/crio.d/05_crio.conf b/sensor/internal/utils/testdata/testCNI/crio.d/05_crio.conf new file mode 100644 index 00000000..8ff6bd59 --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/crio.d/05_crio.conf @@ -0,0 +1,570 @@ +# The CRI-O configuration file specifies all of the available configuration +# options and command-line flags for the crio(8) OCI Kubernetes Container Runtime +# daemon, but in a TOML format that can be more easily modified and versioned. +# +# Please refer to crio.conf(5) for details of all configuration options. + +# CRI-O supports partial configuration reload during runtime, which can be +# done by sending SIGHUP to the running process. Currently supported options +# are explicitly mentioned with: 'This option supports live configuration +# reload'. + +# CRI-O reads its storage defaults from the containers-storage.conf(5) file +# located at /etc/containers/storage.conf. Modify this storage configuration if +# you want to change the system's defaults. If you want to modify storage just +# for CRI-O, you can change the storage configuration options here. +[crio] + +# Path to the "root directory". CRI-O stores all of its data, including +# containers images, in this directory. +# root = "/home/abuild/.local/share/containers/storage" + +# Path to the "run directory". CRI-O stores all of its state in this directory. +# runroot = "/tmp/containers-user-399/containers" + +# Storage driver used to manage the storage of images and containers. Please +# refer to containers-storage.conf(5) to see all available storage drivers. +# storage_driver = "vfs" + +# List to pass options to the storage driver. Please refer to +# containers-storage.conf(5) to see all available storage options. +# storage_option = [ +# ] + +# The default log directory where all logs will go unless directly specified by +# the kubelet. The log directory specified must be an absolute directory. +# log_dir = "/var/log/crio/pods" + +# Location for CRI-O to lay down the temporary version file. +# It is used to check if crio wipe should wipe containers, which should +# always happen on a node reboot +# version_file = "/var/run/crio/version" + +# Location for CRI-O to lay down the persistent version file. +# It is used to check if crio wipe should wipe images, which should +# only happen when CRI-O has been upgraded +# version_file_persist = "/var/lib/crio/version" + +# InternalWipe is whether CRI-O should wipe containers and images after a reboot when the server starts. +# If set to false, one must use the external command 'crio wipe' to wipe the containers and images in these situations. +# internal_wipe = true + +# Location for CRI-O to lay down the clean shutdown file. +# It is used to check whether crio had time to sync before shutting down. +# If not found, crio wipe will clear the storage directory. +# clean_shutdown_file = "/var/lib/crio/clean.shutdown" + +# The crio.api table contains settings for the kubelet/gRPC interface. +[crio.api] + +# Path to AF_LOCAL socket on which CRI-O will listen. +# listen = "/var/run/crio/crio.sock" + +# IP address on which the stream server will listen. +# stream_address = "127.0.0.1" + +# The port on which the stream server will listen. If the port is set to "0", then +# CRI-O will allocate a random free port number. +# stream_port = "0" + +# Enable encrypted TLS transport of the stream server. +# stream_enable_tls = false + +# Length of time until open streams terminate due to lack of activity +# stream_idle_timeout = "" + +# Path to the x509 certificate file used to serve the encrypted stream. This +# file can change, and CRI-O will automatically pick up the changes within 5 +# minutes. +# stream_tls_cert = "" + +# Path to the key file used to serve the encrypted stream. This file can +# change and CRI-O will automatically pick up the changes within 5 minutes. +# stream_tls_key = "" + +# Path to the x509 CA(s) file used to verify and authenticate client +# communication with the encrypted stream. This file can change and CRI-O will +# automatically pick up the changes within 5 minutes. +# stream_tls_ca = "" + +# Maximum grpc send message size in bytes. If not set or <=0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_send_msg_size = 83886080 + +# Maximum grpc receive message size. If not set or <= 0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_recv_msg_size = 83886080 + +# The crio.runtime table contains settings pertaining to the OCI runtime used +# and options for how to set up and manage the OCI runtime. +[crio.runtime] + +# A list of ulimits to be set in containers by default, specified as +# "=:", for example: +# "nofile=1024:2048" +# If nothing is set here, settings will be inherited from the CRI-O daemon +# default_ulimits = [ +# ] + +# If true, the runtime will not use pivot_root, but instead use MS_MOVE. +# no_pivot = false + +# decryption_keys_path is the path where the keys required for +# image decryption are stored. This option supports live configuration reload. +# decryption_keys_path = "/etc/crio/keys/" + +# Path to the conmon binary, used for monitoring the OCI runtime. +# Will be searched for using $PATH if empty. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon = "" + +# Cgroup setting for conmon +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorCgroup. +# conmon_cgroup = "" + +# Environment variable list for the conmon process, used for passing necessary +# environment variables to conmon or the runtime. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon_env = [ +# ] + +# Additional environment variables to set for all the +# containers. These are overridden if set in the +# container image spec or in the container runtime configuration. +# default_env = [ +# ] + +# If true, SELinux will be used for pod separation on the host. +# selinux = false + +# Path to the seccomp.json profile which is used as the default seccomp profile +# for the runtime. If not specified, then the internal default seccomp profile +# will be used. This option supports live configuration reload. +# seccomp_profile = "" + +# Changes the meaning of an empty seccomp profile. By default +# (and according to CRI spec), an empty profile means unconfined. +# This option tells CRI-O to treat an empty profile as the default profile, +# which might increase security. +# seccomp_use_default_when_empty = true + +# Used to change the name of the default AppArmor profile of CRI-O. The default +# profile name is "crio-default". This profile only takes effect if the user +# does not specify a profile via the Kubernetes Pod's metadata annotation. If +# the profile is set to "unconfined", then this equals to disabling AppArmor. +# This option supports live configuration reload. +# apparmor_profile = "crio-default" + +# Path to the blockio class configuration file for configuring +# the cgroup blockio controller. +# blockio_config_file = "" + +# Used to change irqbalance service config file path which is used for configuring +# irqbalance daemon. +# irqbalance_config_file = "/etc/sysconfig/irqbalance" + +# Path to the RDT configuration file for configuring the resctrl pseudo-filesystem. +# This option supports live configuration reload. +# rdt_config_file = "" + +# Cgroup management implementation used for the runtime. +# cgroup_manager = "systemd" + +# Specify whether the image pull must be performed in a separate cgroup. +# separate_pull_cgroup = "" + +# List of default capabilities for containers. If it is empty or commented out, +# only the capabilities defined in the containers json file by the user/kube +# will be added. +# default_capabilities = [ +# "CHOWN", +# "DAC_OVERRIDE", +# "FSETID", +# "FOWNER", +# "SETGID", +# "SETUID", +# "SETPCAP", +# "NET_BIND_SERVICE", +# "KILL", +# ] + +# List of default sysctls. If it is empty or commented out, only the sysctls +# defined in the container json file by the user/kube will be added. +# default_sysctls = [ +# ] + +# List of devices on the host that a +# user can specify with the "io.kubernetes.cri-o.Devices" allowed annotation. +# allowed_devices = [ +# "/dev/fuse", +# ] + +# List of additional devices. specified as +# "::", for example: "--device=/dev/sdc:/dev/xvdc:rwm". +# If it is empty or commented out, only the devices +# defined in the container json file by the user/kube will be added. +# additional_devices = [ +# ] + +# List of directories to scan for CDI Spec files. +# cdi_spec_dirs = [ +# "/etc/cdi", +# "/var/run/cdi", +# ] + +# Change the default behavior of setting container devices uid/gid from CRI's +# SecurityContext (RunAsUser/RunAsGroup) instead of taking host's uid/gid. +# Defaults to false. +# device_ownership_from_security_context = false + +# Path to OCI hooks directories for automatically executed hooks. If one of the +# directories does not exist, then CRI-O will automatically skip them. +# hooks_dir = [ +# "/usr/share/containers/oci/hooks.d", +# ] + +# Path to the file specifying the defaults mounts for each container. The +# format of the config is /SRC:/DST, one mount per line. Notice that CRI-O reads +# its default mounts from the following two files: +# +# 1) /etc/containers/mounts.conf (i.e., default_mounts_file): This is the +# override file, where users can either add in their own default mounts, or +# override the default mounts shipped with the package. +# +# 2) /usr/share/containers/mounts.conf: This is the default file read for +# mounts. If you want CRI-O to read from a different, specific mounts file, +# you can change the default_mounts_file. Note, if this is done, CRI-O will +# only add mounts it finds in this file. +# +# default_mounts_file = "" + +# Maximum number of processes allowed in a container. +# This option is deprecated. The Kubelet flag '--pod-pids-limit' should be used instead. +# pids_limit = 0 + +# Maximum sized allowed for the container log file. Negative numbers indicate +# that no size limit is imposed. If it is positive, it must be >= 8192 to +# match/exceed conmon's read buffer. The file is truncated and re-opened so the +# limit is never exceeded. This option is deprecated. The Kubelet flag '--container-log-max-size' should be used instead. +# log_size_max = -1 + +# Whether container output should be logged to journald in addition to the kuberentes log file +# log_to_journald = false + +# Path to directory in which container exit files are written to by conmon. +# container_exits_dir = "/var/run/crio/exits" + +# Path to directory for container attach sockets. +# container_attach_socket_dir = "/var/run/crio" + +# The prefix to use for the source of the bind mounts. +# bind_mount_prefix = "" + +# If set to true, all containers will run in read-only mode. +# read_only = false + +# Changes the verbosity of the logs based on the level it is set to. Options +# are fatal, panic, error, warn, info, debug and trace. This option supports +# live configuration reload. +# log_level = "info" + +# Filter the log messages by the provided regular expression. +# This option supports live configuration reload. +# log_filter = "" + +# The UID mappings for the user namespace of each container. A range is +# specified in the form containerUID:HostUID:Size. Multiple ranges must be +# separated by comma. +# uid_mappings = "" + +# The GID mappings for the user namespace of each container. A range is +# specified in the form containerGID:HostGID:Size. Multiple ranges must be +# separated by comma. +# gid_mappings = "" + +# If set, CRI-O will reject any attempt to map host UIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_uid = -1 + +# If set, CRI-O will reject any attempt to map host GIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_gid = -1 + +# The minimal amount of time in seconds to wait before issuing a timeout +# regarding the proper termination of the container. The lowest possible +# value is 30s, whereas lower values are not considered by CRI-O. +# ctr_stop_timeout = 30 + +# drop_infra_ctr determines whether CRI-O drops the infra container +# when a pod does not have a private PID namespace, and does not use +# a kernel separating runtime (like kata). +# It requires manage_ns_lifecycle to be true. +# drop_infra_ctr = true + +# infra_ctr_cpuset determines what CPUs will be used to run infra containers. +# You can use linux CPU list format to specify desired CPUs. +# To get better isolation for guaranteed pods, set this parameter to be equal to kubelet reserved-cpus. +# infra_ctr_cpuset = "" + +# The directory where the state of the managed namespaces gets tracked. +# Only used when manage_ns_lifecycle is true. +# namespaces_dir = "/var/run" + +# pinns_path is the path to find the pinns binary, which is needed to manage namespace lifecycle +# pinns_path = "" + +# default_runtime is the _name_ of the OCI runtime to be used as the default. +# The name is matched against the runtimes map below. If this value is changed, +# the corresponding existing entry from the runtimes map below will be ignored. +# default_runtime = "runc" + +# A list of paths that, when absent from the host, +# will cause a container creation to fail (as opposed to the current behavior being created as a directory). +# This option is to protect from source locations whose existence as a directory could jepordize the health of the node, and whose +# creation as a file is not desired either. +# An example is /etc/hostname, which will cause failures on reboot if it's created as a directory, but often doesn't exist because +# the hostname is being managed dynamically. +# absent_mount_sources_to_reject = [ +# ] + +# The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. +# The runtime to use is picked based on the runtime handler provided by the CRI. +# If no runtime handler is provided, the runtime will be picked based on the level +# of trust of the workload. Each entry in the table should follow the format: +# +#[crio.runtime.runtimes.runtime-handler] +# runtime_path = "/path/to/the/executable" +# runtime_type = "oci" +# runtime_root = "/path/to/the/root" +# privileged_without_host_devices = false +# allowed_annotations = [] +# Where: +# - runtime-handler: name used to identify the runtime +# - runtime_path (optional, string): absolute path to the runtime executable in +# the host filesystem. If omitted, the runtime-handler identifier should match +# the runtime executable name, and the runtime executable should be placed +# in $PATH. +# - runtime_type (optional, string): type of runtime, one of: "oci", "vm". If +# omitted, an "oci" runtime is assumed. +# - runtime_root (optional, string): root directory for storage of containers +# state. +# - runtime_config_path (optional, string): the path for the runtime configuration +# file. This can only be used with when using the VM runtime_type. +# - privileged_without_host_devices (optional, bool): an option for restricting +# host devices from being passed to privileged containers. +# - allowed_annotations (optional, array of strings): an option for specifying +# a list of experimental annotations that this runtime handler is allowed to process. +# The currently recognized values are: +# "io.kubernetes.cri-o.userns-mode" for configuring a user namespace for the pod. +# "io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw" for mounting cgroups writably when set to "true". +# "io.kubernetes.cri-o.Devices" for configuring devices for the pod. +# "io.kubernetes.cri-o.ShmSize" for configuring the size of /dev/shm. +# "io.kubernetes.cri-o.UnifiedCgroup.$CTR_NAME" for configuring the cgroup v2 unified block for a container. +# "io.containers.trace-syscall" for tracing syscalls via the OCI seccomp BPF hook. +# "io.kubernetes.cri.rdt-class" for setting the RDT class of a container +# - monitor_exec_cgroup (optional, string): if set to "container", indicates exec probes +# should be moved to the container's cgroup + + +# [crio.runtime.runtimes.runc] +# runtime_path = "" +# runtime_type = "oci" +# runtime_root = "/run/runc" +# runtime_config_path = "" + +# +# allowed_annotations = [ +# "io.containers.trace-syscall", +# ] +# +# monitor_path = "" +# +# monitor_env = [ +# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +# ] +# +# monitor_cgroup = "system.slice" +# monitor_exec_cgroup = "" +# + +# crun is a fast and lightweight fully featured OCI runtime and C library for +# running containers +#[crio.runtime.runtimes.crun] + +# Kata Containers is an OCI runtime, where containers are run inside lightweight +# VMs. Kata provides additional isolation towards the host, minimizing the host attack +# surface and mitigating the consequences of containers breakout. + +# Kata Containers with the default configured VMM +#[crio.runtime.runtimes.kata-runtime] + +# Kata Containers with the QEMU VMM +#[crio.runtime.runtimes.kata-qemu] + +# Kata Containers with the Firecracker VMM +#[crio.runtime.runtimes.kata-fc] + +# The workloads table defines ways to customize containers with different resources +# that work based on annotations, rather than the CRI. +# Note, the behavior of this table is EXPERIMENTAL and may change at any time. +# Each workload, has a name, activation_annotation, annotation_prefix and set of resources it supports mutating. +# The currently supported resources are "cpu" (to configure the cpu shares) and "cpuset" to configure the cpuset. +# Each resource can have a default value specified, or be empty. +# For a container to opt-into this workload, the pod should be configured with the annotation $activation_annotation (key only, value is ignored). +# To customize per-container, an annotation of the form $annotation_prefix.$resource/$ctrName = "value" can be specified +# signifying for that resource type to override the default value. +# If the annotation_prefix is not present, every container in the pod will be given the default values. +# Example: +# [crio.runtime.workloads.workload-type] +# activation_annotation = "io.crio/workload" +# annotation_prefix = "io.crio.workload-type" +# [crio.runtime.workloads.workload-type.resources] +# cpuset = 0 +# cpushares = "0-1" +# Where: +# The workload name is workload-type. +# To specify, the pod must have the "io.crio.workload" annotation (this is a precise string match). +# This workload supports setting cpuset and cpu resources. +# annotation_prefix is used to customize the different resources. +# To configure the cpu shares a container gets in the example above, the pod would have to have the following annotation: +# "io.crio.workload-type/$container_name = {"cpushares": "value"}" +# + +# The crio.image table contains settings pertaining to the management of OCI images. +# +# CRI-O reads its configured registries defaults from the system wide +# containers-registries.conf(5) located in /etc/containers/registries.conf. If +# you want to modify just CRI-O, you can change the registries configuration in +# this file. Otherwise, leave insecure_registries and registries commented out to +# use the system's defaults from /etc/containers/registries.conf. +[crio.image] + +# Default transport for pulling images from a remote container storage. +# default_transport = "docker://" + +# The path to a file containing credentials necessary for pulling images from +# secure registries. The file is similar to that of /var/lib/kubelet/config.json +# global_auth_file = "" + +# The image used to instantiate infra containers. +# This option supports live configuration reload. +# pause_image = "registry.k8s.io/pause:3.6" + +# The path to a file containing credentials specific for pulling the pause_image from +# above. The file is similar to that of /var/lib/kubelet/config.json +# This option supports live configuration reload. +# pause_image_auth_file = "" + +# The command to run to have a container stay in the paused state. +# When explicitly set to "", it will fallback to the entrypoint and command +# specified in the pause image. When commented out, it will fallback to the +# default: "/pause". This option supports live configuration reload. +# pause_command = "/pause" + +# Path to the file which decides what sort of policy we use when deciding +# whether or not to trust an image that we've pulled. It is not recommended that +# this option be used, as the default behavior of using the system-wide default +# policy (i.e., /etc/containers/policy.json) is most often preferred. Please +# refer to containers-policy.json(5) for more details. +# signature_policy = "" + +# List of registries to skip TLS verification for pulling images. Please +# consider configuring the registries via /etc/containers/registries.conf before +# changing them here. +# insecure_registries = [ +# ] + +# Controls how image volumes are handled. The valid values are mkdir, bind and +# ignore; the latter will ignore volumes entirely. +# image_volumes = "mkdir" + +# Temporary directory to use for storing big files +# big_files_temporary_dir = "" + +# The crio.network table containers settings pertaining to the management of +# CNI plugins. +[crio.network] + +# The default CNI network name to be selected. If not set or "", then +# CRI-O will pick-up the first one found in network_dir. +# cni_default_network = "" + +# Path to the directory where CNI configuration files are located. +network_dir = "" + +# Paths to directories where CNI plugin binaries are located. +plugin_dirs = [ + +] + +# A necessary configuration for Prometheus based metrics retrieval +[crio.metrics] + +# Globally enable or disable metrics support. +# enable_metrics = false + +# Specify enabled metrics collectors. +# Per default all metrics are enabled. +# It is possible, to prefix the metrics with "container_runtime_" and "crio_". +# For example, the metrics collector "operations" would be treated in the same +# way as "crio_operations" and "container_runtime_crio_operations". +# metrics_collectors = [ +# "operations", +# "operations_latency_microseconds_total", +# "operations_latency_microseconds", +# "operations_errors", +# "image_pulls_by_digest", +# "image_pulls_by_name", +# "image_pulls_by_name_skipped", +# "image_pulls_failures", +# "image_pulls_successes", +# "image_pulls_layer_size", +# "image_layer_reuse", +# "containers_oom_total", +# "containers_oom", +# "processes_defunct", +# "operations_total", +# "operations_latency_seconds", +# "operations_latency_seconds_total", +# "operations_errors_total", +# "image_pulls_bytes_total", +# "image_pulls_skipped_bytes_total", +# "image_pulls_failure_total", +# "image_pulls_success_total", +# "image_layer_reuse_total", +# "containers_oom_count_total", +# ] +# The port on which the metrics server will listen. +# metrics_port = 9090 + +# Local socket path to bind the metrics server to +# metrics_socket = "" + +# The certificate for the secure metrics server. +# If the certificate is not available on disk, then CRI-O will generate a +# self-signed one. CRI-O also watches for changes of this path and reloads the +# certificate on any modification event. +# metrics_cert = "" + +# The certificate key for the secure metrics server. +# Behaves in the same way as the metrics_cert. +# metrics_key = "" + +# A necessary configuration for OpenTelemetry trace data exporting +[crio.tracing] + +# Globally enable or disable exporting OpenTelemetry traces. +# enable_tracing = false + +# Address on which the gRPC trace collector listens on. +# tracing_endpoint = "0.0.0.0:4317" + +# Number of samples to collect per million spans. +# tracing_sampling_rate_per_million = 0 + +# Necessary information pertaining to container and pod stats reporting. +[crio.stats] + +# The number of seconds between collecting pod and container stats. +# If set to 0, the stats are collected on-demand instead. +# stats_collection_period = 0 \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/crio.d/06_crio.conf b/sensor/internal/utils/testdata/testCNI/crio.d/06_crio.conf new file mode 100644 index 00000000..2035c6e0 --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/crio.d/06_crio.conf @@ -0,0 +1,570 @@ +# The CRI-O configuration file specifies all of the available configuration +# options and command-line flags for the crio(8) OCI Kubernetes Container Runtime +# daemon, but in a TOML format that can be more easily modified and versioned. +# +# Please refer to crio.conf(5) for details of all configuration options. + +# CRI-O supports partial configuration reload during runtime, which can be +# done by sending SIGHUP to the running process. Currently supported options +# are explicitly mentioned with: 'This option supports live configuration +# reload'. + +# CRI-O reads its storage defaults from the containers-storage.conf(5) file +# located at /etc/containers/storage.conf. Modify this storage configuration if +# you want to change the system's defaults. If you want to modify storage just +# for CRI-O, you can change the storage configuration options here. +[crio] + +# Path to the "root directory". CRI-O stores all of its data, including +# containers images, in this directory. +# root = "/home/abuild/.local/share/containers/storage" + +# Path to the "run directory". CRI-O stores all of its state in this directory. +# runroot = "/tmp/containers-user-399/containers" + +# Storage driver used to manage the storage of images and containers. Please +# refer to containers-storage.conf(5) to see all available storage drivers. +# storage_driver = "vfs" + +# List to pass options to the storage driver. Please refer to +# containers-storage.conf(5) to see all available storage options. +# storage_option = [ +# ] + +# The default log directory where all logs will go unless directly specified by +# the kubelet. The log directory specified must be an absolute directory. +# log_dir = "/var/log/crio/pods" + +# Location for CRI-O to lay down the temporary version file. +# It is used to check if crio wipe should wipe containers, which should +# always happen on a node reboot +# version_file = "/var/run/crio/version" + +# Location for CRI-O to lay down the persistent version file. +# It is used to check if crio wipe should wipe images, which should +# only happen when CRI-O has been upgraded +# version_file_persist = "/var/lib/crio/version" + +# InternalWipe is whether CRI-O should wipe containers and images after a reboot when the server starts. +# If set to false, one must use the external command 'crio wipe' to wipe the containers and images in these situations. +# internal_wipe = true + +# Location for CRI-O to lay down the clean shutdown file. +# It is used to check whether crio had time to sync before shutting down. +# If not found, crio wipe will clear the storage directory. +# clean_shutdown_file = "/var/lib/crio/clean.shutdown" + +# The crio.api table contains settings for the kubelet/gRPC interface. +[crio.api] + +# Path to AF_LOCAL socket on which CRI-O will listen. +# listen = "/var/run/crio/crio.sock" + +# IP address on which the stream server will listen. +# stream_address = "127.0.0.1" + +# The port on which the stream server will listen. If the port is set to "0", then +# CRI-O will allocate a random free port number. +# stream_port = "0" + +# Enable encrypted TLS transport of the stream server. +# stream_enable_tls = false + +# Length of time until open streams terminate due to lack of activity +# stream_idle_timeout = "" + +# Path to the x509 certificate file used to serve the encrypted stream. This +# file can change, and CRI-O will automatically pick up the changes within 5 +# minutes. +# stream_tls_cert = "" + +# Path to the key file used to serve the encrypted stream. This file can +# change and CRI-O will automatically pick up the changes within 5 minutes. +# stream_tls_key = "" + +# Path to the x509 CA(s) file used to verify and authenticate client +# communication with the encrypted stream. This file can change and CRI-O will +# automatically pick up the changes within 5 minutes. +# stream_tls_ca = "" + +# Maximum grpc send message size in bytes. If not set or <=0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_send_msg_size = 83886080 + +# Maximum grpc receive message size. If not set or <= 0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_recv_msg_size = 83886080 + +# The crio.runtime table contains settings pertaining to the OCI runtime used +# and options for how to set up and manage the OCI runtime. +[crio.runtime] + +# A list of ulimits to be set in containers by default, specified as +# "=:", for example: +# "nofile=1024:2048" +# If nothing is set here, settings will be inherited from the CRI-O daemon +# default_ulimits = [ +# ] + +# If true, the runtime will not use pivot_root, but instead use MS_MOVE. +# no_pivot = false + +# decryption_keys_path is the path where the keys required for +# image decryption are stored. This option supports live configuration reload. +# decryption_keys_path = "/etc/crio/keys/" + +# Path to the conmon binary, used for monitoring the OCI runtime. +# Will be searched for using $PATH if empty. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon = "" + +# Cgroup setting for conmon +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorCgroup. +# conmon_cgroup = "" + +# Environment variable list for the conmon process, used for passing necessary +# environment variables to conmon or the runtime. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon_env = [ +# ] + +# Additional environment variables to set for all the +# containers. These are overridden if set in the +# container image spec or in the container runtime configuration. +# default_env = [ +# ] + +# If true, SELinux will be used for pod separation on the host. +# selinux = false + +# Path to the seccomp.json profile which is used as the default seccomp profile +# for the runtime. If not specified, then the internal default seccomp profile +# will be used. This option supports live configuration reload. +# seccomp_profile = "" + +# Changes the meaning of an empty seccomp profile. By default +# (and according to CRI spec), an empty profile means unconfined. +# This option tells CRI-O to treat an empty profile as the default profile, +# which might increase security. +# seccomp_use_default_when_empty = true + +# Used to change the name of the default AppArmor profile of CRI-O. The default +# profile name is "crio-default". This profile only takes effect if the user +# does not specify a profile via the Kubernetes Pod's metadata annotation. If +# the profile is set to "unconfined", then this equals to disabling AppArmor. +# This option supports live configuration reload. +# apparmor_profile = "crio-default" + +# Path to the blockio class configuration file for configuring +# the cgroup blockio controller. +# blockio_config_file = "" + +# Used to change irqbalance service config file path which is used for configuring +# irqbalance daemon. +# irqbalance_config_file = "/etc/sysconfig/irqbalance" + +# Path to the RDT configuration file for configuring the resctrl pseudo-filesystem. +# This option supports live configuration reload. +# rdt_config_file = "" + +# Cgroup management implementation used for the runtime. +# cgroup_manager = "systemd" + +# Specify whether the image pull must be performed in a separate cgroup. +# separate_pull_cgroup = "" + +# List of default capabilities for containers. If it is empty or commented out, +# only the capabilities defined in the containers json file by the user/kube +# will be added. +# default_capabilities = [ +# "CHOWN", +# "DAC_OVERRIDE", +# "FSETID", +# "FOWNER", +# "SETGID", +# "SETUID", +# "SETPCAP", +# "NET_BIND_SERVICE", +# "KILL", +# ] + +# List of default sysctls. If it is empty or commented out, only the sysctls +# defined in the container json file by the user/kube will be added. +# default_sysctls = [ +# ] + +# List of devices on the host that a +# user can specify with the "io.kubernetes.cri-o.Devices" allowed annotation. +# allowed_devices = [ +# "/dev/fuse", +# ] + +# List of additional devices. specified as +# "::", for example: "--device=/dev/sdc:/dev/xvdc:rwm". +# If it is empty or commented out, only the devices +# defined in the container json file by the user/kube will be added. +# additional_devices = [ +# ] + +# List of directories to scan for CDI Spec files. +# cdi_spec_dirs = [ +# "/etc/cdi", +# "/var/run/cdi", +# ] + +# Change the default behavior of setting container devices uid/gid from CRI's +# SecurityContext (RunAsUser/RunAsGroup) instead of taking host's uid/gid. +# Defaults to false. +# device_ownership_from_security_context = false + +# Path to OCI hooks directories for automatically executed hooks. If one of the +# directories does not exist, then CRI-O will automatically skip them. +# hooks_dir = [ +# "/usr/share/containers/oci/hooks.d", +# ] + +# Path to the file specifying the defaults mounts for each container. The +# format of the config is /SRC:/DST, one mount per line. Notice that CRI-O reads +# its default mounts from the following two files: +# +# 1) /etc/containers/mounts.conf (i.e., default_mounts_file): This is the +# override file, where users can either add in their own default mounts, or +# override the default mounts shipped with the package. +# +# 2) /usr/share/containers/mounts.conf: This is the default file read for +# mounts. If you want CRI-O to read from a different, specific mounts file, +# you can change the default_mounts_file. Note, if this is done, CRI-O will +# only add mounts it finds in this file. +# +# default_mounts_file = "" + +# Maximum number of processes allowed in a container. +# This option is deprecated. The Kubelet flag '--pod-pids-limit' should be used instead. +# pids_limit = 0 + +# Maximum sized allowed for the container log file. Negative numbers indicate +# that no size limit is imposed. If it is positive, it must be >= 8192 to +# match/exceed conmon's read buffer. The file is truncated and re-opened so the +# limit is never exceeded. This option is deprecated. The Kubelet flag '--container-log-max-size' should be used instead. +# log_size_max = -1 + +# Whether container output should be logged to journald in addition to the kuberentes log file +# log_to_journald = false + +# Path to directory in which container exit files are written to by conmon. +# container_exits_dir = "/var/run/crio/exits" + +# Path to directory for container attach sockets. +# container_attach_socket_dir = "/var/run/crio" + +# The prefix to use for the source of the bind mounts. +# bind_mount_prefix = "" + +# If set to true, all containers will run in read-only mode. +# read_only = false + +# Changes the verbosity of the logs based on the level it is set to. Options +# are fatal, panic, error, warn, info, debug and trace. This option supports +# live configuration reload. +# log_level = "info" + +# Filter the log messages by the provided regular expression. +# This option supports live configuration reload. +# log_filter = "" + +# The UID mappings for the user namespace of each container. A range is +# specified in the form containerUID:HostUID:Size. Multiple ranges must be +# separated by comma. +# uid_mappings = "" + +# The GID mappings for the user namespace of each container. A range is +# specified in the form containerGID:HostGID:Size. Multiple ranges must be +# separated by comma. +# gid_mappings = "" + +# If set, CRI-O will reject any attempt to map host UIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_uid = -1 + +# If set, CRI-O will reject any attempt to map host GIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_gid = -1 + +# The minimal amount of time in seconds to wait before issuing a timeout +# regarding the proper termination of the container. The lowest possible +# value is 30s, whereas lower values are not considered by CRI-O. +# ctr_stop_timeout = 30 + +# drop_infra_ctr determines whether CRI-O drops the infra container +# when a pod does not have a private PID namespace, and does not use +# a kernel separating runtime (like kata). +# It requires manage_ns_lifecycle to be true. +# drop_infra_ctr = true + +# infra_ctr_cpuset determines what CPUs will be used to run infra containers. +# You can use linux CPU list format to specify desired CPUs. +# To get better isolation for guaranteed pods, set this parameter to be equal to kubelet reserved-cpus. +# infra_ctr_cpuset = "" + +# The directory where the state of the managed namespaces gets tracked. +# Only used when manage_ns_lifecycle is true. +# namespaces_dir = "/var/run" + +# pinns_path is the path to find the pinns binary, which is needed to manage namespace lifecycle +# pinns_path = "" + +# default_runtime is the _name_ of the OCI runtime to be used as the default. +# The name is matched against the runtimes map below. If this value is changed, +# the corresponding existing entry from the runtimes map below will be ignored. +# default_runtime = "runc" + +# A list of paths that, when absent from the host, +# will cause a container creation to fail (as opposed to the current behavior being created as a directory). +# This option is to protect from source locations whose existence as a directory could jepordize the health of the node, and whose +# creation as a file is not desired either. +# An example is /etc/hostname, which will cause failures on reboot if it's created as a directory, but often doesn't exist because +# the hostname is being managed dynamically. +# absent_mount_sources_to_reject = [ +# ] + +# The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. +# The runtime to use is picked based on the runtime handler provided by the CRI. +# If no runtime handler is provided, the runtime will be picked based on the level +# of trust of the workload. Each entry in the table should follow the format: +# +#[crio.runtime.runtimes.runtime-handler] +# runtime_path = "/path/to/the/executable" +# runtime_type = "oci" +# runtime_root = "/path/to/the/root" +# privileged_without_host_devices = false +# allowed_annotations = [] +# Where: +# - runtime-handler: name used to identify the runtime +# - runtime_path (optional, string): absolute path to the runtime executable in +# the host filesystem. If omitted, the runtime-handler identifier should match +# the runtime executable name, and the runtime executable should be placed +# in $PATH. +# - runtime_type (optional, string): type of runtime, one of: "oci", "vm". If +# omitted, an "oci" runtime is assumed. +# - runtime_root (optional, string): root directory for storage of containers +# state. +# - runtime_config_path (optional, string): the path for the runtime configuration +# file. This can only be used with when using the VM runtime_type. +# - privileged_without_host_devices (optional, bool): an option for restricting +# host devices from being passed to privileged containers. +# - allowed_annotations (optional, array of strings): an option for specifying +# a list of experimental annotations that this runtime handler is allowed to process. +# The currently recognized values are: +# "io.kubernetes.cri-o.userns-mode" for configuring a user namespace for the pod. +# "io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw" for mounting cgroups writably when set to "true". +# "io.kubernetes.cri-o.Devices" for configuring devices for the pod. +# "io.kubernetes.cri-o.ShmSize" for configuring the size of /dev/shm. +# "io.kubernetes.cri-o.UnifiedCgroup.$CTR_NAME" for configuring the cgroup v2 unified block for a container. +# "io.containers.trace-syscall" for tracing syscalls via the OCI seccomp BPF hook. +# "io.kubernetes.cri.rdt-class" for setting the RDT class of a container +# - monitor_exec_cgroup (optional, string): if set to "container", indicates exec probes +# should be moved to the container's cgroup + + +# [crio.runtime.runtimes.runc] +# runtime_path = "" +# runtime_type = "oci" +# runtime_root = "/run/runc" +# runtime_config_path = "" + +# +# allowed_annotations = [ +# "io.containers.trace-syscall", +# ] +# +# monitor_path = "" +# +# monitor_env = [ +# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +# ] +# +# monitor_cgroup = "system.slice" +# monitor_exec_cgroup = "" +# + +# crun is a fast and lightweight fully featured OCI runtime and C library for +# running containers +#[crio.runtime.runtimes.crun] + +# Kata Containers is an OCI runtime, where containers are run inside lightweight +# VMs. Kata provides additional isolation towards the host, minimizing the host attack +# surface and mitigating the consequences of containers breakout. + +# Kata Containers with the default configured VMM +#[crio.runtime.runtimes.kata-runtime] + +# Kata Containers with the QEMU VMM +#[crio.runtime.runtimes.kata-qemu] + +# Kata Containers with the Firecracker VMM +#[crio.runtime.runtimes.kata-fc] + +# The workloads table defines ways to customize containers with different resources +# that work based on annotations, rather than the CRI. +# Note, the behavior of this table is EXPERIMENTAL and may change at any time. +# Each workload, has a name, activation_annotation, annotation_prefix and set of resources it supports mutating. +# The currently supported resources are "cpu" (to configure the cpu shares) and "cpuset" to configure the cpuset. +# Each resource can have a default value specified, or be empty. +# For a container to opt-into this workload, the pod should be configured with the annotation $activation_annotation (key only, value is ignored). +# To customize per-container, an annotation of the form $annotation_prefix.$resource/$ctrName = "value" can be specified +# signifying for that resource type to override the default value. +# If the annotation_prefix is not present, every container in the pod will be given the default values. +# Example: +# [crio.runtime.workloads.workload-type] +# activation_annotation = "io.crio/workload" +# annotation_prefix = "io.crio.workload-type" +# [crio.runtime.workloads.workload-type.resources] +# cpuset = 0 +# cpushares = "0-1" +# Where: +# The workload name is workload-type. +# To specify, the pod must have the "io.crio.workload" annotation (this is a precise string match). +# This workload supports setting cpuset and cpu resources. +# annotation_prefix is used to customize the different resources. +# To configure the cpu shares a container gets in the example above, the pod would have to have the following annotation: +# "io.crio.workload-type/$container_name = {"cpushares": "value"}" +# + +# The crio.image table contains settings pertaining to the management of OCI images. +# +# CRI-O reads its configured registries defaults from the system wide +# containers-registries.conf(5) located in /etc/containers/registries.conf. If +# you want to modify just CRI-O, you can change the registries configuration in +# this file. Otherwise, leave insecure_registries and registries commented out to +# use the system's defaults from /etc/containers/registries.conf. +[crio.image] + +# Default transport for pulling images from a remote container storage. +# default_transport = "docker://" + +# The path to a file containing credentials necessary for pulling images from +# secure registries. The file is similar to that of /var/lib/kubelet/config.json +# global_auth_file = "" + +# The image used to instantiate infra containers. +# This option supports live configuration reload. +# pause_image = "registry.k8s.io/pause:3.6" + +# The path to a file containing credentials specific for pulling the pause_image from +# above. The file is similar to that of /var/lib/kubelet/config.json +# This option supports live configuration reload. +# pause_image_auth_file = "" + +# The command to run to have a container stay in the paused state. +# When explicitly set to "", it will fallback to the entrypoint and command +# specified in the pause image. When commented out, it will fallback to the +# default: "/pause". This option supports live configuration reload. +# pause_command = "/pause" + +# Path to the file which decides what sort of policy we use when deciding +# whether or not to trust an image that we've pulled. It is not recommended that +# this option be used, as the default behavior of using the system-wide default +# policy (i.e., /etc/containers/policy.json) is most often preferred. Please +# refer to containers-policy.json(5) for more details. +# signature_policy = "" + +# List of registries to skip TLS verification for pulling images. Please +# consider configuring the registries via /etc/containers/registries.conf before +# changing them here. +# insecure_registries = [ +# ] + +# Controls how image volumes are handled. The valid values are mkdir, bind and +# ignore; the latter will ignore volumes entirely. +# image_volumes = "mkdir" + +# Temporary directory to use for storing big files +# big_files_temporary_dir = "" + +# The crio.network table containers settings pertaining to the management of +# CNI plugins. +[crio.network] + +# The default CNI network name to be selected. If not set or "", then +# CRI-O will pick-up the first one found in network_dir. +# cni_default_network = "" + +# # Path to the directory where CNI configuration files are located. +# network_dir = "" + +# # Paths to directories where CNI plugin binaries are located. +# plugin_dirs = [ + +# ] + +# A necessary configuration for Prometheus based metrics retrieval +[crio.metrics] + +# Globally enable or disable metrics support. +# enable_metrics = false + +# Specify enabled metrics collectors. +# Per default all metrics are enabled. +# It is possible, to prefix the metrics with "container_runtime_" and "crio_". +# For example, the metrics collector "operations" would be treated in the same +# way as "crio_operations" and "container_runtime_crio_operations". +# metrics_collectors = [ +# "operations", +# "operations_latency_microseconds_total", +# "operations_latency_microseconds", +# "operations_errors", +# "image_pulls_by_digest", +# "image_pulls_by_name", +# "image_pulls_by_name_skipped", +# "image_pulls_failures", +# "image_pulls_successes", +# "image_pulls_layer_size", +# "image_layer_reuse", +# "containers_oom_total", +# "containers_oom", +# "processes_defunct", +# "operations_total", +# "operations_latency_seconds", +# "operations_latency_seconds_total", +# "operations_errors_total", +# "image_pulls_bytes_total", +# "image_pulls_skipped_bytes_total", +# "image_pulls_failure_total", +# "image_pulls_success_total", +# "image_layer_reuse_total", +# "containers_oom_count_total", +# ] +# The port on which the metrics server will listen. +# metrics_port = 9090 + +# Local socket path to bind the metrics server to +# metrics_socket = "" + +# The certificate for the secure metrics server. +# If the certificate is not available on disk, then CRI-O will generate a +# self-signed one. CRI-O also watches for changes of this path and reloads the +# certificate on any modification event. +# metrics_cert = "" + +# The certificate key for the secure metrics server. +# Behaves in the same way as the metrics_cert. +# metrics_key = "" + +# A necessary configuration for OpenTelemetry trace data exporting +[crio.tracing] + +# Globally enable or disable exporting OpenTelemetry traces. +# enable_tracing = false + +# Address on which the gRPC trace collector listens on. +# tracing_endpoint = "0.0.0.0:4317" + +# Number of samples to collect per million spans. +# tracing_sampling_rate_per_million = 0 + +# Necessary information pertaining to container and pod stats reporting. +[crio.stats] + +# The number of seconds between collecting pod and container stats. +# If set to 0, the stats are collected on-demand instead. +# stats_collection_period = 0 \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/crio.d_noparams/05_crio.conf b/sensor/internal/utils/testdata/testCNI/crio.d_noparams/05_crio.conf new file mode 100644 index 00000000..8ff6bd59 --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/crio.d_noparams/05_crio.conf @@ -0,0 +1,570 @@ +# The CRI-O configuration file specifies all of the available configuration +# options and command-line flags for the crio(8) OCI Kubernetes Container Runtime +# daemon, but in a TOML format that can be more easily modified and versioned. +# +# Please refer to crio.conf(5) for details of all configuration options. + +# CRI-O supports partial configuration reload during runtime, which can be +# done by sending SIGHUP to the running process. Currently supported options +# are explicitly mentioned with: 'This option supports live configuration +# reload'. + +# CRI-O reads its storage defaults from the containers-storage.conf(5) file +# located at /etc/containers/storage.conf. Modify this storage configuration if +# you want to change the system's defaults. If you want to modify storage just +# for CRI-O, you can change the storage configuration options here. +[crio] + +# Path to the "root directory". CRI-O stores all of its data, including +# containers images, in this directory. +# root = "/home/abuild/.local/share/containers/storage" + +# Path to the "run directory". CRI-O stores all of its state in this directory. +# runroot = "/tmp/containers-user-399/containers" + +# Storage driver used to manage the storage of images and containers. Please +# refer to containers-storage.conf(5) to see all available storage drivers. +# storage_driver = "vfs" + +# List to pass options to the storage driver. Please refer to +# containers-storage.conf(5) to see all available storage options. +# storage_option = [ +# ] + +# The default log directory where all logs will go unless directly specified by +# the kubelet. The log directory specified must be an absolute directory. +# log_dir = "/var/log/crio/pods" + +# Location for CRI-O to lay down the temporary version file. +# It is used to check if crio wipe should wipe containers, which should +# always happen on a node reboot +# version_file = "/var/run/crio/version" + +# Location for CRI-O to lay down the persistent version file. +# It is used to check if crio wipe should wipe images, which should +# only happen when CRI-O has been upgraded +# version_file_persist = "/var/lib/crio/version" + +# InternalWipe is whether CRI-O should wipe containers and images after a reboot when the server starts. +# If set to false, one must use the external command 'crio wipe' to wipe the containers and images in these situations. +# internal_wipe = true + +# Location for CRI-O to lay down the clean shutdown file. +# It is used to check whether crio had time to sync before shutting down. +# If not found, crio wipe will clear the storage directory. +# clean_shutdown_file = "/var/lib/crio/clean.shutdown" + +# The crio.api table contains settings for the kubelet/gRPC interface. +[crio.api] + +# Path to AF_LOCAL socket on which CRI-O will listen. +# listen = "/var/run/crio/crio.sock" + +# IP address on which the stream server will listen. +# stream_address = "127.0.0.1" + +# The port on which the stream server will listen. If the port is set to "0", then +# CRI-O will allocate a random free port number. +# stream_port = "0" + +# Enable encrypted TLS transport of the stream server. +# stream_enable_tls = false + +# Length of time until open streams terminate due to lack of activity +# stream_idle_timeout = "" + +# Path to the x509 certificate file used to serve the encrypted stream. This +# file can change, and CRI-O will automatically pick up the changes within 5 +# minutes. +# stream_tls_cert = "" + +# Path to the key file used to serve the encrypted stream. This file can +# change and CRI-O will automatically pick up the changes within 5 minutes. +# stream_tls_key = "" + +# Path to the x509 CA(s) file used to verify and authenticate client +# communication with the encrypted stream. This file can change and CRI-O will +# automatically pick up the changes within 5 minutes. +# stream_tls_ca = "" + +# Maximum grpc send message size in bytes. If not set or <=0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_send_msg_size = 83886080 + +# Maximum grpc receive message size. If not set or <= 0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_recv_msg_size = 83886080 + +# The crio.runtime table contains settings pertaining to the OCI runtime used +# and options for how to set up and manage the OCI runtime. +[crio.runtime] + +# A list of ulimits to be set in containers by default, specified as +# "=:", for example: +# "nofile=1024:2048" +# If nothing is set here, settings will be inherited from the CRI-O daemon +# default_ulimits = [ +# ] + +# If true, the runtime will not use pivot_root, but instead use MS_MOVE. +# no_pivot = false + +# decryption_keys_path is the path where the keys required for +# image decryption are stored. This option supports live configuration reload. +# decryption_keys_path = "/etc/crio/keys/" + +# Path to the conmon binary, used for monitoring the OCI runtime. +# Will be searched for using $PATH if empty. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon = "" + +# Cgroup setting for conmon +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorCgroup. +# conmon_cgroup = "" + +# Environment variable list for the conmon process, used for passing necessary +# environment variables to conmon or the runtime. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon_env = [ +# ] + +# Additional environment variables to set for all the +# containers. These are overridden if set in the +# container image spec or in the container runtime configuration. +# default_env = [ +# ] + +# If true, SELinux will be used for pod separation on the host. +# selinux = false + +# Path to the seccomp.json profile which is used as the default seccomp profile +# for the runtime. If not specified, then the internal default seccomp profile +# will be used. This option supports live configuration reload. +# seccomp_profile = "" + +# Changes the meaning of an empty seccomp profile. By default +# (and according to CRI spec), an empty profile means unconfined. +# This option tells CRI-O to treat an empty profile as the default profile, +# which might increase security. +# seccomp_use_default_when_empty = true + +# Used to change the name of the default AppArmor profile of CRI-O. The default +# profile name is "crio-default". This profile only takes effect if the user +# does not specify a profile via the Kubernetes Pod's metadata annotation. If +# the profile is set to "unconfined", then this equals to disabling AppArmor. +# This option supports live configuration reload. +# apparmor_profile = "crio-default" + +# Path to the blockio class configuration file for configuring +# the cgroup blockio controller. +# blockio_config_file = "" + +# Used to change irqbalance service config file path which is used for configuring +# irqbalance daemon. +# irqbalance_config_file = "/etc/sysconfig/irqbalance" + +# Path to the RDT configuration file for configuring the resctrl pseudo-filesystem. +# This option supports live configuration reload. +# rdt_config_file = "" + +# Cgroup management implementation used for the runtime. +# cgroup_manager = "systemd" + +# Specify whether the image pull must be performed in a separate cgroup. +# separate_pull_cgroup = "" + +# List of default capabilities for containers. If it is empty or commented out, +# only the capabilities defined in the containers json file by the user/kube +# will be added. +# default_capabilities = [ +# "CHOWN", +# "DAC_OVERRIDE", +# "FSETID", +# "FOWNER", +# "SETGID", +# "SETUID", +# "SETPCAP", +# "NET_BIND_SERVICE", +# "KILL", +# ] + +# List of default sysctls. If it is empty or commented out, only the sysctls +# defined in the container json file by the user/kube will be added. +# default_sysctls = [ +# ] + +# List of devices on the host that a +# user can specify with the "io.kubernetes.cri-o.Devices" allowed annotation. +# allowed_devices = [ +# "/dev/fuse", +# ] + +# List of additional devices. specified as +# "::", for example: "--device=/dev/sdc:/dev/xvdc:rwm". +# If it is empty or commented out, only the devices +# defined in the container json file by the user/kube will be added. +# additional_devices = [ +# ] + +# List of directories to scan for CDI Spec files. +# cdi_spec_dirs = [ +# "/etc/cdi", +# "/var/run/cdi", +# ] + +# Change the default behavior of setting container devices uid/gid from CRI's +# SecurityContext (RunAsUser/RunAsGroup) instead of taking host's uid/gid. +# Defaults to false. +# device_ownership_from_security_context = false + +# Path to OCI hooks directories for automatically executed hooks. If one of the +# directories does not exist, then CRI-O will automatically skip them. +# hooks_dir = [ +# "/usr/share/containers/oci/hooks.d", +# ] + +# Path to the file specifying the defaults mounts for each container. The +# format of the config is /SRC:/DST, one mount per line. Notice that CRI-O reads +# its default mounts from the following two files: +# +# 1) /etc/containers/mounts.conf (i.e., default_mounts_file): This is the +# override file, where users can either add in their own default mounts, or +# override the default mounts shipped with the package. +# +# 2) /usr/share/containers/mounts.conf: This is the default file read for +# mounts. If you want CRI-O to read from a different, specific mounts file, +# you can change the default_mounts_file. Note, if this is done, CRI-O will +# only add mounts it finds in this file. +# +# default_mounts_file = "" + +# Maximum number of processes allowed in a container. +# This option is deprecated. The Kubelet flag '--pod-pids-limit' should be used instead. +# pids_limit = 0 + +# Maximum sized allowed for the container log file. Negative numbers indicate +# that no size limit is imposed. If it is positive, it must be >= 8192 to +# match/exceed conmon's read buffer. The file is truncated and re-opened so the +# limit is never exceeded. This option is deprecated. The Kubelet flag '--container-log-max-size' should be used instead. +# log_size_max = -1 + +# Whether container output should be logged to journald in addition to the kuberentes log file +# log_to_journald = false + +# Path to directory in which container exit files are written to by conmon. +# container_exits_dir = "/var/run/crio/exits" + +# Path to directory for container attach sockets. +# container_attach_socket_dir = "/var/run/crio" + +# The prefix to use for the source of the bind mounts. +# bind_mount_prefix = "" + +# If set to true, all containers will run in read-only mode. +# read_only = false + +# Changes the verbosity of the logs based on the level it is set to. Options +# are fatal, panic, error, warn, info, debug and trace. This option supports +# live configuration reload. +# log_level = "info" + +# Filter the log messages by the provided regular expression. +# This option supports live configuration reload. +# log_filter = "" + +# The UID mappings for the user namespace of each container. A range is +# specified in the form containerUID:HostUID:Size. Multiple ranges must be +# separated by comma. +# uid_mappings = "" + +# The GID mappings for the user namespace of each container. A range is +# specified in the form containerGID:HostGID:Size. Multiple ranges must be +# separated by comma. +# gid_mappings = "" + +# If set, CRI-O will reject any attempt to map host UIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_uid = -1 + +# If set, CRI-O will reject any attempt to map host GIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_gid = -1 + +# The minimal amount of time in seconds to wait before issuing a timeout +# regarding the proper termination of the container. The lowest possible +# value is 30s, whereas lower values are not considered by CRI-O. +# ctr_stop_timeout = 30 + +# drop_infra_ctr determines whether CRI-O drops the infra container +# when a pod does not have a private PID namespace, and does not use +# a kernel separating runtime (like kata). +# It requires manage_ns_lifecycle to be true. +# drop_infra_ctr = true + +# infra_ctr_cpuset determines what CPUs will be used to run infra containers. +# You can use linux CPU list format to specify desired CPUs. +# To get better isolation for guaranteed pods, set this parameter to be equal to kubelet reserved-cpus. +# infra_ctr_cpuset = "" + +# The directory where the state of the managed namespaces gets tracked. +# Only used when manage_ns_lifecycle is true. +# namespaces_dir = "/var/run" + +# pinns_path is the path to find the pinns binary, which is needed to manage namespace lifecycle +# pinns_path = "" + +# default_runtime is the _name_ of the OCI runtime to be used as the default. +# The name is matched against the runtimes map below. If this value is changed, +# the corresponding existing entry from the runtimes map below will be ignored. +# default_runtime = "runc" + +# A list of paths that, when absent from the host, +# will cause a container creation to fail (as opposed to the current behavior being created as a directory). +# This option is to protect from source locations whose existence as a directory could jepordize the health of the node, and whose +# creation as a file is not desired either. +# An example is /etc/hostname, which will cause failures on reboot if it's created as a directory, but often doesn't exist because +# the hostname is being managed dynamically. +# absent_mount_sources_to_reject = [ +# ] + +# The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. +# The runtime to use is picked based on the runtime handler provided by the CRI. +# If no runtime handler is provided, the runtime will be picked based on the level +# of trust of the workload. Each entry in the table should follow the format: +# +#[crio.runtime.runtimes.runtime-handler] +# runtime_path = "/path/to/the/executable" +# runtime_type = "oci" +# runtime_root = "/path/to/the/root" +# privileged_without_host_devices = false +# allowed_annotations = [] +# Where: +# - runtime-handler: name used to identify the runtime +# - runtime_path (optional, string): absolute path to the runtime executable in +# the host filesystem. If omitted, the runtime-handler identifier should match +# the runtime executable name, and the runtime executable should be placed +# in $PATH. +# - runtime_type (optional, string): type of runtime, one of: "oci", "vm". If +# omitted, an "oci" runtime is assumed. +# - runtime_root (optional, string): root directory for storage of containers +# state. +# - runtime_config_path (optional, string): the path for the runtime configuration +# file. This can only be used with when using the VM runtime_type. +# - privileged_without_host_devices (optional, bool): an option for restricting +# host devices from being passed to privileged containers. +# - allowed_annotations (optional, array of strings): an option for specifying +# a list of experimental annotations that this runtime handler is allowed to process. +# The currently recognized values are: +# "io.kubernetes.cri-o.userns-mode" for configuring a user namespace for the pod. +# "io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw" for mounting cgroups writably when set to "true". +# "io.kubernetes.cri-o.Devices" for configuring devices for the pod. +# "io.kubernetes.cri-o.ShmSize" for configuring the size of /dev/shm. +# "io.kubernetes.cri-o.UnifiedCgroup.$CTR_NAME" for configuring the cgroup v2 unified block for a container. +# "io.containers.trace-syscall" for tracing syscalls via the OCI seccomp BPF hook. +# "io.kubernetes.cri.rdt-class" for setting the RDT class of a container +# - monitor_exec_cgroup (optional, string): if set to "container", indicates exec probes +# should be moved to the container's cgroup + + +# [crio.runtime.runtimes.runc] +# runtime_path = "" +# runtime_type = "oci" +# runtime_root = "/run/runc" +# runtime_config_path = "" + +# +# allowed_annotations = [ +# "io.containers.trace-syscall", +# ] +# +# monitor_path = "" +# +# monitor_env = [ +# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +# ] +# +# monitor_cgroup = "system.slice" +# monitor_exec_cgroup = "" +# + +# crun is a fast and lightweight fully featured OCI runtime and C library for +# running containers +#[crio.runtime.runtimes.crun] + +# Kata Containers is an OCI runtime, where containers are run inside lightweight +# VMs. Kata provides additional isolation towards the host, minimizing the host attack +# surface and mitigating the consequences of containers breakout. + +# Kata Containers with the default configured VMM +#[crio.runtime.runtimes.kata-runtime] + +# Kata Containers with the QEMU VMM +#[crio.runtime.runtimes.kata-qemu] + +# Kata Containers with the Firecracker VMM +#[crio.runtime.runtimes.kata-fc] + +# The workloads table defines ways to customize containers with different resources +# that work based on annotations, rather than the CRI. +# Note, the behavior of this table is EXPERIMENTAL and may change at any time. +# Each workload, has a name, activation_annotation, annotation_prefix and set of resources it supports mutating. +# The currently supported resources are "cpu" (to configure the cpu shares) and "cpuset" to configure the cpuset. +# Each resource can have a default value specified, or be empty. +# For a container to opt-into this workload, the pod should be configured with the annotation $activation_annotation (key only, value is ignored). +# To customize per-container, an annotation of the form $annotation_prefix.$resource/$ctrName = "value" can be specified +# signifying for that resource type to override the default value. +# If the annotation_prefix is not present, every container in the pod will be given the default values. +# Example: +# [crio.runtime.workloads.workload-type] +# activation_annotation = "io.crio/workload" +# annotation_prefix = "io.crio.workload-type" +# [crio.runtime.workloads.workload-type.resources] +# cpuset = 0 +# cpushares = "0-1" +# Where: +# The workload name is workload-type. +# To specify, the pod must have the "io.crio.workload" annotation (this is a precise string match). +# This workload supports setting cpuset and cpu resources. +# annotation_prefix is used to customize the different resources. +# To configure the cpu shares a container gets in the example above, the pod would have to have the following annotation: +# "io.crio.workload-type/$container_name = {"cpushares": "value"}" +# + +# The crio.image table contains settings pertaining to the management of OCI images. +# +# CRI-O reads its configured registries defaults from the system wide +# containers-registries.conf(5) located in /etc/containers/registries.conf. If +# you want to modify just CRI-O, you can change the registries configuration in +# this file. Otherwise, leave insecure_registries and registries commented out to +# use the system's defaults from /etc/containers/registries.conf. +[crio.image] + +# Default transport for pulling images from a remote container storage. +# default_transport = "docker://" + +# The path to a file containing credentials necessary for pulling images from +# secure registries. The file is similar to that of /var/lib/kubelet/config.json +# global_auth_file = "" + +# The image used to instantiate infra containers. +# This option supports live configuration reload. +# pause_image = "registry.k8s.io/pause:3.6" + +# The path to a file containing credentials specific for pulling the pause_image from +# above. The file is similar to that of /var/lib/kubelet/config.json +# This option supports live configuration reload. +# pause_image_auth_file = "" + +# The command to run to have a container stay in the paused state. +# When explicitly set to "", it will fallback to the entrypoint and command +# specified in the pause image. When commented out, it will fallback to the +# default: "/pause". This option supports live configuration reload. +# pause_command = "/pause" + +# Path to the file which decides what sort of policy we use when deciding +# whether or not to trust an image that we've pulled. It is not recommended that +# this option be used, as the default behavior of using the system-wide default +# policy (i.e., /etc/containers/policy.json) is most often preferred. Please +# refer to containers-policy.json(5) for more details. +# signature_policy = "" + +# List of registries to skip TLS verification for pulling images. Please +# consider configuring the registries via /etc/containers/registries.conf before +# changing them here. +# insecure_registries = [ +# ] + +# Controls how image volumes are handled. The valid values are mkdir, bind and +# ignore; the latter will ignore volumes entirely. +# image_volumes = "mkdir" + +# Temporary directory to use for storing big files +# big_files_temporary_dir = "" + +# The crio.network table containers settings pertaining to the management of +# CNI plugins. +[crio.network] + +# The default CNI network name to be selected. If not set or "", then +# CRI-O will pick-up the first one found in network_dir. +# cni_default_network = "" + +# Path to the directory where CNI configuration files are located. +network_dir = "" + +# Paths to directories where CNI plugin binaries are located. +plugin_dirs = [ + +] + +# A necessary configuration for Prometheus based metrics retrieval +[crio.metrics] + +# Globally enable or disable metrics support. +# enable_metrics = false + +# Specify enabled metrics collectors. +# Per default all metrics are enabled. +# It is possible, to prefix the metrics with "container_runtime_" and "crio_". +# For example, the metrics collector "operations" would be treated in the same +# way as "crio_operations" and "container_runtime_crio_operations". +# metrics_collectors = [ +# "operations", +# "operations_latency_microseconds_total", +# "operations_latency_microseconds", +# "operations_errors", +# "image_pulls_by_digest", +# "image_pulls_by_name", +# "image_pulls_by_name_skipped", +# "image_pulls_failures", +# "image_pulls_successes", +# "image_pulls_layer_size", +# "image_layer_reuse", +# "containers_oom_total", +# "containers_oom", +# "processes_defunct", +# "operations_total", +# "operations_latency_seconds", +# "operations_latency_seconds_total", +# "operations_errors_total", +# "image_pulls_bytes_total", +# "image_pulls_skipped_bytes_total", +# "image_pulls_failure_total", +# "image_pulls_success_total", +# "image_layer_reuse_total", +# "containers_oom_count_total", +# ] +# The port on which the metrics server will listen. +# metrics_port = 9090 + +# Local socket path to bind the metrics server to +# metrics_socket = "" + +# The certificate for the secure metrics server. +# If the certificate is not available on disk, then CRI-O will generate a +# self-signed one. CRI-O also watches for changes of this path and reloads the +# certificate on any modification event. +# metrics_cert = "" + +# The certificate key for the secure metrics server. +# Behaves in the same way as the metrics_cert. +# metrics_key = "" + +# A necessary configuration for OpenTelemetry trace data exporting +[crio.tracing] + +# Globally enable or disable exporting OpenTelemetry traces. +# enable_tracing = false + +# Address on which the gRPC trace collector listens on. +# tracing_endpoint = "0.0.0.0:4317" + +# Number of samples to collect per million spans. +# tracing_sampling_rate_per_million = 0 + +# Necessary information pertaining to container and pod stats reporting. +[crio.stats] + +# The number of seconds between collecting pod and container stats. +# If set to 0, the stats are collected on-demand instead. +# stats_collection_period = 0 \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/crio.d_noparams/06_crio.conf b/sensor/internal/utils/testdata/testCNI/crio.d_noparams/06_crio.conf new file mode 100644 index 00000000..2035c6e0 --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/crio.d_noparams/06_crio.conf @@ -0,0 +1,570 @@ +# The CRI-O configuration file specifies all of the available configuration +# options and command-line flags for the crio(8) OCI Kubernetes Container Runtime +# daemon, but in a TOML format that can be more easily modified and versioned. +# +# Please refer to crio.conf(5) for details of all configuration options. + +# CRI-O supports partial configuration reload during runtime, which can be +# done by sending SIGHUP to the running process. Currently supported options +# are explicitly mentioned with: 'This option supports live configuration +# reload'. + +# CRI-O reads its storage defaults from the containers-storage.conf(5) file +# located at /etc/containers/storage.conf. Modify this storage configuration if +# you want to change the system's defaults. If you want to modify storage just +# for CRI-O, you can change the storage configuration options here. +[crio] + +# Path to the "root directory". CRI-O stores all of its data, including +# containers images, in this directory. +# root = "/home/abuild/.local/share/containers/storage" + +# Path to the "run directory". CRI-O stores all of its state in this directory. +# runroot = "/tmp/containers-user-399/containers" + +# Storage driver used to manage the storage of images and containers. Please +# refer to containers-storage.conf(5) to see all available storage drivers. +# storage_driver = "vfs" + +# List to pass options to the storage driver. Please refer to +# containers-storage.conf(5) to see all available storage options. +# storage_option = [ +# ] + +# The default log directory where all logs will go unless directly specified by +# the kubelet. The log directory specified must be an absolute directory. +# log_dir = "/var/log/crio/pods" + +# Location for CRI-O to lay down the temporary version file. +# It is used to check if crio wipe should wipe containers, which should +# always happen on a node reboot +# version_file = "/var/run/crio/version" + +# Location for CRI-O to lay down the persistent version file. +# It is used to check if crio wipe should wipe images, which should +# only happen when CRI-O has been upgraded +# version_file_persist = "/var/lib/crio/version" + +# InternalWipe is whether CRI-O should wipe containers and images after a reboot when the server starts. +# If set to false, one must use the external command 'crio wipe' to wipe the containers and images in these situations. +# internal_wipe = true + +# Location for CRI-O to lay down the clean shutdown file. +# It is used to check whether crio had time to sync before shutting down. +# If not found, crio wipe will clear the storage directory. +# clean_shutdown_file = "/var/lib/crio/clean.shutdown" + +# The crio.api table contains settings for the kubelet/gRPC interface. +[crio.api] + +# Path to AF_LOCAL socket on which CRI-O will listen. +# listen = "/var/run/crio/crio.sock" + +# IP address on which the stream server will listen. +# stream_address = "127.0.0.1" + +# The port on which the stream server will listen. If the port is set to "0", then +# CRI-O will allocate a random free port number. +# stream_port = "0" + +# Enable encrypted TLS transport of the stream server. +# stream_enable_tls = false + +# Length of time until open streams terminate due to lack of activity +# stream_idle_timeout = "" + +# Path to the x509 certificate file used to serve the encrypted stream. This +# file can change, and CRI-O will automatically pick up the changes within 5 +# minutes. +# stream_tls_cert = "" + +# Path to the key file used to serve the encrypted stream. This file can +# change and CRI-O will automatically pick up the changes within 5 minutes. +# stream_tls_key = "" + +# Path to the x509 CA(s) file used to verify and authenticate client +# communication with the encrypted stream. This file can change and CRI-O will +# automatically pick up the changes within 5 minutes. +# stream_tls_ca = "" + +# Maximum grpc send message size in bytes. If not set or <=0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_send_msg_size = 83886080 + +# Maximum grpc receive message size. If not set or <= 0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_recv_msg_size = 83886080 + +# The crio.runtime table contains settings pertaining to the OCI runtime used +# and options for how to set up and manage the OCI runtime. +[crio.runtime] + +# A list of ulimits to be set in containers by default, specified as +# "=:", for example: +# "nofile=1024:2048" +# If nothing is set here, settings will be inherited from the CRI-O daemon +# default_ulimits = [ +# ] + +# If true, the runtime will not use pivot_root, but instead use MS_MOVE. +# no_pivot = false + +# decryption_keys_path is the path where the keys required for +# image decryption are stored. This option supports live configuration reload. +# decryption_keys_path = "/etc/crio/keys/" + +# Path to the conmon binary, used for monitoring the OCI runtime. +# Will be searched for using $PATH if empty. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon = "" + +# Cgroup setting for conmon +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorCgroup. +# conmon_cgroup = "" + +# Environment variable list for the conmon process, used for passing necessary +# environment variables to conmon or the runtime. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon_env = [ +# ] + +# Additional environment variables to set for all the +# containers. These are overridden if set in the +# container image spec or in the container runtime configuration. +# default_env = [ +# ] + +# If true, SELinux will be used for pod separation on the host. +# selinux = false + +# Path to the seccomp.json profile which is used as the default seccomp profile +# for the runtime. If not specified, then the internal default seccomp profile +# will be used. This option supports live configuration reload. +# seccomp_profile = "" + +# Changes the meaning of an empty seccomp profile. By default +# (and according to CRI spec), an empty profile means unconfined. +# This option tells CRI-O to treat an empty profile as the default profile, +# which might increase security. +# seccomp_use_default_when_empty = true + +# Used to change the name of the default AppArmor profile of CRI-O. The default +# profile name is "crio-default". This profile only takes effect if the user +# does not specify a profile via the Kubernetes Pod's metadata annotation. If +# the profile is set to "unconfined", then this equals to disabling AppArmor. +# This option supports live configuration reload. +# apparmor_profile = "crio-default" + +# Path to the blockio class configuration file for configuring +# the cgroup blockio controller. +# blockio_config_file = "" + +# Used to change irqbalance service config file path which is used for configuring +# irqbalance daemon. +# irqbalance_config_file = "/etc/sysconfig/irqbalance" + +# Path to the RDT configuration file for configuring the resctrl pseudo-filesystem. +# This option supports live configuration reload. +# rdt_config_file = "" + +# Cgroup management implementation used for the runtime. +# cgroup_manager = "systemd" + +# Specify whether the image pull must be performed in a separate cgroup. +# separate_pull_cgroup = "" + +# List of default capabilities for containers. If it is empty or commented out, +# only the capabilities defined in the containers json file by the user/kube +# will be added. +# default_capabilities = [ +# "CHOWN", +# "DAC_OVERRIDE", +# "FSETID", +# "FOWNER", +# "SETGID", +# "SETUID", +# "SETPCAP", +# "NET_BIND_SERVICE", +# "KILL", +# ] + +# List of default sysctls. If it is empty or commented out, only the sysctls +# defined in the container json file by the user/kube will be added. +# default_sysctls = [ +# ] + +# List of devices on the host that a +# user can specify with the "io.kubernetes.cri-o.Devices" allowed annotation. +# allowed_devices = [ +# "/dev/fuse", +# ] + +# List of additional devices. specified as +# "::", for example: "--device=/dev/sdc:/dev/xvdc:rwm". +# If it is empty or commented out, only the devices +# defined in the container json file by the user/kube will be added. +# additional_devices = [ +# ] + +# List of directories to scan for CDI Spec files. +# cdi_spec_dirs = [ +# "/etc/cdi", +# "/var/run/cdi", +# ] + +# Change the default behavior of setting container devices uid/gid from CRI's +# SecurityContext (RunAsUser/RunAsGroup) instead of taking host's uid/gid. +# Defaults to false. +# device_ownership_from_security_context = false + +# Path to OCI hooks directories for automatically executed hooks. If one of the +# directories does not exist, then CRI-O will automatically skip them. +# hooks_dir = [ +# "/usr/share/containers/oci/hooks.d", +# ] + +# Path to the file specifying the defaults mounts for each container. The +# format of the config is /SRC:/DST, one mount per line. Notice that CRI-O reads +# its default mounts from the following two files: +# +# 1) /etc/containers/mounts.conf (i.e., default_mounts_file): This is the +# override file, where users can either add in their own default mounts, or +# override the default mounts shipped with the package. +# +# 2) /usr/share/containers/mounts.conf: This is the default file read for +# mounts. If you want CRI-O to read from a different, specific mounts file, +# you can change the default_mounts_file. Note, if this is done, CRI-O will +# only add mounts it finds in this file. +# +# default_mounts_file = "" + +# Maximum number of processes allowed in a container. +# This option is deprecated. The Kubelet flag '--pod-pids-limit' should be used instead. +# pids_limit = 0 + +# Maximum sized allowed for the container log file. Negative numbers indicate +# that no size limit is imposed. If it is positive, it must be >= 8192 to +# match/exceed conmon's read buffer. The file is truncated and re-opened so the +# limit is never exceeded. This option is deprecated. The Kubelet flag '--container-log-max-size' should be used instead. +# log_size_max = -1 + +# Whether container output should be logged to journald in addition to the kuberentes log file +# log_to_journald = false + +# Path to directory in which container exit files are written to by conmon. +# container_exits_dir = "/var/run/crio/exits" + +# Path to directory for container attach sockets. +# container_attach_socket_dir = "/var/run/crio" + +# The prefix to use for the source of the bind mounts. +# bind_mount_prefix = "" + +# If set to true, all containers will run in read-only mode. +# read_only = false + +# Changes the verbosity of the logs based on the level it is set to. Options +# are fatal, panic, error, warn, info, debug and trace. This option supports +# live configuration reload. +# log_level = "info" + +# Filter the log messages by the provided regular expression. +# This option supports live configuration reload. +# log_filter = "" + +# The UID mappings for the user namespace of each container. A range is +# specified in the form containerUID:HostUID:Size. Multiple ranges must be +# separated by comma. +# uid_mappings = "" + +# The GID mappings for the user namespace of each container. A range is +# specified in the form containerGID:HostGID:Size. Multiple ranges must be +# separated by comma. +# gid_mappings = "" + +# If set, CRI-O will reject any attempt to map host UIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_uid = -1 + +# If set, CRI-O will reject any attempt to map host GIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_gid = -1 + +# The minimal amount of time in seconds to wait before issuing a timeout +# regarding the proper termination of the container. The lowest possible +# value is 30s, whereas lower values are not considered by CRI-O. +# ctr_stop_timeout = 30 + +# drop_infra_ctr determines whether CRI-O drops the infra container +# when a pod does not have a private PID namespace, and does not use +# a kernel separating runtime (like kata). +# It requires manage_ns_lifecycle to be true. +# drop_infra_ctr = true + +# infra_ctr_cpuset determines what CPUs will be used to run infra containers. +# You can use linux CPU list format to specify desired CPUs. +# To get better isolation for guaranteed pods, set this parameter to be equal to kubelet reserved-cpus. +# infra_ctr_cpuset = "" + +# The directory where the state of the managed namespaces gets tracked. +# Only used when manage_ns_lifecycle is true. +# namespaces_dir = "/var/run" + +# pinns_path is the path to find the pinns binary, which is needed to manage namespace lifecycle +# pinns_path = "" + +# default_runtime is the _name_ of the OCI runtime to be used as the default. +# The name is matched against the runtimes map below. If this value is changed, +# the corresponding existing entry from the runtimes map below will be ignored. +# default_runtime = "runc" + +# A list of paths that, when absent from the host, +# will cause a container creation to fail (as opposed to the current behavior being created as a directory). +# This option is to protect from source locations whose existence as a directory could jepordize the health of the node, and whose +# creation as a file is not desired either. +# An example is /etc/hostname, which will cause failures on reboot if it's created as a directory, but often doesn't exist because +# the hostname is being managed dynamically. +# absent_mount_sources_to_reject = [ +# ] + +# The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. +# The runtime to use is picked based on the runtime handler provided by the CRI. +# If no runtime handler is provided, the runtime will be picked based on the level +# of trust of the workload. Each entry in the table should follow the format: +# +#[crio.runtime.runtimes.runtime-handler] +# runtime_path = "/path/to/the/executable" +# runtime_type = "oci" +# runtime_root = "/path/to/the/root" +# privileged_without_host_devices = false +# allowed_annotations = [] +# Where: +# - runtime-handler: name used to identify the runtime +# - runtime_path (optional, string): absolute path to the runtime executable in +# the host filesystem. If omitted, the runtime-handler identifier should match +# the runtime executable name, and the runtime executable should be placed +# in $PATH. +# - runtime_type (optional, string): type of runtime, one of: "oci", "vm". If +# omitted, an "oci" runtime is assumed. +# - runtime_root (optional, string): root directory for storage of containers +# state. +# - runtime_config_path (optional, string): the path for the runtime configuration +# file. This can only be used with when using the VM runtime_type. +# - privileged_without_host_devices (optional, bool): an option for restricting +# host devices from being passed to privileged containers. +# - allowed_annotations (optional, array of strings): an option for specifying +# a list of experimental annotations that this runtime handler is allowed to process. +# The currently recognized values are: +# "io.kubernetes.cri-o.userns-mode" for configuring a user namespace for the pod. +# "io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw" for mounting cgroups writably when set to "true". +# "io.kubernetes.cri-o.Devices" for configuring devices for the pod. +# "io.kubernetes.cri-o.ShmSize" for configuring the size of /dev/shm. +# "io.kubernetes.cri-o.UnifiedCgroup.$CTR_NAME" for configuring the cgroup v2 unified block for a container. +# "io.containers.trace-syscall" for tracing syscalls via the OCI seccomp BPF hook. +# "io.kubernetes.cri.rdt-class" for setting the RDT class of a container +# - monitor_exec_cgroup (optional, string): if set to "container", indicates exec probes +# should be moved to the container's cgroup + + +# [crio.runtime.runtimes.runc] +# runtime_path = "" +# runtime_type = "oci" +# runtime_root = "/run/runc" +# runtime_config_path = "" + +# +# allowed_annotations = [ +# "io.containers.trace-syscall", +# ] +# +# monitor_path = "" +# +# monitor_env = [ +# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +# ] +# +# monitor_cgroup = "system.slice" +# monitor_exec_cgroup = "" +# + +# crun is a fast and lightweight fully featured OCI runtime and C library for +# running containers +#[crio.runtime.runtimes.crun] + +# Kata Containers is an OCI runtime, where containers are run inside lightweight +# VMs. Kata provides additional isolation towards the host, minimizing the host attack +# surface and mitigating the consequences of containers breakout. + +# Kata Containers with the default configured VMM +#[crio.runtime.runtimes.kata-runtime] + +# Kata Containers with the QEMU VMM +#[crio.runtime.runtimes.kata-qemu] + +# Kata Containers with the Firecracker VMM +#[crio.runtime.runtimes.kata-fc] + +# The workloads table defines ways to customize containers with different resources +# that work based on annotations, rather than the CRI. +# Note, the behavior of this table is EXPERIMENTAL and may change at any time. +# Each workload, has a name, activation_annotation, annotation_prefix and set of resources it supports mutating. +# The currently supported resources are "cpu" (to configure the cpu shares) and "cpuset" to configure the cpuset. +# Each resource can have a default value specified, or be empty. +# For a container to opt-into this workload, the pod should be configured with the annotation $activation_annotation (key only, value is ignored). +# To customize per-container, an annotation of the form $annotation_prefix.$resource/$ctrName = "value" can be specified +# signifying for that resource type to override the default value. +# If the annotation_prefix is not present, every container in the pod will be given the default values. +# Example: +# [crio.runtime.workloads.workload-type] +# activation_annotation = "io.crio/workload" +# annotation_prefix = "io.crio.workload-type" +# [crio.runtime.workloads.workload-type.resources] +# cpuset = 0 +# cpushares = "0-1" +# Where: +# The workload name is workload-type. +# To specify, the pod must have the "io.crio.workload" annotation (this is a precise string match). +# This workload supports setting cpuset and cpu resources. +# annotation_prefix is used to customize the different resources. +# To configure the cpu shares a container gets in the example above, the pod would have to have the following annotation: +# "io.crio.workload-type/$container_name = {"cpushares": "value"}" +# + +# The crio.image table contains settings pertaining to the management of OCI images. +# +# CRI-O reads its configured registries defaults from the system wide +# containers-registries.conf(5) located in /etc/containers/registries.conf. If +# you want to modify just CRI-O, you can change the registries configuration in +# this file. Otherwise, leave insecure_registries and registries commented out to +# use the system's defaults from /etc/containers/registries.conf. +[crio.image] + +# Default transport for pulling images from a remote container storage. +# default_transport = "docker://" + +# The path to a file containing credentials necessary for pulling images from +# secure registries. The file is similar to that of /var/lib/kubelet/config.json +# global_auth_file = "" + +# The image used to instantiate infra containers. +# This option supports live configuration reload. +# pause_image = "registry.k8s.io/pause:3.6" + +# The path to a file containing credentials specific for pulling the pause_image from +# above. The file is similar to that of /var/lib/kubelet/config.json +# This option supports live configuration reload. +# pause_image_auth_file = "" + +# The command to run to have a container stay in the paused state. +# When explicitly set to "", it will fallback to the entrypoint and command +# specified in the pause image. When commented out, it will fallback to the +# default: "/pause". This option supports live configuration reload. +# pause_command = "/pause" + +# Path to the file which decides what sort of policy we use when deciding +# whether or not to trust an image that we've pulled. It is not recommended that +# this option be used, as the default behavior of using the system-wide default +# policy (i.e., /etc/containers/policy.json) is most often preferred. Please +# refer to containers-policy.json(5) for more details. +# signature_policy = "" + +# List of registries to skip TLS verification for pulling images. Please +# consider configuring the registries via /etc/containers/registries.conf before +# changing them here. +# insecure_registries = [ +# ] + +# Controls how image volumes are handled. The valid values are mkdir, bind and +# ignore; the latter will ignore volumes entirely. +# image_volumes = "mkdir" + +# Temporary directory to use for storing big files +# big_files_temporary_dir = "" + +# The crio.network table containers settings pertaining to the management of +# CNI plugins. +[crio.network] + +# The default CNI network name to be selected. If not set or "", then +# CRI-O will pick-up the first one found in network_dir. +# cni_default_network = "" + +# # Path to the directory where CNI configuration files are located. +# network_dir = "" + +# # Paths to directories where CNI plugin binaries are located. +# plugin_dirs = [ + +# ] + +# A necessary configuration for Prometheus based metrics retrieval +[crio.metrics] + +# Globally enable or disable metrics support. +# enable_metrics = false + +# Specify enabled metrics collectors. +# Per default all metrics are enabled. +# It is possible, to prefix the metrics with "container_runtime_" and "crio_". +# For example, the metrics collector "operations" would be treated in the same +# way as "crio_operations" and "container_runtime_crio_operations". +# metrics_collectors = [ +# "operations", +# "operations_latency_microseconds_total", +# "operations_latency_microseconds", +# "operations_errors", +# "image_pulls_by_digest", +# "image_pulls_by_name", +# "image_pulls_by_name_skipped", +# "image_pulls_failures", +# "image_pulls_successes", +# "image_pulls_layer_size", +# "image_layer_reuse", +# "containers_oom_total", +# "containers_oom", +# "processes_defunct", +# "operations_total", +# "operations_latency_seconds", +# "operations_latency_seconds_total", +# "operations_errors_total", +# "image_pulls_bytes_total", +# "image_pulls_skipped_bytes_total", +# "image_pulls_failure_total", +# "image_pulls_success_total", +# "image_layer_reuse_total", +# "containers_oom_count_total", +# ] +# The port on which the metrics server will listen. +# metrics_port = 9090 + +# Local socket path to bind the metrics server to +# metrics_socket = "" + +# The certificate for the secure metrics server. +# If the certificate is not available on disk, then CRI-O will generate a +# self-signed one. CRI-O also watches for changes of this path and reloads the +# certificate on any modification event. +# metrics_cert = "" + +# The certificate key for the secure metrics server. +# Behaves in the same way as the metrics_cert. +# metrics_key = "" + +# A necessary configuration for OpenTelemetry trace data exporting +[crio.tracing] + +# Globally enable or disable exporting OpenTelemetry traces. +# enable_tracing = false + +# Address on which the gRPC trace collector listens on. +# tracing_endpoint = "0.0.0.0:4317" + +# Number of samples to collect per million spans. +# tracing_sampling_rate_per_million = 0 + +# Necessary information pertaining to container and pod stats reporting. +[crio.stats] + +# The number of seconds between collecting pod and container stats. +# If set to 0, the stats are collected on-demand instead. +# stats_collection_period = 0 \ No newline at end of file diff --git a/sensor/internal/utils/testdata/testCNI/crio_noparams.conf b/sensor/internal/utils/testdata/testCNI/crio_noparams.conf new file mode 100644 index 00000000..b43431ae --- /dev/null +++ b/sensor/internal/utils/testdata/testCNI/crio_noparams.conf @@ -0,0 +1,570 @@ +# The CRI-O configuration file specifies all of the available configuration +# options and command-line flags for the crio(8) OCI Kubernetes Container Runtime +# daemon, but in a TOML format that can be more easily modified and versioned. +# +# Please refer to crio.conf(5) for details of all configuration options. + +# CRI-O supports partial configuration reload during runtime, which can be +# done by sending SIGHUP to the running process. Currently supported options +# are explicitly mentioned with: 'This option supports live configuration +# reload'. + +# CRI-O reads its storage defaults from the containers-storage.conf(5) file +# located at /etc/containers/storage.conf. Modify this storage configuration if +# you want to change the system's defaults. If you want to modify storage just +# for CRI-O, you can change the storage configuration options here. +[crio] + +# Path to the "root directory". CRI-O stores all of its data, including +# containers images, in this directory. +# root = "/home/abuild/.local/share/containers/storage" + +# Path to the "run directory". CRI-O stores all of its state in this directory. +# runroot = "/tmp/containers-user-399/containers" + +# Storage driver used to manage the storage of images and containers. Please +# refer to containers-storage.conf(5) to see all available storage drivers. +# storage_driver = "vfs" + +# List to pass options to the storage driver. Please refer to +# containers-storage.conf(5) to see all available storage options. +# storage_option = [ +# ] + +# The default log directory where all logs will go unless directly specified by +# the kubelet. The log directory specified must be an absolute directory. +# log_dir = "/var/log/crio/pods" + +# Location for CRI-O to lay down the temporary version file. +# It is used to check if crio wipe should wipe containers, which should +# always happen on a node reboot +# version_file = "/var/run/crio/version" + +# Location for CRI-O to lay down the persistent version file. +# It is used to check if crio wipe should wipe images, which should +# only happen when CRI-O has been upgraded +# version_file_persist = "/var/lib/crio/version" + +# InternalWipe is whether CRI-O should wipe containers and images after a reboot when the server starts. +# If set to false, one must use the external command 'crio wipe' to wipe the containers and images in these situations. +# internal_wipe = true + +# Location for CRI-O to lay down the clean shutdown file. +# It is used to check whether crio had time to sync before shutting down. +# If not found, crio wipe will clear the storage directory. +# clean_shutdown_file = "/var/lib/crio/clean.shutdown" + +# The crio.api table contains settings for the kubelet/gRPC interface. +[crio.api] + +# Path to AF_LOCAL socket on which CRI-O will listen. +# listen = "/var/run/crio/crio.sock" + +# IP address on which the stream server will listen. +# stream_address = "127.0.0.1" + +# The port on which the stream server will listen. If the port is set to "0", then +# CRI-O will allocate a random free port number. +# stream_port = "0" + +# Enable encrypted TLS transport of the stream server. +# stream_enable_tls = false + +# Length of time until open streams terminate due to lack of activity +# stream_idle_timeout = "" + +# Path to the x509 certificate file used to serve the encrypted stream. This +# file can change, and CRI-O will automatically pick up the changes within 5 +# minutes. +# stream_tls_cert = "" + +# Path to the key file used to serve the encrypted stream. This file can +# change and CRI-O will automatically pick up the changes within 5 minutes. +# stream_tls_key = "" + +# Path to the x509 CA(s) file used to verify and authenticate client +# communication with the encrypted stream. This file can change and CRI-O will +# automatically pick up the changes within 5 minutes. +# stream_tls_ca = "" + +# Maximum grpc send message size in bytes. If not set or <=0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_send_msg_size = 83886080 + +# Maximum grpc receive message size. If not set or <= 0, then CRI-O will default to 16 * 1024 * 1024. +# grpc_max_recv_msg_size = 83886080 + +# The crio.runtime table contains settings pertaining to the OCI runtime used +# and options for how to set up and manage the OCI runtime. +[crio.runtime] + +# A list of ulimits to be set in containers by default, specified as +# "=:", for example: +# "nofile=1024:2048" +# If nothing is set here, settings will be inherited from the CRI-O daemon +# default_ulimits = [ +# ] + +# If true, the runtime will not use pivot_root, but instead use MS_MOVE. +# no_pivot = false + +# decryption_keys_path is the path where the keys required for +# image decryption are stored. This option supports live configuration reload. +# decryption_keys_path = "/etc/crio/keys/" + +# Path to the conmon binary, used for monitoring the OCI runtime. +# Will be searched for using $PATH if empty. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon = "" + +# Cgroup setting for conmon +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorCgroup. +# conmon_cgroup = "" + +# Environment variable list for the conmon process, used for passing necessary +# environment variables to conmon or the runtime. +# This option is currently deprecated, and will be replaced with RuntimeHandler.MonitorEnv. +# conmon_env = [ +# ] + +# Additional environment variables to set for all the +# containers. These are overridden if set in the +# container image spec or in the container runtime configuration. +# default_env = [ +# ] + +# If true, SELinux will be used for pod separation on the host. +# selinux = false + +# Path to the seccomp.json profile which is used as the default seccomp profile +# for the runtime. If not specified, then the internal default seccomp profile +# will be used. This option supports live configuration reload. +# seccomp_profile = "" + +# Changes the meaning of an empty seccomp profile. By default +# (and according to CRI spec), an empty profile means unconfined. +# This option tells CRI-O to treat an empty profile as the default profile, +# which might increase security. +# seccomp_use_default_when_empty = true + +# Used to change the name of the default AppArmor profile of CRI-O. The default +# profile name is "crio-default". This profile only takes effect if the user +# does not specify a profile via the Kubernetes Pod's metadata annotation. If +# the profile is set to "unconfined", then this equals to disabling AppArmor. +# This option supports live configuration reload. +# apparmor_profile = "crio-default" + +# Path to the blockio class configuration file for configuring +# the cgroup blockio controller. +# blockio_config_file = "" + +# Used to change irqbalance service config file path which is used for configuring +# irqbalance daemon. +# irqbalance_config_file = "/etc/sysconfig/irqbalance" + +# Path to the RDT configuration file for configuring the resctrl pseudo-filesystem. +# This option supports live configuration reload. +# rdt_config_file = "" + +# Cgroup management implementation used for the runtime. +# cgroup_manager = "systemd" + +# Specify whether the image pull must be performed in a separate cgroup. +# separate_pull_cgroup = "" + +# List of default capabilities for containers. If it is empty or commented out, +# only the capabilities defined in the containers json file by the user/kube +# will be added. +# default_capabilities = [ +# "CHOWN", +# "DAC_OVERRIDE", +# "FSETID", +# "FOWNER", +# "SETGID", +# "SETUID", +# "SETPCAP", +# "NET_BIND_SERVICE", +# "KILL", +# ] + +# List of default sysctls. If it is empty or commented out, only the sysctls +# defined in the container json file by the user/kube will be added. +# default_sysctls = [ +# ] + +# List of devices on the host that a +# user can specify with the "io.kubernetes.cri-o.Devices" allowed annotation. +# allowed_devices = [ +# "/dev/fuse", +# ] + +# List of additional devices. specified as +# "::", for example: "--device=/dev/sdc:/dev/xvdc:rwm". +# If it is empty or commented out, only the devices +# defined in the container json file by the user/kube will be added. +# additional_devices = [ +# ] + +# List of directories to scan for CDI Spec files. +# cdi_spec_dirs = [ +# "/etc/cdi", +# "/var/run/cdi", +# ] + +# Change the default behavior of setting container devices uid/gid from CRI's +# SecurityContext (RunAsUser/RunAsGroup) instead of taking host's uid/gid. +# Defaults to false. +# device_ownership_from_security_context = false + +# Path to OCI hooks directories for automatically executed hooks. If one of the +# directories does not exist, then CRI-O will automatically skip them. +# hooks_dir = [ +# "/usr/share/containers/oci/hooks.d", +# ] + +# Path to the file specifying the defaults mounts for each container. The +# format of the config is /SRC:/DST, one mount per line. Notice that CRI-O reads +# its default mounts from the following two files: +# +# 1) /etc/containers/mounts.conf (i.e., default_mounts_file): This is the +# override file, where users can either add in their own default mounts, or +# override the default mounts shipped with the package. +# +# 2) /usr/share/containers/mounts.conf: This is the default file read for +# mounts. If you want CRI-O to read from a different, specific mounts file, +# you can change the default_mounts_file. Note, if this is done, CRI-O will +# only add mounts it finds in this file. +# +# default_mounts_file = "" + +# Maximum number of processes allowed in a container. +# This option is deprecated. The Kubelet flag '--pod-pids-limit' should be used instead. +# pids_limit = 0 + +# Maximum sized allowed for the container log file. Negative numbers indicate +# that no size limit is imposed. If it is positive, it must be >= 8192 to +# match/exceed conmon's read buffer. The file is truncated and re-opened so the +# limit is never exceeded. This option is deprecated. The Kubelet flag '--container-log-max-size' should be used instead. +# log_size_max = -1 + +# Whether container output should be logged to journald in addition to the kuberentes log file +# log_to_journald = false + +# Path to directory in which container exit files are written to by conmon. +# container_exits_dir = "/var/run/crio/exits" + +# Path to directory for container attach sockets. +# container_attach_socket_dir = "/var/run/crio" + +# The prefix to use for the source of the bind mounts. +# bind_mount_prefix = "" + +# If set to true, all containers will run in read-only mode. +# read_only = false + +# Changes the verbosity of the logs based on the level it is set to. Options +# are fatal, panic, error, warn, info, debug and trace. This option supports +# live configuration reload. +# log_level = "info" + +# Filter the log messages by the provided regular expression. +# This option supports live configuration reload. +# log_filter = "" + +# The UID mappings for the user namespace of each container. A range is +# specified in the form containerUID:HostUID:Size. Multiple ranges must be +# separated by comma. +# uid_mappings = "" + +# The GID mappings for the user namespace of each container. A range is +# specified in the form containerGID:HostGID:Size. Multiple ranges must be +# separated by comma. +# gid_mappings = "" + +# If set, CRI-O will reject any attempt to map host UIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_uid = -1 + +# If set, CRI-O will reject any attempt to map host GIDs below this value +# into user namespaces. A negative value indicates that no minimum is set, +# so specifying mappings will only be allowed for pods that run as UID 0. +# minimum_mappable_gid = -1 + +# The minimal amount of time in seconds to wait before issuing a timeout +# regarding the proper termination of the container. The lowest possible +# value is 30s, whereas lower values are not considered by CRI-O. +# ctr_stop_timeout = 30 + +# drop_infra_ctr determines whether CRI-O drops the infra container +# when a pod does not have a private PID namespace, and does not use +# a kernel separating runtime (like kata). +# It requires manage_ns_lifecycle to be true. +# drop_infra_ctr = true + +# infra_ctr_cpuset determines what CPUs will be used to run infra containers. +# You can use linux CPU list format to specify desired CPUs. +# To get better isolation for guaranteed pods, set this parameter to be equal to kubelet reserved-cpus. +# infra_ctr_cpuset = "" + +# The directory where the state of the managed namespaces gets tracked. +# Only used when manage_ns_lifecycle is true. +# namespaces_dir = "/var/run" + +# pinns_path is the path to find the pinns binary, which is needed to manage namespace lifecycle +# pinns_path = "" + +# default_runtime is the _name_ of the OCI runtime to be used as the default. +# The name is matched against the runtimes map below. If this value is changed, +# the corresponding existing entry from the runtimes map below will be ignored. +# default_runtime = "runc" + +# A list of paths that, when absent from the host, +# will cause a container creation to fail (as opposed to the current behavior being created as a directory). +# This option is to protect from source locations whose existence as a directory could jepordize the health of the node, and whose +# creation as a file is not desired either. +# An example is /etc/hostname, which will cause failures on reboot if it's created as a directory, but often doesn't exist because +# the hostname is being managed dynamically. +# absent_mount_sources_to_reject = [ +# ] + +# The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. +# The runtime to use is picked based on the runtime handler provided by the CRI. +# If no runtime handler is provided, the runtime will be picked based on the level +# of trust of the workload. Each entry in the table should follow the format: +# +#[crio.runtime.runtimes.runtime-handler] +# runtime_path = "/path/to/the/executable" +# runtime_type = "oci" +# runtime_root = "/path/to/the/root" +# privileged_without_host_devices = false +# allowed_annotations = [] +# Where: +# - runtime-handler: name used to identify the runtime +# - runtime_path (optional, string): absolute path to the runtime executable in +# the host filesystem. If omitted, the runtime-handler identifier should match +# the runtime executable name, and the runtime executable should be placed +# in $PATH. +# - runtime_type (optional, string): type of runtime, one of: "oci", "vm". If +# omitted, an "oci" runtime is assumed. +# - runtime_root (optional, string): root directory for storage of containers +# state. +# - runtime_config_path (optional, string): the path for the runtime configuration +# file. This can only be used with when using the VM runtime_type. +# - privileged_without_host_devices (optional, bool): an option for restricting +# host devices from being passed to privileged containers. +# - allowed_annotations (optional, array of strings): an option for specifying +# a list of experimental annotations that this runtime handler is allowed to process. +# The currently recognized values are: +# "io.kubernetes.cri-o.userns-mode" for configuring a user namespace for the pod. +# "io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw" for mounting cgroups writably when set to "true". +# "io.kubernetes.cri-o.Devices" for configuring devices for the pod. +# "io.kubernetes.cri-o.ShmSize" for configuring the size of /dev/shm. +# "io.kubernetes.cri-o.UnifiedCgroup.$CTR_NAME" for configuring the cgroup v2 unified block for a container. +# "io.containers.trace-syscall" for tracing syscalls via the OCI seccomp BPF hook. +# "io.kubernetes.cri.rdt-class" for setting the RDT class of a container +# - monitor_exec_cgroup (optional, string): if set to "container", indicates exec probes +# should be moved to the container's cgroup + + +# [crio.runtime.runtimes.runc] +# runtime_path = "" +# runtime_type = "oci" +# runtime_root = "/run/runc" +# runtime_config_path = "" + +# +# allowed_annotations = [ +# "io.containers.trace-syscall", +# ] +# +# monitor_path = "" +# +# monitor_env = [ +# "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +# ] +# +# monitor_cgroup = "system.slice" +# monitor_exec_cgroup = "" +# + +# crun is a fast and lightweight fully featured OCI runtime and C library for +# running containers +#[crio.runtime.runtimes.crun] + +# Kata Containers is an OCI runtime, where containers are run inside lightweight +# VMs. Kata provides additional isolation towards the host, minimizing the host attack +# surface and mitigating the consequences of containers breakout. + +# Kata Containers with the default configured VMM +#[crio.runtime.runtimes.kata-runtime] + +# Kata Containers with the QEMU VMM +#[crio.runtime.runtimes.kata-qemu] + +# Kata Containers with the Firecracker VMM +#[crio.runtime.runtimes.kata-fc] + +# The workloads table defines ways to customize containers with different resources +# that work based on annotations, rather than the CRI. +# Note, the behavior of this table is EXPERIMENTAL and may change at any time. +# Each workload, has a name, activation_annotation, annotation_prefix and set of resources it supports mutating. +# The currently supported resources are "cpu" (to configure the cpu shares) and "cpuset" to configure the cpuset. +# Each resource can have a default value specified, or be empty. +# For a container to opt-into this workload, the pod should be configured with the annotation $activation_annotation (key only, value is ignored). +# To customize per-container, an annotation of the form $annotation_prefix.$resource/$ctrName = "value" can be specified +# signifying for that resource type to override the default value. +# If the annotation_prefix is not present, every container in the pod will be given the default values. +# Example: +# [crio.runtime.workloads.workload-type] +# activation_annotation = "io.crio/workload" +# annotation_prefix = "io.crio.workload-type" +# [crio.runtime.workloads.workload-type.resources] +# cpuset = 0 +# cpushares = "0-1" +# Where: +# The workload name is workload-type. +# To specify, the pod must have the "io.crio.workload" annotation (this is a precise string match). +# This workload supports setting cpuset and cpu resources. +# annotation_prefix is used to customize the different resources. +# To configure the cpu shares a container gets in the example above, the pod would have to have the following annotation: +# "io.crio.workload-type/$container_name = {"cpushares": "value"}" +# + +# The crio.image table contains settings pertaining to the management of OCI images. +# +# CRI-O reads its configured registries defaults from the system wide +# containers-registries.conf(5) located in /etc/containers/registries.conf. If +# you want to modify just CRI-O, you can change the registries configuration in +# this file. Otherwise, leave insecure_registries and registries commented out to +# use the system's defaults from /etc/containers/registries.conf. +[crio.image] + +# Default transport for pulling images from a remote container storage. +# default_transport = "docker://" + +# The path to a file containing credentials necessary for pulling images from +# secure registries. The file is similar to that of /var/lib/kubelet/config.json +# global_auth_file = "" + +# The image used to instantiate infra containers. +# This option supports live configuration reload. +# pause_image = "registry.k8s.io/pause:3.6" + +# The path to a file containing credentials specific for pulling the pause_image from +# above. The file is similar to that of /var/lib/kubelet/config.json +# This option supports live configuration reload. +# pause_image_auth_file = "" + +# The command to run to have a container stay in the paused state. +# When explicitly set to "", it will fallback to the entrypoint and command +# specified in the pause image. When commented out, it will fallback to the +# default: "/pause". This option supports live configuration reload. +# pause_command = "/pause" + +# Path to the file which decides what sort of policy we use when deciding +# whether or not to trust an image that we've pulled. It is not recommended that +# this option be used, as the default behavior of using the system-wide default +# policy (i.e., /etc/containers/policy.json) is most often preferred. Please +# refer to containers-policy.json(5) for more details. +# signature_policy = "" + +# List of registries to skip TLS verification for pulling images. Please +# consider configuring the registries via /etc/containers/registries.conf before +# changing them here. +# insecure_registries = [ +# ] + +# Controls how image volumes are handled. The valid values are mkdir, bind and +# ignore; the latter will ignore volumes entirely. +# image_volumes = "mkdir" + +# Temporary directory to use for storing big files +# big_files_temporary_dir = "" + +# The crio.network table containers settings pertaining to the management of +# CNI plugins. +[crio.network] + +# The default CNI network name to be selected. If not set or "", then +# CRI-O will pick-up the first one found in network_dir. +# cni_default_network = "" + +# Path to the directory where CNI configuration files are located. +# network_dir = "/etc/cni/net.d/" + +# Paths to directories where CNI plugin binaries are located. +# plugin_dirs = [ +# "/opt/cni/bin/", "/rr/ff" +# ] + +# A necessary configuration for Prometheus based metrics retrieval +[crio.metrics] + +# Globally enable or disable metrics support. +# enable_metrics = false + +# Specify enabled metrics collectors. +# Per default all metrics are enabled. +# It is possible, to prefix the metrics with "container_runtime_" and "crio_". +# For example, the metrics collector "operations" would be treated in the same +# way as "crio_operations" and "container_runtime_crio_operations". +# metrics_collectors = [ +# "operations", +# "operations_latency_microseconds_total", +# "operations_latency_microseconds", +# "operations_errors", +# "image_pulls_by_digest", +# "image_pulls_by_name", +# "image_pulls_by_name_skipped", +# "image_pulls_failures", +# "image_pulls_successes", +# "image_pulls_layer_size", +# "image_layer_reuse", +# "containers_oom_total", +# "containers_oom", +# "processes_defunct", +# "operations_total", +# "operations_latency_seconds", +# "operations_latency_seconds_total", +# "operations_errors_total", +# "image_pulls_bytes_total", +# "image_pulls_skipped_bytes_total", +# "image_pulls_failure_total", +# "image_pulls_success_total", +# "image_layer_reuse_total", +# "containers_oom_count_total", +# ] +# The port on which the metrics server will listen. +# metrics_port = 9090 + +# Local socket path to bind the metrics server to +# metrics_socket = "" + +# The certificate for the secure metrics server. +# If the certificate is not available on disk, then CRI-O will generate a +# self-signed one. CRI-O also watches for changes of this path and reloads the +# certificate on any modification event. +# metrics_cert = "" + +# The certificate key for the secure metrics server. +# Behaves in the same way as the metrics_cert. +# metrics_key = "" + +# A necessary configuration for OpenTelemetry trace data exporting +[crio.tracing] + +# Globally enable or disable exporting OpenTelemetry traces. +# enable_tracing = false + +# Address on which the gRPC trace collector listens on. +# tracing_endpoint = "0.0.0.0:4317" + +# Number of samples to collect per million spans. +# tracing_sampling_rate_per_million = 0 + +# Necessary information pertaining to container and pod stats reporting. +[crio.stats] + +# The number of seconds between collecting pod and container stats. +# If set to 0, the stats are collected on-demand instead. +# stats_collection_period = 0 \ No newline at end of file diff --git a/sensor/internal/utils/testdata/test_1 b/sensor/internal/utils/testdata/test_1 new file mode 100644 index 00000000..a7fb2bd2 --- /dev/null +++ b/sensor/internal/utils/testdata/test_1 @@ -0,0 +1 @@ +not empty file \ No newline at end of file diff --git a/sensor/internal/utils/testdata/test_2 b/sensor/internal/utils/testdata/test_2 new file mode 100755 index 00000000..e69de29b diff --git a/sensor/internal/utils/utils.go b/sensor/internal/utils/utils.go new file mode 100644 index 00000000..9d338bd7 --- /dev/null +++ b/sensor/internal/utils/utils.go @@ -0,0 +1,145 @@ +package utils + +import ( + "context" + "errors" + "os" + "path" + "syscall" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" + ds "github.com/kubescape/node-agent/sensor/datastructures" +) + +var ( + ErrNotUnixFS = errors.New("operation not supported by the file system") +) + +// ReadFileOnHostFileSystem reads a file on the host file system. +func ReadFileOnHostFileSystem(fileName string) ([]byte, error) { + logger.L().Debug("reading file on host file system", helpers.String("path", HostPath(fileName))) + return os.ReadFile(HostPath(fileName)) +} + +func HostPath(filePath string) string { + return path.Join(HostFileSystemDefaultLocation, filePath) +} + +// GetFilePermissions returns file permissions as int. +// On filesystem error, it returns the error as is. +func GetFilePermissions(filePath string) (int, error) { + info, err := os.Stat(filePath) + if err != nil { + return 0, err + } + + permInt := int(info.Mode().Perm()) + + return permInt, nil +} + +// GetFileUNIXOwnership returns the user id and group of a file. +// On error, it return values of -1 for the ids. +// On filesystem error, it returns the error as is. +// If the filesystem not support UNIX ownership (like FAT), it returns ErrNotUnixFS. +func GetFileUNIXOwnership(filePath string) (int64, int64, error) { + info, err := os.Stat(filePath) + if err != nil { + return -1, -1, err + } + + asUnix, ok := info.Sys().(*syscall.Stat_t) + if !ok { + return -1, -1, ErrNotUnixFS + } + + user := int64(asUnix.Uid) + group := int64(asUnix.Gid) + + return user, group, nil +} + +// IsPathExists returns true if a given path exist and false otherwise +func IsPathExists(filename string) bool { + _, err := os.Stat(filename) + return !os.IsNotExist(err) +} + +// MakeFileInfo returns a `ds.FileInfo` object for given path +// If `readContent` is set to `true`, it adds the file content +// On access error, it returns the error as is +func MakeFileInfo(filePath string, readContent bool) (*ds.FileInfo, error) { + ret := ds.FileInfo{Path: filePath} + + logger.L().Debug("making file info", helpers.String("path", filePath)) + + // Permissions + perms, err := GetFilePermissions(filePath) + if err != nil { + return nil, err + } + ret.Permissions = perms + + // Ownership + uid, gid, err := GetFileUNIXOwnership(filePath) + ret.Ownership = &ds.FileOwnership{UID: uid, GID: gid} + if err != nil { + ret.Ownership.Err = err.Error() + } + + // Content + if readContent { + content, err := os.ReadFile(filePath) + if err != nil { + return nil, err + } + ret.Content = content + } + + return &ret, nil +} + +// MakeChangedRootFileInfo makes a file info object +// for the given path on the given root directory. +func MakeChangedRootFileInfo(ctx context.Context, rootDir string, filePath string, readContent bool) (*ds.FileInfo, error) { + fullPath := path.Join(rootDir, filePath) + obj, err := MakeFileInfo(fullPath, readContent) + + if err != nil { + return obj, err + } + + // Remove `rootDir` from path + obj.Path = filePath + + // Username + username, err := getUserName(obj.Ownership.UID, rootDir) + obj.Ownership.Username = username + + if err != nil { + logger.L().Ctx(ctx).Warning("MakeHostFileInfo", helpers.Error(err)) + } + + // Groupname + groupname, err := getGroupName(obj.Ownership.GID, rootDir) + obj.Ownership.Groupname = groupname + + if err != nil { + logger.L().Ctx(ctx).Warning("MakeHostFileInfo", helpers.Error(err)) + } + + return obj, nil +} + +// MakeContaineredFileInfo makes a file info object +// for a given process file system view. +func MakeContaineredFileInfo(ctx context.Context, p *ProcessDetails, filePath string, readContent bool) (*ds.FileInfo, error) { + return MakeChangedRootFileInfo(ctx, p.RootDir(), filePath, readContent) +} + +// MakeHostFileInfo makes a file info object +// for the given path on the host file system. +func makeHostFileInfo(ctx context.Context, filePath string, readContent bool) (*ds.FileInfo, error) { + return MakeChangedRootFileInfo(ctx, HostFileSystemDefaultLocation, filePath, readContent) +} diff --git a/sensor/internal/utils/utils_test.go b/sensor/internal/utils/utils_test.go new file mode 100644 index 00000000..ef9304eb --- /dev/null +++ b/sensor/internal/utils/utils_test.go @@ -0,0 +1,139 @@ +package utils + +import ( + "context" + "reflect" + "testing" + + "github.com/kubescape/node-agent/sensor/datastructures" + ds "github.com/kubescape/node-agent/sensor/datastructures" + "github.com/stretchr/testify/assert" +) + +func Test_GetFileUNIXOwnership(t *testing.T) { + uid_tests := []struct { + name string + filePath string + expected [2]int64 + }{ + { + name: "etc_passwd", + filePath: "/etc/passwd", + expected: [2]int64{0, 0}, + }, + { + name: "etc_doesnt_exist", + filePath: "/etc/doesnt/exist", + expected: [2]int64{-1, -1}, + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + owner, group, err := GetFileUNIXOwnership(tt.filePath) + if err != nil { + t.Log(err) + } + ownership := [2]int64{owner, group} + if !assert.Equal(t, ownership, tt.expected) { + t.Logf("%s has different value", tt.name) + } + }) + } +} + +func Test_MakeChangedRootFileInfo(t *testing.T) { + uid_tests := []struct { + name string + rootDir string + filePath string + readContent bool + expected string + }{ + { + name: "etc_passwd", + rootDir: "/", + filePath: "/etc/passwd", + readContent: true, + expected: "root", + }, + { + name: "etc_shadow", + rootDir: "/", + filePath: "/etc/shadow", + readContent: false, + expected: "root", + }, + } + + for _, tt := range uid_tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + fileInfo, err := MakeChangedRootFileInfo(ctx, tt.rootDir, tt.filePath, tt.readContent) + if err != nil { + t.Log(err) + } + + if !assert.Equal(t, fileInfo.Ownership.Username, tt.expected) { + t.Logf("%s has different value", tt.name) + } + }) + } +} + +func Test_MakeFileInfo(t *testing.T) { + type args struct { + filePath string + readContent bool + } + tests := []struct { + name string + args args + want *ds.FileInfo + wantErr bool + }{ + { + name: "test_1", + args: args{ + filePath: "./testdata/test_1", + readContent: true, + }, + want: &datastructures.FileInfo{ + Ownership: &ds.FileOwnership{}, + Path: "./testdata/test_1", + Content: []byte("not empty file"), + Permissions: 420, + }, + wantErr: false, + }, + { + name: "test_2", + args: args{ + filePath: "./testdata/test_2", + readContent: true, + }, + want: &datastructures.FileInfo{ + Ownership: &ds.FileOwnership{}, + Path: "./testdata/test_2", + Content: []byte(""), + Permissions: 493, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MakeFileInfo(tt.args.filePath, tt.args.readContent) + if (err != nil) != tt.wantErr { + t.Errorf("MakeFileInfo() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got.Content, tt.want.Content) { + t.Errorf("MakeFileInfo() = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got.Permissions, tt.want.Permissions) { + t.Errorf("MakeFileInfo() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/sensor/kernelvariables.go b/sensor/kernelvariables.go new file mode 100644 index 00000000..7b5eddf7 --- /dev/null +++ b/sensor/kernelvariables.go @@ -0,0 +1,109 @@ +package sensor + +import ( + "context" + "fmt" + "io" + "os" + "path" + "strings" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" +) + +const ( + procSysKernelDir = "/proc/sys/kernel" + //TODO: add dir for macos (?) + //TODO: add dir for windows (?) +) + +type KernelVariable struct { + Key string `json:"key"` + Value string `json:"value"` + Source string `json:"source"` +} + +func SenseProcSysKernel(ctx context.Context) ([]KernelVariable, error) { + + // open system kernel directory (only Linux OS) + procDir, err := os.Open(procSysKernelDir) + + if err != nil { + return nil, fmt.Errorf("failed to procSysKernelDir dir(%s): %v", procSysKernelDir, err) + } + defer procDir.Close() + + return walkVarsDir(ctx, procSysKernelDir, procDir) +} + +func walkVarsDir(ctx context.Context, dirPath string, procDir *os.File) ([]KernelVariable, error) { + var varsNames []string + varsList := make([]KernelVariable, 0, 128) + + var err error + for varsNames, err = procDir.Readdirnames(100); err == nil; varsNames, err = procDir.Readdirnames(100) { + for varIdx := range varsNames { + varFileName := path.Join(dirPath, varsNames[varIdx]) + varFile, errOpen := os.Open(varFileName) + if errOpen != nil { + if strings.Contains(errOpen.Error(), "permission denied") { + logger.L().Ctx(ctx).Warning("In walkVarsDir failed to open file", helpers.String("varFileName", varFileName), + helpers.Error(errOpen)) + continue + } + return nil, fmt.Errorf("failed to open file (%s): %v", varFileName, err) + } + defer varFile.Close() + fileInfo, errStat := varFile.Stat() + if err != nil { + return nil, fmt.Errorf("failed to stat file (%s): %v", varFileName, errStat) + } + if fileInfo.IsDir() { + // CAUTION: recursive call!!! + innerVars, errW := walkVarsDir(ctx, varFileName, varFile) + if errW != nil { + return nil, fmt.Errorf("failed to walkVarsDir file (%s): %v", varFileName, errW) + } + if len(innerVars) > 0 { + varsList = append(varsList, innerVars...) + } + } else if fileInfo.Mode().IsRegular() { + strBld := strings.Builder{} + if _, errCopy := io.Copy(&strBld, varFile); err != nil { + if strings.Contains(errCopy.Error(), "operation not permitted") { + logger.L().Ctx(ctx).Warning("In walkVarsDir failed to Copy file", helpers.String("varFileName", varFileName), + helpers.Error(errCopy)) + continue + } + return nil, fmt.Errorf("failed to copy file (%s): %w", varFileName, errCopy) + } + varsList = append(varsList, KernelVariable{ + Key: varsNames[varIdx], + Value: strBld.String(), + Source: varFileName, + }) + } + } + } + if err != io.EOF { + return nil, fmt.Errorf("failed to Readdirnames of procSysKernelDir dir(%s): %w; found so far: %v", procSysKernelDir, err, varsList) + } + return varsList, nil +} + +func SenseKernelConfs() ([]KernelVariable, error) { + varsList := make([]KernelVariable, 0, 16) + + return varsList, nil +} + +func SenseKernelVariables(ctx context.Context) ([]KernelVariable, error) { + vars, err := SenseProcSysKernel(ctx) + if confVars, e := SenseKernelConfs(); err != nil { + logger.L().Ctx(ctx).Warning("In SenseKernelVariables failed to SenseKernelConfs", helpers.Error(e)) + } else { + vars = append(vars, confVars...) + } + return vars, err +} diff --git a/sensor/kernelvariables_test.go b/sensor/kernelvariables_test.go new file mode 100644 index 00000000..eb4a1e2c --- /dev/null +++ b/sensor/kernelvariables_test.go @@ -0,0 +1,31 @@ +package sensor + +import ( + "context" + "fmt" + "runtime" + "testing" +) + +func TestSenseProcSysKernel(t *testing.T) { + + // get OS type + osVar := runtime.GOOS + fmt.Printf("Tests are running over OS: %s.\n", osVar) + + switch osVar { + case "windows": + fmt.Printf("Tests are running over OS: %s. Not supported, skipping test %T", osVar, t) + // TODO: need to add functionality for windows + case "darwin": + fmt.Printf("Tests are running over OS: %s. Not supported, skipping test", osVar) + // TODO: need to add functionality for macos + case "linux": + _, err := SenseProcSysKernel(context.TODO()) + if err != nil { + t.Errorf("%v", err) + } + default: + fmt.Printf("Tests are running over OS: %s.\n", osVar) + } +} diff --git a/sensor/kubelet.go b/sensor/kubelet.go new file mode 100644 index 00000000..a259a411 --- /dev/null +++ b/sensor/kubelet.go @@ -0,0 +1,153 @@ +package sensor + +import ( + "context" + "fmt" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" + ds "github.com/kubescape/node-agent/sensor/datastructures" + "github.com/kubescape/node-agent/sensor/internal/utils" + "sigs.k8s.io/yaml" +) + +const ( + procDirName = "/proc" + kubeletProcessSuffix = "/kubelet" + kubeletConfigArgName = "--config" + kubeletClientCAArgName = "--client-ca-file" +) + +// default paths +var kubeletConfigDefaultPathList = []string{ + "/var/lib/kubelet/config.yaml", // default kubelet config file path + "/etc/kubernetes/kubelet/kubelet-config.json", // default EKS config file path +} +var kubeletKubeConfigDefaultPathList = []string{ + "/etc/kubernetes/kubelet.conf", // default kubelet kube config file path + "/var/lib/kubelet/kubeconfig", // default EKS kubeconfig file path +} + +// KubeletInfo holds information about kubelet +type KubeletInfo struct { + // ServiceFile is a list of files used to configure the kubelet service. + // Most of the times it will be a single file, under /etc/systemd/system/kubelet.service.d. + ServiceFiles []ds.FileInfo `json:"serviceFiles,omitempty"` + + // Information about kubelete config file + ConfigFile *ds.FileInfo `json:"configFile,omitempty"` + + // Information about the kubeconfig file of kubelet + KubeConfigFile *ds.FileInfo `json:"kubeConfigFile,omitempty"` + + // Information about the client ca file of kubelet (if exist) + ClientCAFile *ds.FileInfo `json:"clientCAFile,omitempty"` + + // Raw cmd line of kubelet process + CmdLine string `json:"cmdLine"` +} + +func LocateKubeletProcess() (*utils.ProcessDetails, error) { + return utils.LocateProcessByExecSuffix(kubeletProcessSuffix) +} + +func ReadKubeletConfig(kubeletConfArgs string) ([]byte, error) { + conte, err := utils.ReadFileOnHostFileSystem(kubeletConfArgs) + logger.L().Debug("raw content", helpers.String("cont", string(conte))) + return conte, err +} + +func makeKubeletServiceFilesInfo(ctx context.Context, pid int) []ds.FileInfo { + files, err := utils.GetKubeletServiceFiles(pid) + if err != nil { + logger.L().Ctx(ctx).Warning("failed to getKubeletServiceFiles", helpers.Error(err)) + return nil + } + + serviceFiles := []ds.FileInfo{} + for _, file := range files { + info := makeHostFileInfoVerbose(ctx, file, false, helpers.String("in", "makeProcessInfoVerbose")) + if info != nil { + serviceFiles = append(serviceFiles, *info) + } + } + + if len(serviceFiles) == 0 { + return nil + } + + return serviceFiles +} + +// SenseKubeletInfo return varius information about the kubelet service +func SenseKubeletInfo(ctx context.Context) (*KubeletInfo, error) { + ret := KubeletInfo{} + + kubeletProcess, err := LocateKubeletProcess() + if err != nil { + return &ret, fmt.Errorf("failed to Locate kubelet process: %w", err) + } + + // Serivce files + ret.ServiceFiles = makeKubeletServiceFilesInfo(ctx, int(kubeletProcess.PID)) + + pConfigPath, ok := kubeletProcess.GetArg(kubeletConfigArgName) + if ok { + ret.ConfigFile = makeContaineredFileInfoVerbose(ctx, kubeletProcess, pConfigPath, true, + helpers.String("in", "SenseKubeletInfo"), + ) + } else { + ret.ConfigFile = makeContaineredFileInfoFromListVerbose(ctx, kubeletProcess, kubeletConfigDefaultPathList, true, + helpers.String("in", "SenseKubeletInfo"), + ) + } + + pKubeConfigPath, ok := kubeletProcess.GetArg(kubeConfigArgName) + if ok { + ret.KubeConfigFile = makeContaineredFileInfoVerbose(ctx, kubeletProcess, pKubeConfigPath, true, + helpers.String("in", "SenseKubeletInfo"), + ) + } else { + ret.KubeConfigFile = makeContaineredFileInfoFromListVerbose(ctx, kubeletProcess, kubeletKubeConfigDefaultPathList, true, + helpers.String("in", "SenseKubeletInfo"), + ) + } + + // Kubelet client ca certificate + caFilePath, ok := kubeletProcess.GetArg(kubeletClientCAArgName) + if !ok && ret.ConfigFile != nil && ret.ConfigFile.Content != nil { + logger.L().Debug("extracting kubelet client ca certificate from config") + extracted, err := kubeletExtractCAFileFromConf(ret.ConfigFile.Content) + if err == nil { + caFilePath = extracted + } + } + if caFilePath != "" { + ret.ClientCAFile = makeContaineredFileInfoVerbose(ctx, kubeletProcess, caFilePath, false, + helpers.String("in", "SenseKubeletInfo"), + ) + } + + // Cmd line + ret.CmdLine = kubeletProcess.RawCmd() + + return &ret, nil +} + +// kubeletExtractCAFileFromConf extract the client ca file path from kubelet config +func kubeletExtractCAFileFromConf(content []byte) (string, error) { + var kubeletConfig struct { + Authentication struct { + X509 struct { + ClientCAFile string + } + } + } + + err := yaml.Unmarshal(content, &kubeletConfig) + if err != nil { + return "", fmt.Errorf("failed to unmarshal kubelet config: %w", err) + } + + return kubeletConfig.Authentication.X509.ClientCAFile, nil +} diff --git a/sensor/kubelet_test.go b/sensor/kubelet_test.go new file mode 100644 index 00000000..5dc3f9dd --- /dev/null +++ b/sensor/kubelet_test.go @@ -0,0 +1,52 @@ +package sensor + +import ( + "os" + "testing" +) + +func Test_kubeletExtractCAFileFromConf(t *testing.T) { + + tests := []struct { + name string + dataPath string + want string + wantErr bool + }{ + { + name: "simple exist", + dataPath: "testdata/clientCAKubeletConf.yaml", + want: "/var/lib/minikube/certs/ca.crt", + wantErr: false, + }, + { + name: "simple not exist", + dataPath: "testdata/clientCAKubeletConf_2.yaml", + want: "", + wantErr: false, + }, + { + name: "simple not exist 2", + dataPath: "testdata/clientCAKubeletConf_3.yaml", + want: "", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + data, err := os.ReadFile(tt.dataPath) + if err != nil { + t.Errorf("kubeletExtractCAFileFromConf() failed to read testdata. %v", err) + return + } + got, err := kubeletExtractCAFileFromConf(data) + if (err != nil) != tt.wantErr { + t.Errorf("kubeletExtractCAFileFromConf() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("kubeletExtractCAFileFromConf() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/sensor/kubeproxy.go b/sensor/kubeproxy.go new file mode 100644 index 00000000..9dc7d44c --- /dev/null +++ b/sensor/kubeproxy.go @@ -0,0 +1,47 @@ +package sensor + +import ( + "context" + "fmt" + + "github.com/kubescape/go-logger/helpers" + ds "github.com/kubescape/node-agent/sensor/datastructures" + "github.com/kubescape/node-agent/sensor/internal/utils" +) + +const ( + kubeProxyExe = "kube-proxy" +) + +// KubeProxyInfo holds information about kube-proxy process +type KubeProxyInfo struct { + // Information about the kubeconfig file of kube-proxy + KubeConfigFile *ds.FileInfo `json:"kubeConfigFile,omitempty"` + + // Raw cmd line of kubelet process + CmdLine string `json:"cmdLine"` +} + +// SenseKubeProxyInfo return `KubeProxyInfo` +func SenseKubeProxyInfo(ctx context.Context) (*KubeProxyInfo, error) { + ret := KubeProxyInfo{} + + // Get process + proc, err := utils.LocateProcessByExecSuffix(kubeProxyExe) + if err != nil { + return &ret, fmt.Errorf("failed to locate kube-proxy process: %w", err) + } + + // kubeconfig + kubeConfigPath, ok := proc.GetArg(kubeConfigArgName) + if ok { + ret.KubeConfigFile = makeContaineredFileInfoVerbose(ctx, proc, kubeConfigPath, false, + helpers.String("in", "SenseKubeProxyInfo"), + ) + } + + // cmd line + ret.CmdLine = proc.RawCmd() + + return &ret, nil +} diff --git a/sensor/network.go b/sensor/network.go new file mode 100644 index 00000000..ed7038de --- /dev/null +++ b/sensor/network.go @@ -0,0 +1,69 @@ +package sensor + +import ( + "context" + "fmt" + "os" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" + "github.com/weaveworks/procspy" +) + +const ( + tcpListeningState = 10 +) + +var ( + ProcNetTCPPaths = []string{"/proc/net/tcp", "/proc/net/tcp6"} + ProcNetUDPPaths = []string{"/proc/net/udp", "/proc/net/udp6", "/proc/net/udplite", "/proc/net/udplite6"} + ProcNetICMPPaths = []string{"/proc/net/icmp", "/proc/net/icmp6"} +) + +type OpenPortsStatus struct { + TcpPorts []procspy.Connection `json:"tcpPorts"` + UdpPorts []procspy.Connection `json:"udpPorts"` + ICMPPorts []procspy.Connection `json:"icmpPorts"` +} + +func getOpenedPorts(pathsList []string) ([]procspy.Connection, error) { + res := make([]procspy.Connection, 0) + for netPathIdx := range pathsList { + bytesBuf, err := os.ReadFile(pathsList[netPathIdx]) + if err != nil { + return res, fmt.Errorf("failed to ReadFile(%s): %v", pathsList[netPathIdx], err) + } + netCons := procspy.NewProcNet(bytesBuf, tcpListeningState) + for c := netCons.Next(); c != nil; c = netCons.Next() { + res = append(res, *c) + } + } + return res, nil +} + +func SenseOpenPorts(ctx context.Context) (*OpenPortsStatus, error) { + // TODO: take process name. walks on ProcNetTCPPaths for each process in the system + res := OpenPortsStatus{TcpPorts: make([]procspy.Connection, 0)} + // tcp + ports, err := getOpenedPorts(ProcNetTCPPaths) + if err != nil { + logger.L().Ctx(ctx).Warning("In SenseOpenPorts", helpers.String("paths", fmt.Sprintf("%v", ProcNetTCPPaths)), helpers.Error(err)) + } else { + res.TcpPorts = ports + } + // udp + ports, err = getOpenedPorts(ProcNetUDPPaths) + if err != nil { + logger.L().Ctx(ctx).Warning("In SenseOpenPorts", helpers.String("paths", fmt.Sprintf("%v", ProcNetUDPPaths)), helpers.Error(err)) + } else { + res.UdpPorts = ports + } + // icmp + ports, err = getOpenedPorts(ProcNetICMPPaths) + if err != nil { + logger.L().Ctx(ctx).Warning("In SenseOpenPorts", helpers.String("paths", fmt.Sprintf("%v", ProcNetICMPPaths)), helpers.Error(err)) + } else { + res.ICMPPorts = ports + } + return &res, nil +} diff --git a/sensor/network_test.go b/sensor/network_test.go new file mode 100644 index 00000000..bbe29703 --- /dev/null +++ b/sensor/network_test.go @@ -0,0 +1,13 @@ +package sensor + +import ( + "context" + "testing" +) + +func TestSenseOpenPorts(t *testing.T) { + _, err := SenseOpenPorts(context.TODO()) + if err != nil { + t.Errorf("%v", err) + } +} diff --git a/sensor/osrelease.go b/sensor/osrelease.go new file mode 100644 index 00000000..dd8059fc --- /dev/null +++ b/sensor/osrelease.go @@ -0,0 +1,89 @@ +package sensor + +import ( + "fmt" + "os" + "path" + "strings" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" + ds "github.com/kubescape/node-agent/sensor/datastructures" + "github.com/kubescape/node-agent/sensor/internal/utils" +) + +const ( + etcDirName = "/etc" + osReleaseFileSuffix = "os-release" + appArmorProfilesFileName = "/sys/kernel/security/apparmor/profiles" + seLinuxConfigFileName = "/etc/selinux/semanage.conf" +) + +func SenseOsRelease() ([]byte, error) { + osFileName, err := getOsReleaseFile() + if err == nil { + return utils.ReadFileOnHostFileSystem(path.Join(etcDirName, osFileName)) + } + return []byte{}, fmt.Errorf("failed to find os-release file: %v", err) +} + +func getOsReleaseFile() (string, error) { + hostEtcDir := utils.HostPath(etcDirName) + etcDir, err := os.Open(hostEtcDir) + if err != nil { + return "", fmt.Errorf("failed to open etc dir: %v", err) + } + defer etcDir.Close() + var etcSons []string + for etcSons, err = etcDir.Readdirnames(100); err == nil; etcSons, err = etcDir.Readdirnames(100) { + for idx := range etcSons { + if strings.HasSuffix(etcSons[idx], osReleaseFileSuffix) { + logger.L().Debug("os release file found", helpers.String("filename", etcSons[idx])) + return etcSons[idx], nil + } + } + } + return "", err +} + +func SenseKernelVersion() ([]byte, error) { + return utils.ReadFileOnHostFileSystem(path.Join(procDirName, "version")) +} + +func getAppArmorStatus() string { + statusStr := "unloaded" + hostAppArmorProfilesFileName := utils.HostPath(appArmorProfilesFileName) + profFile, err := os.Open(hostAppArmorProfilesFileName) + if err == nil { + defer profFile.Close() + statusStr = "stopped" + content, err := utils.ReadFileOnHostFileSystem(appArmorProfilesFileName) + if err == nil && len(content) > 0 { + statusStr = string(content) + } + } + return statusStr +} + +func getSELinuxStatus() string { + statusStr := "not found" + hostAppArmorProfilesFileName := utils.HostPath(seLinuxConfigFileName) + conFile, err := os.Open(hostAppArmorProfilesFileName) + if err == nil { + defer conFile.Close() + content, err := utils.ReadFileOnHostFileSystem(appArmorProfilesFileName) + if err == nil && len(content) > 0 { + statusStr = string(content) + } + } + return statusStr +} + +func SenseLinuxSecurityHardening() (*ds.LinuxSecurityHardeningStatus, error) { + res := ds.LinuxSecurityHardeningStatus{} + + res.AppArmor = getAppArmorStatus() + res.SeLinux = getSELinuxStatus() + + return &res, nil +} diff --git a/sensor/testdata/clientCAKubeletConf.yaml b/sensor/testdata/clientCAKubeletConf.yaml new file mode 100644 index 00000000..871bba8f --- /dev/null +++ b/sensor/testdata/clientCAKubeletConf.yaml @@ -0,0 +1,19 @@ +apiVersion: kubelet.config.k8s.io/v1beta1 +authentication: + anonymous: + enabled: false + webhook: + cacheTTL: 0s + enabled: true + x509: + clientCAFile: /var/lib/minikube/certs/ca.crt +evictionHard: + imagefs.available: 0% + nodefs.available: 0% + nodefs.inodesFree: 0% +logging: + flushFrequency: 0 + options: + json: + infoBufferSize: "0" + verbosity: 0 \ No newline at end of file diff --git a/sensor/testdata/clientCAKubeletConf_2.yaml b/sensor/testdata/clientCAKubeletConf_2.yaml new file mode 100644 index 00000000..00559e12 --- /dev/null +++ b/sensor/testdata/clientCAKubeletConf_2.yaml @@ -0,0 +1,17 @@ +apiVersion: kubelet.config.k8s.io/v1beta1 +authentication: + anonymous: + enabled: false + webhook: + cacheTTL: 0s + enabled: true +evictionHard: + imagefs.available: 0% + nodefs.available: 0% + nodefs.inodesFree: 0% +logging: + flushFrequency: 0 + options: + json: + infoBufferSize: "0" + verbosity: 0 \ No newline at end of file diff --git a/sensor/testdata/clientCAKubeletConf_3.yaml b/sensor/testdata/clientCAKubeletConf_3.yaml new file mode 100644 index 00000000..8d4de28d --- /dev/null +++ b/sensor/testdata/clientCAKubeletConf_3.yaml @@ -0,0 +1,11 @@ +apiVersion: kubelet.config.k8s.io/v1beta1 +evictionHard: + imagefs.available: 0% + nodefs.available: 0% + nodefs.inodesFree: 0% +logging: + flushFrequency: 0 + options: + json: + infoBufferSize: "0" + verbosity: 0 \ No newline at end of file diff --git a/sensor/testdata/testmakehostfiles/dir/placeholder.json b/sensor/testdata/testmakehostfiles/dir/placeholder.json new file mode 100644 index 00000000..e69de29b diff --git a/sensor/testdata/testmakehostfiles/file1.yaml b/sensor/testdata/testmakehostfiles/file1.yaml new file mode 100644 index 00000000..e69de29b diff --git a/sensor/testdata/testmakehostfiles/file2.yaml b/sensor/testdata/testmakehostfiles/file2.yaml new file mode 100644 index 00000000..e69de29b diff --git a/sensor/testdata/testmakehostfiles/file3.yaml b/sensor/testdata/testmakehostfiles/file3.yaml new file mode 100644 index 00000000..e69de29b diff --git a/sensor/verboseutils.go b/sensor/verboseutils.go new file mode 100644 index 00000000..28cfd06a --- /dev/null +++ b/sensor/verboseutils.go @@ -0,0 +1,128 @@ +package sensor + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "path" + "sort" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" + ds "github.com/kubescape/node-agent/sensor/datastructures" + "github.com/kubescape/node-agent/sensor/internal/utils" +) + +const ( + maxRecursionDepth = 10 +) + +// makeHostFileInfoVerbose makes a file info object +// for the given path on the host file system, and with error logging. +// It returns nil on error. +func makeHostFileInfoVerbose(ctx context.Context, path string, readContent bool, failMsgs ...helpers.IDetails) *ds.FileInfo { + return makeChangedRootFileInfoVerbose(ctx, utils.HostFileSystemDefaultLocation, path, readContent, failMsgs...) +} + +// makeContaineredFileInfoFromListVerbose makes a file info object +// for a given process file system view, and with error logging. +// It tries to find the file in the given list of paths, by the order of the list. +// It returns nil on error. +func makeContaineredFileInfoFromListVerbose(ctx context.Context, p *utils.ProcessDetails, filePathList []string, readContent bool, failMsgs ...helpers.IDetails) *ds.FileInfo { + + for _, filePath := range filePathList { + fileInfo := makeChangedRootFileInfoVerbose(ctx, p.RootDir(), filePath, readContent, failMsgs...) + if fileInfo != nil { + return fileInfo + } + } + return nil +} + +// makeContaineredFileInfoVerbose makes a file info object +// for a given process file system view, and with error logging. +// It returns nil on error. +func makeContaineredFileInfoVerbose(ctx context.Context, p *utils.ProcessDetails, filePath string, readContent bool, failMsgs ...helpers.IDetails) *ds.FileInfo { + return makeChangedRootFileInfoVerbose(ctx, p.RootDir(), filePath, readContent, failMsgs...) +} + +// makeChangedRootFileInfoVerbose makes a file info object +// for the given path on the given root directory, and with error logging. +func makeChangedRootFileInfoVerbose(ctx context.Context, rootDir string, path string, readContent bool, failMsgs ...helpers.IDetails) *ds.FileInfo { + fileInfo, err := utils.MakeChangedRootFileInfo(ctx, rootDir, path, readContent) + if err != nil { + logArgs := append([]helpers.IDetails{ + helpers.String("path", path), + helpers.Error(err), + }, + failMsgs..., + ) + logger.L().Ctx(ctx).Warning("failed to MakeHostFileInfo", logArgs...) + } + return fileInfo +} + +// makeHostDirFilesInfo iterate over a directory and make a list of +// file infos for all the files inside it. If `recursive` is set to true, +// the file infos will be added recursively until `maxRecursionDepth` is reached +func makeHostDirFilesInfoVerbose(ctx context.Context, dir string, recursive bool, fileInfos *[]*ds.FileInfo, recursionLevel int) ([]*ds.FileInfo, error) { + dirInfo, err := os.Open(utils.HostPath(dir)) + if err != nil { + return nil, fmt.Errorf("failed to open dir at %s: %w", dir, err) + } + defer dirInfo.Close() + + if fileInfos == nil { + fileInfos = &([]*ds.FileInfo{}) + } + + var fileNames []string + for fileNames, err = dirInfo.Readdirnames(100); err == nil; fileNames, err = dirInfo.Readdirnames(100) { + // add sorting to make tests deterministic + sort.Strings(fileNames) + for i := range fileNames { + filePath := path.Join(dir, fileNames[i]) + + // Check if is directory + stats, err := os.Stat(utils.HostPath(filePath)) + if err != nil { + logger.L().Ctx(ctx).Warning("failed to get file stats", + helpers.String("in", "makeHostDirFilesInfo"), + helpers.String("path", filePath)) + continue + } + if stats.IsDir() { + if recursionLevel+1 == maxRecursionDepth { + logger.L().Ctx(ctx).Warning("max recursion depth exceeded", + helpers.String("in", "makeHostDirFilesInfo"), + helpers.String("path", filePath)) + continue + } + makeHostDirFilesInfoVerbose(ctx, filePath, recursive, fileInfos, recursionLevel+1) + } + + fileInfo := makeHostFileInfoVerbose(ctx, filePath, + false, + helpers.String("in", "makeHostDirFilesInfo"), + helpers.String("dir", dir), + ) + + // if it is not a directory and content is different from nil, then add this to the list. + if fileInfo != nil && !stats.IsDir() { + *fileInfos = append(*fileInfos, fileInfo) + } + + if !recursive { + continue + } + } + } + + if errors.Is(err, io.EOF) { + err = nil + } + + return *fileInfos, err +} diff --git a/sensor/verboseutils_test.go b/sensor/verboseutils_test.go new file mode 100644 index 00000000..5bf56af0 --- /dev/null +++ b/sensor/verboseutils_test.go @@ -0,0 +1,38 @@ +package sensor + +import ( + "context" + "os" + "regexp" + "testing" + + "github.com/kubescape/go-logger" + "github.com/kubescape/node-agent/sensor/internal/utils" + "github.com/stretchr/testify/assert" +) + +func Test_makeHostDirFilesInfo(t *testing.T) { + utils.HostFileSystemDefaultLocation = "." + fileInfos, err := makeHostDirFilesInfoVerbose(context.TODO(), "testdata/testmakehostfiles", true, nil, 0) + assert.NoError(t, err) + assert.Len(t, fileInfos, 4) + + // Test maxRecursionDepth + // create a log file + f, err := os.CreateTemp("", "log-*") + assert.NoError(t, err) + defer os.Remove(f.Name()) // clean up + logger.InitLogger("pretty") + logger.L().SetWriter(f) + + // test + fileInfos, err = makeHostDirFilesInfoVerbose(context.TODO(), "testdata/testmakehostfiles", true, nil, maxRecursionDepth-1) + assert.NoError(t, err) + assert.Len(t, fileInfos, 3) + + // check log output for error message + data, err := os.ReadFile(f.Name()) + assert.NoError(t, err) + re := regexp.MustCompile("max recursion depth exceeded") + assert.Len(t, re.FindAll(data, -1), 1) +} From aee8944f8b999a3ea666e6827f2317d6f9ddcf99 Mon Sep 17 00:00:00 2001 From: Yuval Leibovich Date: Sun, 17 Dec 2023 13:59:49 +0200 Subject: [PATCH 3/7] adding senosr folder --- go.mod | 1 + go.sum | 4 ++++ sensor/CNI.go | 7 +++++-- sensor/cloudProvider.go | 5 ++++- sensor/controlplane.go | 6 ++++-- sensor/internal/utils/utils.go | 3 ++- sensor/internal/utils/utils_test.go | 7 +++++-- sensor/kubelet.go | 7 +++++-- sensor/kubeproxy.go | 6 ++++-- sensor/osrelease.go | 7 +++++-- sensor/verboseutils.go | 7 +++++-- sensor/verboseutils_test.go | 6 ++++-- 12 files changed, 48 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index a4c08296..17f9f5c7 100644 --- a/go.mod +++ b/go.mod @@ -88,6 +88,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.3 // indirect + github.com/kubescape/host-scanner v0.0.0-20230815131417-6e97c07e07da github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect diff --git a/go.sum b/go.sum index 0504fe91..64e93b8b 100644 --- a/go.sum +++ b/go.sum @@ -319,6 +319,8 @@ github.com/kubescape/backend v0.0.16 h1:bkQGY39GSoNIeFfnAJ2zlcrGEyXk6LGYv1/MgS51 github.com/kubescape/backend v0.0.16/go.mod h1:ug9NFmmxT4DcQx3sgdLRzlLPWMKGHE/fpbcYUm5G5Qo= github.com/kubescape/go-logger v0.0.22 h1:gle7wH6emOiGv9ljdpVi82pWLQ3jGucrUucvil6JXHE= github.com/kubescape/go-logger v0.0.22/go.mod h1:x3HBpZo3cMT/WIdy18BxvVVd5D0e/PWFVk/HiwBNu3g= +github.com/kubescape/host-scanner v0.0.0-20230815131417-6e97c07e07da h1:7C6TX/i078UouHAMibUEePmRZmhoHinY3y61RfaFVRI= +github.com/kubescape/host-scanner v0.0.0-20230815131417-6e97c07e07da/go.mod h1:HrTFA+wNEJzQWrxS+zPGEHDC0+Nsnl7WZd9XGjSXM1I= github.com/kubescape/k8s-interface v0.0.152 h1:1tm2zPYVK7+1fewpca0/MCoK3TgUNButpM3F3uZz6yo= github.com/kubescape/k8s-interface v0.0.152/go.mod h1:5sz+5Cjvo98lTbTVDiDA4MmlXxeHSVMW/wR0V3hV4K8= github.com/kubescape/storage v0.0.39 h1:zxdu6pQ/8Fdzp0Er0yX+KWApMYvNZh9y7ONWyJcbb08= @@ -337,6 +339,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= +github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= diff --git a/sensor/CNI.go b/sensor/CNI.go index dc976240..0abbe7e1 100644 --- a/sensor/CNI.go +++ b/sensor/CNI.go @@ -6,8 +6,11 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - ds "github.com/kubescape/node-agent/sensor/datastructures" - "github.com/kubescape/node-agent/sensor/internal/utils" + + // ds "github.com/kubescape/node-agent/sensor/datastructures" + ds "github.com/kubescape/host-scanner/sensor/datastructures" + // "github.com/kubescape/node-agent/sensor/internal/utils" + "github.com/kubescape/host-scanner/sensor/internal/utils" ) // KubeProxyInfo holds information about kube-proxy process diff --git a/sensor/cloudProvider.go b/sensor/cloudProvider.go index 39e85351..15eca0f4 100644 --- a/sensor/cloudProvider.go +++ b/sensor/cloudProvider.go @@ -1,8 +1,11 @@ package sensor import ( + "node-agent/sensor/internal/utils" + "github.com/armosec/utils-go/httputils" - "github.com/kubescape/node-agent/sensor/internal/utils" + "github.com/kubescape/host-scanner/sensor/internal/utils" + // "github.com/kubescape/node-agent/sensor/internal/utils" ) // CloudProviderInfo holds information about the Cloud Provider diff --git a/sensor/controlplane.go b/sensor/controlplane.go index e35c8ed7..a450896c 100644 --- a/sensor/controlplane.go +++ b/sensor/controlplane.go @@ -11,8 +11,10 @@ import ( "github.com/kubescape/go-logger/helpers" "gopkg.in/yaml.v3" - ds "github.com/kubescape/node-agent/sensor/datastructures" - "github.com/kubescape/node-agent/sensor/internal/utils" + // ds "github.com/kubescape/node-agent/sensor/datastructures" + ds "github.com/kubescape/host-scanner/sensor/datastructures" + // "github.com/kubescape/node-agent/sensor/internal/utils" + "github.com/kubescape/host-scanner/sensor/internal/utils" ) const ( diff --git a/sensor/internal/utils/utils.go b/sensor/internal/utils/utils.go index 9d338bd7..d5cc8b24 100644 --- a/sensor/internal/utils/utils.go +++ b/sensor/internal/utils/utils.go @@ -9,7 +9,8 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - ds "github.com/kubescape/node-agent/sensor/datastructures" + ds "github.com/kubescape/host-scanner/sensor/datastructures" + // ds "github.com/kubescape/node-agent/sensor/datastructures" ) var ( diff --git a/sensor/internal/utils/utils_test.go b/sensor/internal/utils/utils_test.go index ef9304eb..2a3a0117 100644 --- a/sensor/internal/utils/utils_test.go +++ b/sensor/internal/utils/utils_test.go @@ -5,8 +5,11 @@ import ( "reflect" "testing" - "github.com/kubescape/node-agent/sensor/datastructures" - ds "github.com/kubescape/node-agent/sensor/datastructures" + // "github.com/kubescape/node-agent/sensor/datastructures" + "github.com/kubescape/host-scanner/sensor/datastructures" + + // ds "github.com/kubescape/node-agent/sensor/datastructures" + ds "github.com/kubescape/host-scanner/sensor/datastructures" "github.com/stretchr/testify/assert" ) diff --git a/sensor/kubelet.go b/sensor/kubelet.go index a259a411..601521b9 100644 --- a/sensor/kubelet.go +++ b/sensor/kubelet.go @@ -6,8 +6,11 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - ds "github.com/kubescape/node-agent/sensor/datastructures" - "github.com/kubescape/node-agent/sensor/internal/utils" + + // ds "github.com/kubescape/node-agent/sensor/datastructures" + ds "github.com/kubescape/host-scanner/sensor/datastructures" + // "github.com/kubescape/node-agent/sensor/internal/utils" + "github.com/kubescape/host-scanner/sensor/internal/utils" "sigs.k8s.io/yaml" ) diff --git a/sensor/kubeproxy.go b/sensor/kubeproxy.go index 9dc7d44c..831f49b8 100644 --- a/sensor/kubeproxy.go +++ b/sensor/kubeproxy.go @@ -5,8 +5,10 @@ import ( "fmt" "github.com/kubescape/go-logger/helpers" - ds "github.com/kubescape/node-agent/sensor/datastructures" - "github.com/kubescape/node-agent/sensor/internal/utils" + // ds "github.com/kubescape/node-agent/sensor/datastructures" + ds "github.com/kubescape/host-scanner/sensor/datastructures" + // "github.com/kubescape/node-agent/sensor/internal/utils" + "github.com/kubescape/host-scanner/sensor/internal/utils" ) const ( diff --git a/sensor/osrelease.go b/sensor/osrelease.go index dd8059fc..06d65976 100644 --- a/sensor/osrelease.go +++ b/sensor/osrelease.go @@ -8,8 +8,11 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - ds "github.com/kubescape/node-agent/sensor/datastructures" - "github.com/kubescape/node-agent/sensor/internal/utils" + + // ds "github.com/kubescape/node-agent/sensor/datastructures" + // "github.com/kubescape/node-agent/sensor/internal/utils" + ds "github.com/kubescape/host-scanner/sensor/datastructures" + "github.com/kubescape/host-scanner/sensor/internal/utils" ) const ( diff --git a/sensor/verboseutils.go b/sensor/verboseutils.go index 28cfd06a..d33f02a9 100644 --- a/sensor/verboseutils.go +++ b/sensor/verboseutils.go @@ -11,8 +11,11 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - ds "github.com/kubescape/node-agent/sensor/datastructures" - "github.com/kubescape/node-agent/sensor/internal/utils" + + // ds "github.com/kubescape/node-agent/sensor/datastructures" + // "github.com/kubescape/node-agent/sensor/internal/utils" + ds "github.com/kubescape/host-scanner/sensor/datastructures" + "github.com/kubescape/host-scanner/sensor/internal/utils" ) const ( diff --git a/sensor/verboseutils_test.go b/sensor/verboseutils_test.go index 5bf56af0..efb7d534 100644 --- a/sensor/verboseutils_test.go +++ b/sensor/verboseutils_test.go @@ -7,7 +7,9 @@ import ( "testing" "github.com/kubescape/go-logger" - "github.com/kubescape/node-agent/sensor/internal/utils" + // "github.com/kubescape/node-agent/sensor/internal/utils" + "github.com/kubescape/host-scanner/sensor/internal/utils" + "github.com/stretchr/testify/assert" ) @@ -24,7 +26,7 @@ func Test_makeHostDirFilesInfo(t *testing.T) { defer os.Remove(f.Name()) // clean up logger.InitLogger("pretty") logger.L().SetWriter(f) - + // test fileInfos, err = makeHostDirFilesInfoVerbose(context.TODO(), "testdata/testmakehostfiles", true, nil, maxRecursionDepth-1) assert.NoError(t, err) From 2b2b720d94d531665292289ea516be0762e82bfc Mon Sep 17 00:00:00 2001 From: Yuval Leibovich Date: Sun, 17 Dec 2023 15:48:35 +0200 Subject: [PATCH 4/7] test int fix --- sensor/internal/utils/process.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensor/internal/utils/process.go b/sensor/internal/utils/process.go index 3b826603..684803b0 100644 --- a/sensor/internal/utils/process.go +++ b/sensor/internal/utils/process.go @@ -36,7 +36,7 @@ func LocateProcessByExecSuffix(processSuffix string) (*ProcessDetails, error) { for pidDirs, err = procDir.Readdirnames(100); err == nil; pidDirs, err = procDir.Readdirnames(100) { for pidIdx := range pidDirs { // since processes are about to die in the middle of the loop, we will ignore next errors - pid, err := strconv.ParseInt(pidDirs[pidIdx], 10, 0) + pid, err := strconv.ParseInt(pidDirs[pidIdx], 10, 32) if err != nil { continue } From 217f56839631c7f1ed88e2cfdcdc3ccce00bf0c9 Mon Sep 17 00:00:00 2001 From: Yuval Leibovich Date: Sun, 17 Dec 2023 15:51:53 +0200 Subject: [PATCH 5/7] fix to 64 --- sensor/internal/utils/process.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensor/internal/utils/process.go b/sensor/internal/utils/process.go index 684803b0..f47288e4 100644 --- a/sensor/internal/utils/process.go +++ b/sensor/internal/utils/process.go @@ -36,7 +36,7 @@ func LocateProcessByExecSuffix(processSuffix string) (*ProcessDetails, error) { for pidDirs, err = procDir.Readdirnames(100); err == nil; pidDirs, err = procDir.Readdirnames(100) { for pidIdx := range pidDirs { // since processes are about to die in the middle of the loop, we will ignore next errors - pid, err := strconv.ParseInt(pidDirs[pidIdx], 10, 32) + pid, err := strconv.ParseInt(pidDirs[pidIdx], 10, 64) if err != nil { continue } From 7fe3a5578ecfb777e6edb04d628900d1cfc37b85 Mon Sep 17 00:00:00 2001 From: Yuval Leibovich Date: Tue, 19 Dec 2023 14:17:40 +0200 Subject: [PATCH 6/7] fixing some links --- build/Dockerfile | 2 +- go.mod | 10 +++++----- sensor/CNI.go | 6 +++--- sensor/cloudProvider.go | 6 ++---- sensor/verboseutils_test.go | 4 +--- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index f1e14b5d..9f9c1b5e 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -37,4 +37,4 @@ WORKDIR /root # Define entrypoint # NOTE: This needs to be adjusted if both applications can't run simultaneously -ENTRYPOINT ["/usr/bin/node-agent"] \ No newline at end of file +ENTRYPOINT ["node-agent"] \ No newline at end of file diff --git a/go.mod b/go.mod index 17f9f5c7..71e08d60 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/Microsoft/hcsshim v0.12.0-rc.1 // indirect github.com/armosec/armoapi-go v0.0.254 // indirect github.com/armosec/gojay v1.2.15 // indirect - github.com/armosec/utils-go v0.0.40 // indirect + github.com/armosec/utils-go v0.0.40 github.com/briandowns/spinner v1.23.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/containerd/cgroups/v3 v3.0.2 // indirect @@ -49,7 +49,7 @@ require ( github.com/containerd/ttrpc v1.2.2 // indirect github.com/containerd/typeurl/v2 v2.1.1 // indirect github.com/coreos/go-oidc v2.2.1+incompatible // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.5.0 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect @@ -68,7 +68,7 @@ require ( github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -169,7 +169,7 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + gopkg.in/yaml.v3 v3.0.1 k8s.io/cli-runtime v0.28.4 // indirect k8s.io/cri-api v0.28.4 // indirect k8s.io/klog/v2 v2.110.1 // indirect @@ -179,7 +179,7 @@ require ( sigs.k8s.io/kustomize/api v0.14.0 // indirect sigs.k8s.io/kustomize/kyaml v0.14.3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + sigs.k8s.io/yaml v1.4.0 ) replace github.com/vishvananda/netns => github.com/inspektor-gadget/netns v0.0.5-0.20230524185006-155d84c555d6 diff --git a/sensor/CNI.go b/sensor/CNI.go index 0abbe7e1..652711dc 100644 --- a/sensor/CNI.go +++ b/sensor/CNI.go @@ -10,7 +10,7 @@ import ( // ds "github.com/kubescape/node-agent/sensor/datastructures" ds "github.com/kubescape/host-scanner/sensor/datastructures" // "github.com/kubescape/node-agent/sensor/internal/utils" - "github.com/kubescape/host-scanner/sensor/internal/utils" + hostUtils "github.com/kubescape/host-scanner/sensor/internal/utils" ) // KubeProxyInfo holds information about kube-proxy process @@ -49,7 +49,7 @@ func makeCNIConfigFilesInfo(ctx context.Context) ([]*ds.FileInfo, error) { return nil, err } - CNIConfigDir := utils.GetCNIConfigPath(ctx, kubeletProc) + CNIConfigDir := hostUtils.GetCNIConfigPath(ctx, kubeletProc) if CNIConfigDir == "" { return nil, fmt.Errorf("no CNI Config dir found in getCNIConfigPath") @@ -89,7 +89,7 @@ func getCNINames(ctx context.Context) []string { } for _, cni := range supportedCNIs { - p, _ := utils.LocateProcessByExecSuffix(cni.processSuffix) + p, _ := hostUtils.LocateProcessByExecSuffix(cni.processSuffix) if p != nil { logger.L().Debug("CNI process found", helpers.String("name", cni.name)) diff --git a/sensor/cloudProvider.go b/sensor/cloudProvider.go index 15eca0f4..c5b05ddd 100644 --- a/sensor/cloudProvider.go +++ b/sensor/cloudProvider.go @@ -3,9 +3,7 @@ package sensor import ( "node-agent/sensor/internal/utils" - "github.com/armosec/utils-go/httputils" - "github.com/kubescape/host-scanner/sensor/internal/utils" - // "github.com/kubescape/node-agent/sensor/internal/utils" + httpUtils "github.com/armosec/utils-go/httputils" ) // CloudProviderInfo holds information about the Cloud Provider @@ -52,7 +50,7 @@ func hasMetaDataAPIAccess() bool { client.Timeout = 1000000000 for _, req := range CloudProviderMetaDataAPIs { - res, err := httputils.HttpGet(client, req.url, req.headers) + res, err := httpUtils.HttpGet(client, req.url, req.headers) if err == nil && res.StatusCode == 200 { return true diff --git a/sensor/verboseutils_test.go b/sensor/verboseutils_test.go index efb7d534..fe1eb494 100644 --- a/sensor/verboseutils_test.go +++ b/sensor/verboseutils_test.go @@ -7,9 +7,7 @@ import ( "testing" "github.com/kubescape/go-logger" - // "github.com/kubescape/node-agent/sensor/internal/utils" - "github.com/kubescape/host-scanner/sensor/internal/utils" - + "github.com/kubescape/node-agent/sensor/internal/utils" "github.com/stretchr/testify/assert" ) From db13f53471df7e1eb14bbf139ee3359ef0dae1fc Mon Sep 17 00:00:00 2001 From: Yuval Leibovich Date: Tue, 19 Dec 2023 14:54:57 +0200 Subject: [PATCH 7/7] moving sensor folder under pkg --- build/Dockerfile_hostscanner | 21 ++++++++ build/Dockerfile_nodeagent | 20 +++++++ {sensor => pkg/sensor}/CNI.go | 14 +++-- {sensor => pkg/sensor}/CNI_test.go | 0 {sensor => pkg/sensor}/cloudProvider.go | 6 +-- {sensor => pkg/sensor}/cloudProvider_test.go | 0 {sensor => pkg/sensor}/controlplane.go | 52 +++++++++---------- {sensor => pkg/sensor}/controlplane_test.go | 0 .../sensor}/datastructures/fileinfo.go | 0 .../linuxsecurityhardeningstatus.go | 0 {sensor => pkg/sensor}/error.go | 0 {sensor => pkg/sensor}/globals.go | 0 .../internal/utils/containerruntime.go | 0 .../internal/utils/containerruntime_test.go | 0 .../sensor}/internal/utils/globals.go | 0 .../sensor}/internal/utils/osuserswrapper.go | 0 .../internal/utils/osuserswrapper_test.go | 0 .../sensor}/internal/utils/process.go | 0 .../sensor}/internal/utils/process_test.go | 0 .../sensor}/internal/utils/service.go | 0 .../sensor}/internal/utils/testdata/etc/group | 0 .../internal/utils/testdata/etc/passwd | 0 .../utils/testdata/testCNI/containerd.toml | 0 .../testdata/testCNI/containerd_noparams.toml | 0 .../internal/utils/testdata/testCNI/crio.conf | 0 .../testdata/testCNI/crio.d/01_crio.conf | 0 .../testdata/testCNI/crio.d/03_crio.conf | 0 .../testdata/testCNI/crio.d/05_crio.conf | 0 .../testdata/testCNI/crio.d/06_crio.conf | 0 .../testCNI/crio.d_noparams/05_crio.conf | 0 .../testCNI/crio.d_noparams/06_crio.conf | 0 .../utils/testdata/testCNI/crio_noparams.conf | 0 .../sensor}/internal/utils/testdata/test_1 | 0 .../sensor}/internal/utils/testdata/test_2 | 0 .../sensor}/internal/utils/utils.go | 15 +++--- .../sensor}/internal/utils/utils_test.go | 15 +++--- {sensor => pkg/sensor}/kernelvariables.go | 0 .../sensor}/kernelvariables_test.go | 0 {sensor => pkg/sensor}/kubelet.go | 28 +++++----- {sensor => pkg/sensor}/kubelet_test.go | 0 {sensor => pkg/sensor}/kubeproxy.go | 11 ++-- {sensor => pkg/sensor}/network.go | 0 {sensor => pkg/sensor}/network_test.go | 0 {sensor => pkg/sensor}/osrelease.go | 24 ++++----- .../sensor}/testdata/clientCAKubeletConf.yaml | 0 .../testdata/clientCAKubeletConf_2.yaml | 0 .../testdata/clientCAKubeletConf_3.yaml | 0 .../testmakehostfiles/dir/placeholder.json | 0 .../testdata/testmakehostfiles/file1.yaml | 0 .../testdata/testmakehostfiles/file2.yaml | 0 .../testdata/testmakehostfiles/file3.yaml | 0 {sensor => pkg/sensor}/verboseutils.go | 33 ++++++------ {sensor => pkg/sensor}/verboseutils_test.go | 5 +- 53 files changed, 136 insertions(+), 108 deletions(-) create mode 100644 build/Dockerfile_hostscanner create mode 100644 build/Dockerfile_nodeagent rename {sensor => pkg/sensor}/CNI.go (81%) rename {sensor => pkg/sensor}/CNI_test.go (100%) rename {sensor => pkg/sensor}/cloudProvider.go (94%) rename {sensor => pkg/sensor}/cloudProvider_test.go (100%) rename {sensor => pkg/sensor}/controlplane.go (81%) rename {sensor => pkg/sensor}/controlplane_test.go (100%) rename {sensor => pkg/sensor}/datastructures/fileinfo.go (100%) rename {sensor => pkg/sensor}/datastructures/linuxsecurityhardeningstatus.go (100%) rename {sensor => pkg/sensor}/error.go (100%) rename {sensor => pkg/sensor}/globals.go (100%) rename {sensor => pkg/sensor}/internal/utils/containerruntime.go (100%) rename {sensor => pkg/sensor}/internal/utils/containerruntime_test.go (100%) rename {sensor => pkg/sensor}/internal/utils/globals.go (100%) rename {sensor => pkg/sensor}/internal/utils/osuserswrapper.go (100%) rename {sensor => pkg/sensor}/internal/utils/osuserswrapper_test.go (100%) rename {sensor => pkg/sensor}/internal/utils/process.go (100%) rename {sensor => pkg/sensor}/internal/utils/process_test.go (100%) rename {sensor => pkg/sensor}/internal/utils/service.go (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/etc/group (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/etc/passwd (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/containerd.toml (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/containerd_noparams.toml (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/crio.conf (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/crio.d/01_crio.conf (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/crio.d/03_crio.conf (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/crio.d/05_crio.conf (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/crio.d/06_crio.conf (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/crio.d_noparams/05_crio.conf (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/crio.d_noparams/06_crio.conf (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/testCNI/crio_noparams.conf (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/test_1 (100%) rename {sensor => pkg/sensor}/internal/utils/testdata/test_2 (100%) rename {sensor => pkg/sensor}/internal/utils/utils.go (88%) rename {sensor => pkg/sensor}/internal/utils/utils_test.go (86%) rename {sensor => pkg/sensor}/kernelvariables.go (100%) rename {sensor => pkg/sensor}/kernelvariables_test.go (100%) rename {sensor => pkg/sensor}/kubelet.go (84%) rename {sensor => pkg/sensor}/kubelet_test.go (100%) rename {sensor => pkg/sensor}/kubeproxy.go (70%) rename {sensor => pkg/sensor}/network.go (100%) rename {sensor => pkg/sensor}/network_test.go (100%) rename {sensor => pkg/sensor}/osrelease.go (67%) rename {sensor => pkg/sensor}/testdata/clientCAKubeletConf.yaml (100%) rename {sensor => pkg/sensor}/testdata/clientCAKubeletConf_2.yaml (100%) rename {sensor => pkg/sensor}/testdata/clientCAKubeletConf_3.yaml (100%) rename {sensor => pkg/sensor}/testdata/testmakehostfiles/dir/placeholder.json (100%) rename {sensor => pkg/sensor}/testdata/testmakehostfiles/file1.yaml (100%) rename {sensor => pkg/sensor}/testdata/testmakehostfiles/file2.yaml (100%) rename {sensor => pkg/sensor}/testdata/testmakehostfiles/file3.yaml (100%) rename {sensor => pkg/sensor}/verboseutils.go (75%) rename {sensor => pkg/sensor}/verboseutils_test.go (90%) diff --git a/build/Dockerfile_hostscanner b/build/Dockerfile_hostscanner new file mode 100644 index 00000000..77a10165 --- /dev/null +++ b/build/Dockerfile_hostscanner @@ -0,0 +1,21 @@ +FROM --platform=$BUILDPLATFORM golang:1.20-bullseye as builder + +ARG BUILD_VERSION +ENV GO111MODULE=on CGO_ENABLED=0 +WORKDIR /work +ARG TARGETOS TARGETARCH + +RUN --mount=target=. \ + --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg \ + GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/kube-host-sensor --ldflags "-w -s -X main.BuildVersion=$BUILD_VERSION" . + +FROM gcr.io/distroless/static-debian11:latest + +COPY --from=builder /out/kube-host-sensor /usr/bin/kube-host-sensor + +ARG image_version +ENV RELEASE=$image_version + +WORKDIR /root +ENTRYPOINT ["kube-host-sensor"] \ No newline at end of file diff --git a/build/Dockerfile_nodeagent b/build/Dockerfile_nodeagent new file mode 100644 index 00000000..b2e13b6e --- /dev/null +++ b/build/Dockerfile_nodeagent @@ -0,0 +1,20 @@ +FROM --platform=$BUILDPLATFORM golang:1.21-bullseye as builder + +ENV GO111MODULE=on CGO_ENABLED=0 +WORKDIR /work +ARG TARGETOS TARGETARCH + +RUN --mount=target=. \ + --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg \ + GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/node-agent . + +FROM gcr.io/distroless/static-debian11:latest + +COPY --from=builder /out/node-agent /usr/bin/node-agent + +ARG image_version +ENV RELEASE=$image_version + +WORKDIR /root +ENTRYPOINT ["node-agent"] \ No newline at end of file diff --git a/sensor/CNI.go b/pkg/sensor/CNI.go similarity index 81% rename from sensor/CNI.go rename to pkg/sensor/CNI.go index 652711dc..392ff7a0 100644 --- a/sensor/CNI.go +++ b/pkg/sensor/CNI.go @@ -7,15 +7,13 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - // ds "github.com/kubescape/node-agent/sensor/datastructures" - ds "github.com/kubescape/host-scanner/sensor/datastructures" - // "github.com/kubescape/node-agent/sensor/internal/utils" - hostUtils "github.com/kubescape/host-scanner/sensor/internal/utils" + sensorDs "node-agent/pkg/sensor/datastructures" + sensorUtils "node-agent/pkg/sensor/internal/utils" ) // KubeProxyInfo holds information about kube-proxy process type CNIInfo struct { - CNIConfigFiles []*ds.FileInfo `json:"CNIConfigFiles,omitempty"` + CNIConfigFiles []*sensorDs.FileInfo `json:"CNIConfigFiles,omitempty"` // The name of the running CNI CNINames []string `json:"CNINames,omitempty"` @@ -42,14 +40,14 @@ func SenseCNIInfo(ctx context.Context) (*CNIInfo, error) { } // makeCNIConfigFilesInfo - returns a list of FileInfos of cni config files. -func makeCNIConfigFilesInfo(ctx context.Context) ([]*ds.FileInfo, error) { +func makeCNIConfigFilesInfo(ctx context.Context) ([]*sensorDs.FileInfo, error) { // *** Start handling CNI Files kubeletProc, err := LocateKubeletProcess() if err != nil { return nil, err } - CNIConfigDir := hostUtils.GetCNIConfigPath(ctx, kubeletProc) + CNIConfigDir := sensorUtils.GetCNIConfigPath(ctx, kubeletProc) if CNIConfigDir == "" { return nil, fmt.Errorf("no CNI Config dir found in getCNIConfigPath") @@ -89,7 +87,7 @@ func getCNINames(ctx context.Context) []string { } for _, cni := range supportedCNIs { - p, _ := hostUtils.LocateProcessByExecSuffix(cni.processSuffix) + p, _ := sensorUtils.LocateProcessByExecSuffix(cni.processSuffix) if p != nil { logger.L().Debug("CNI process found", helpers.String("name", cni.name)) diff --git a/sensor/CNI_test.go b/pkg/sensor/CNI_test.go similarity index 100% rename from sensor/CNI_test.go rename to pkg/sensor/CNI_test.go diff --git a/sensor/cloudProvider.go b/pkg/sensor/cloudProvider.go similarity index 94% rename from sensor/cloudProvider.go rename to pkg/sensor/cloudProvider.go index c5b05ddd..07fd69c2 100644 --- a/sensor/cloudProvider.go +++ b/pkg/sensor/cloudProvider.go @@ -1,7 +1,7 @@ package sensor import ( - "node-agent/sensor/internal/utils" + sensorUtils "node-agent/pkg/sensor/internal/utils" httpUtils "github.com/armosec/utils-go/httputils" ) @@ -46,7 +46,7 @@ func SenseCloudProviderInfo() (*CloudProviderInfo, error) { // hasMetaDataAPIAccess - checks if there is an access to cloud provider meta data func hasMetaDataAPIAccess() bool { - client := utils.GetHttpClient() + client := sensorUtils.GetHttpClient() client.Timeout = 1000000000 for _, req := range CloudProviderMetaDataAPIs { @@ -56,7 +56,5 @@ func hasMetaDataAPIAccess() bool { return true } } - return false - } diff --git a/sensor/cloudProvider_test.go b/pkg/sensor/cloudProvider_test.go similarity index 100% rename from sensor/cloudProvider_test.go rename to pkg/sensor/cloudProvider_test.go diff --git a/sensor/controlplane.go b/pkg/sensor/controlplane.go similarity index 81% rename from sensor/controlplane.go rename to pkg/sensor/controlplane.go index a450896c..f7f6af93 100644 --- a/sensor/controlplane.go +++ b/pkg/sensor/controlplane.go @@ -11,10 +11,8 @@ import ( "github.com/kubescape/go-logger/helpers" "gopkg.in/yaml.v3" - // ds "github.com/kubescape/node-agent/sensor/datastructures" - ds "github.com/kubescape/host-scanner/sensor/datastructures" - // "github.com/kubescape/node-agent/sensor/internal/utils" - "github.com/kubescape/host-scanner/sensor/internal/utils" + sensorDs "node-agent/pkg/sensor/datastructures" + sensorUtils "node-agent/pkg/sensor/internal/utils" ) const ( @@ -45,44 +43,44 @@ var ( // KubeProxyInfo holds information about kube-proxy process type ControlPlaneInfo struct { - APIServerInfo *ApiServerInfo `json:"APIServerInfo,omitempty"` - ControllerManagerInfo *K8sProcessInfo `json:"controllerManagerInfo,omitempty"` - SchedulerInfo *K8sProcessInfo `json:"schedulerInfo,omitempty"` - EtcdConfigFile *ds.FileInfo `json:"etcdConfigFile,omitempty"` - EtcdDataDir *ds.FileInfo `json:"etcdDataDir,omitempty"` - AdminConfigFile *ds.FileInfo `json:"adminConfigFile,omitempty"` - PKIDIr *ds.FileInfo `json:"PKIDir,omitempty"` - PKIFiles []*ds.FileInfo `json:"PKIFiles,omitempty"` + APIServerInfo *ApiServerInfo `json:"APIServerInfo,omitempty"` + ControllerManagerInfo *K8sProcessInfo `json:"controllerManagerInfo,omitempty"` + SchedulerInfo *K8sProcessInfo `json:"schedulerInfo,omitempty"` + EtcdConfigFile *sensorDs.FileInfo `json:"etcdConfigFile,omitempty"` + EtcdDataDir *sensorDs.FileInfo `json:"etcdDataDir,omitempty"` + AdminConfigFile *sensorDs.FileInfo `json:"adminConfigFile,omitempty"` + PKIDIr *sensorDs.FileInfo `json:"PKIDir,omitempty"` + PKIFiles []*sensorDs.FileInfo `json:"PKIFiles,omitempty"` } // K8sProcessInfo holds information about a k8s process type K8sProcessInfo struct { // Information about the process specs file (if relevant) - SpecsFile *ds.FileInfo `json:"specsFile,omitempty"` + SpecsFile *sensorDs.FileInfo `json:"specsFile,omitempty"` // Information about the process config file (if relevant) - ConfigFile *ds.FileInfo `json:"configFile,omitempty"` + ConfigFile *sensorDs.FileInfo `json:"configFile,omitempty"` // Information about the process kubeconfig file (if relevant) - KubeConfigFile *ds.FileInfo `json:"kubeConfigFile,omitempty"` + KubeConfigFile *sensorDs.FileInfo `json:"kubeConfigFile,omitempty"` // Information about the process client ca file (if relevant) - ClientCAFile *ds.FileInfo `json:"clientCAFile,omitempty"` + ClientCAFile *sensorDs.FileInfo `json:"clientCAFile,omitempty"` // Raw cmd line of the process CmdLine string `json:"cmdLine"` } type ApiServerInfo struct { - EncryptionProviderConfigFile *ds.FileInfo `json:"encryptionProviderConfigFile,omitempty"` - AuditPolicyFile *ds.FileInfo `json:"auditPolicyFile,omitempty"` + EncryptionProviderConfigFile *sensorDs.FileInfo `json:"encryptionProviderConfigFile,omitempty"` + AuditPolicyFile *sensorDs.FileInfo `json:"auditPolicyFile,omitempty"` *K8sProcessInfo `json:",inline"` } // getEtcdDataDir find the `data-dir` path of etcd k8s component func getEtcdDataDir() (string, error) { - proc, err := utils.LocateProcessByExecSuffix(etcdExe) + proc, err := sensorUtils.LocateProcessByExecSuffix(etcdExe) if err != nil { return "", fmt.Errorf("failed to locate etcd process: %w", err) } @@ -95,12 +93,12 @@ func getEtcdDataDir() (string, error) { return dataDir, nil } -func makeProcessInfoVerbose(ctx context.Context, p *utils.ProcessDetails, specsPath, configPath, kubeConfigPath, clientCaPath string) *K8sProcessInfo { +func makeProcessInfoVerbose(ctx context.Context, p *sensorUtils.ProcessDetails, specsPath, configPath, kubeConfigPath, clientCaPath string) *K8sProcessInfo { ret := K8sProcessInfo{} // init files files := []struct { - data **ds.FileInfo + data **sensorDs.FileInfo path string file string }{ @@ -136,14 +134,14 @@ func makeProcessInfoVerbose(ctx context.Context, p *utils.ProcessDetails, specsP } // makeAPIserverEncryptionProviderConfigFile returns a ds.FileInfo object for the encryption provider config file of the API server. Required for https://workbench.cisecurity.org/sections/1126663/recommendations/1838675 -func makeAPIserverEncryptionProviderConfigFile(ctx context.Context, p *utils.ProcessDetails) *ds.FileInfo { +func makeAPIserverEncryptionProviderConfigFile(ctx context.Context, p *sensorUtils.ProcessDetails) *sensorDs.FileInfo { encryptionProviderConfigPath, ok := p.GetArg(apiEncryptionProviderConfigArg) if !ok { logger.L().Ctx(ctx).Warning("failed to find encryption provider config path", helpers.String("in", "makeAPIserverEncryptionProviderConfigFile")) return nil } - fi, err := utils.MakeContaineredFileInfo(ctx, p, encryptionProviderConfigPath, true) + fi, err := sensorUtils.MakeContaineredFileInfo(ctx, p, encryptionProviderConfigPath, true) if err != nil { logger.L().Ctx(ctx).Warning("failed to create encryption provider config file info", helpers.Error(err)) return nil @@ -223,7 +221,7 @@ func removeEncryptionProviderConfigSecrets(data map[string]interface{}) { } // makeAPIserverAuditPolicyFile returns a ds.FileInfo object for an audit policy file of the API server. Required for https://workbench.cisecurity.org/sections/1126663/recommendations/1838675 -func makeAPIserverAuditPolicyFile(ctx context.Context, p *utils.ProcessDetails) *ds.FileInfo { +func makeAPIserverAuditPolicyFile(ctx context.Context, p *sensorUtils.ProcessDetails) *sensorDs.FileInfo { auditPolicyFilePath, ok := p.GetArg(auditPolicyFileArg) if !ok { logger.L().Info("audit-policy-file argument was not set ", helpers.String("in", "makeAPIserverAuditPolicyFile")) @@ -242,7 +240,7 @@ func SenseControlPlaneInfo(ctx context.Context) (*ControlPlaneInfo, error) { debugInfo := helpers.String("in", "SenseControlPlaneInfo") - apiProc, err := utils.LocateProcessByExecSuffix(apiServerExe) + apiProc, err := sensorUtils.LocateProcessByExecSuffix(apiServerExe) if err == nil { ret.APIServerInfo = &ApiServerInfo{} ret.APIServerInfo.K8sProcessInfo = makeProcessInfoVerbose(ctx, apiProc, apiServerSpecsPath, "", "", "") @@ -252,14 +250,14 @@ func SenseControlPlaneInfo(ctx context.Context) (*ControlPlaneInfo, error) { logger.L().Ctx(ctx).Warning("SenseControlPlaneInfo", helpers.Error(err)) } - controllerMangerProc, err := utils.LocateProcessByExecSuffix(controllerManagerExe) + controllerMangerProc, err := sensorUtils.LocateProcessByExecSuffix(controllerManagerExe) if err == nil { ret.ControllerManagerInfo = makeProcessInfoVerbose(ctx, controllerMangerProc, controllerManagerSpecsPath, controllerManagerConfigPath, "", "") } else { logger.L().Ctx(ctx).Warning("SenseControlPlaneInfo", helpers.Error(err)) } - SchedulerProc, err := utils.LocateProcessByExecSuffix(schedulerExe) + SchedulerProc, err := sensorUtils.LocateProcessByExecSuffix(schedulerExe) if err == nil { ret.SchedulerInfo = makeProcessInfoVerbose(ctx, SchedulerProc, schedulerSpecsPath, schedulerConfigPath, "", "") } else { diff --git a/sensor/controlplane_test.go b/pkg/sensor/controlplane_test.go similarity index 100% rename from sensor/controlplane_test.go rename to pkg/sensor/controlplane_test.go diff --git a/sensor/datastructures/fileinfo.go b/pkg/sensor/datastructures/fileinfo.go similarity index 100% rename from sensor/datastructures/fileinfo.go rename to pkg/sensor/datastructures/fileinfo.go diff --git a/sensor/datastructures/linuxsecurityhardeningstatus.go b/pkg/sensor/datastructures/linuxsecurityhardeningstatus.go similarity index 100% rename from sensor/datastructures/linuxsecurityhardeningstatus.go rename to pkg/sensor/datastructures/linuxsecurityhardeningstatus.go diff --git a/sensor/error.go b/pkg/sensor/error.go similarity index 100% rename from sensor/error.go rename to pkg/sensor/error.go diff --git a/sensor/globals.go b/pkg/sensor/globals.go similarity index 100% rename from sensor/globals.go rename to pkg/sensor/globals.go diff --git a/sensor/internal/utils/containerruntime.go b/pkg/sensor/internal/utils/containerruntime.go similarity index 100% rename from sensor/internal/utils/containerruntime.go rename to pkg/sensor/internal/utils/containerruntime.go diff --git a/sensor/internal/utils/containerruntime_test.go b/pkg/sensor/internal/utils/containerruntime_test.go similarity index 100% rename from sensor/internal/utils/containerruntime_test.go rename to pkg/sensor/internal/utils/containerruntime_test.go diff --git a/sensor/internal/utils/globals.go b/pkg/sensor/internal/utils/globals.go similarity index 100% rename from sensor/internal/utils/globals.go rename to pkg/sensor/internal/utils/globals.go diff --git a/sensor/internal/utils/osuserswrapper.go b/pkg/sensor/internal/utils/osuserswrapper.go similarity index 100% rename from sensor/internal/utils/osuserswrapper.go rename to pkg/sensor/internal/utils/osuserswrapper.go diff --git a/sensor/internal/utils/osuserswrapper_test.go b/pkg/sensor/internal/utils/osuserswrapper_test.go similarity index 100% rename from sensor/internal/utils/osuserswrapper_test.go rename to pkg/sensor/internal/utils/osuserswrapper_test.go diff --git a/sensor/internal/utils/process.go b/pkg/sensor/internal/utils/process.go similarity index 100% rename from sensor/internal/utils/process.go rename to pkg/sensor/internal/utils/process.go diff --git a/sensor/internal/utils/process_test.go b/pkg/sensor/internal/utils/process_test.go similarity index 100% rename from sensor/internal/utils/process_test.go rename to pkg/sensor/internal/utils/process_test.go diff --git a/sensor/internal/utils/service.go b/pkg/sensor/internal/utils/service.go similarity index 100% rename from sensor/internal/utils/service.go rename to pkg/sensor/internal/utils/service.go diff --git a/sensor/internal/utils/testdata/etc/group b/pkg/sensor/internal/utils/testdata/etc/group similarity index 100% rename from sensor/internal/utils/testdata/etc/group rename to pkg/sensor/internal/utils/testdata/etc/group diff --git a/sensor/internal/utils/testdata/etc/passwd b/pkg/sensor/internal/utils/testdata/etc/passwd similarity index 100% rename from sensor/internal/utils/testdata/etc/passwd rename to pkg/sensor/internal/utils/testdata/etc/passwd diff --git a/sensor/internal/utils/testdata/testCNI/containerd.toml b/pkg/sensor/internal/utils/testdata/testCNI/containerd.toml similarity index 100% rename from sensor/internal/utils/testdata/testCNI/containerd.toml rename to pkg/sensor/internal/utils/testdata/testCNI/containerd.toml diff --git a/sensor/internal/utils/testdata/testCNI/containerd_noparams.toml b/pkg/sensor/internal/utils/testdata/testCNI/containerd_noparams.toml similarity index 100% rename from sensor/internal/utils/testdata/testCNI/containerd_noparams.toml rename to pkg/sensor/internal/utils/testdata/testCNI/containerd_noparams.toml diff --git a/sensor/internal/utils/testdata/testCNI/crio.conf b/pkg/sensor/internal/utils/testdata/testCNI/crio.conf similarity index 100% rename from sensor/internal/utils/testdata/testCNI/crio.conf rename to pkg/sensor/internal/utils/testdata/testCNI/crio.conf diff --git a/sensor/internal/utils/testdata/testCNI/crio.d/01_crio.conf b/pkg/sensor/internal/utils/testdata/testCNI/crio.d/01_crio.conf similarity index 100% rename from sensor/internal/utils/testdata/testCNI/crio.d/01_crio.conf rename to pkg/sensor/internal/utils/testdata/testCNI/crio.d/01_crio.conf diff --git a/sensor/internal/utils/testdata/testCNI/crio.d/03_crio.conf b/pkg/sensor/internal/utils/testdata/testCNI/crio.d/03_crio.conf similarity index 100% rename from sensor/internal/utils/testdata/testCNI/crio.d/03_crio.conf rename to pkg/sensor/internal/utils/testdata/testCNI/crio.d/03_crio.conf diff --git a/sensor/internal/utils/testdata/testCNI/crio.d/05_crio.conf b/pkg/sensor/internal/utils/testdata/testCNI/crio.d/05_crio.conf similarity index 100% rename from sensor/internal/utils/testdata/testCNI/crio.d/05_crio.conf rename to pkg/sensor/internal/utils/testdata/testCNI/crio.d/05_crio.conf diff --git a/sensor/internal/utils/testdata/testCNI/crio.d/06_crio.conf b/pkg/sensor/internal/utils/testdata/testCNI/crio.d/06_crio.conf similarity index 100% rename from sensor/internal/utils/testdata/testCNI/crio.d/06_crio.conf rename to pkg/sensor/internal/utils/testdata/testCNI/crio.d/06_crio.conf diff --git a/sensor/internal/utils/testdata/testCNI/crio.d_noparams/05_crio.conf b/pkg/sensor/internal/utils/testdata/testCNI/crio.d_noparams/05_crio.conf similarity index 100% rename from sensor/internal/utils/testdata/testCNI/crio.d_noparams/05_crio.conf rename to pkg/sensor/internal/utils/testdata/testCNI/crio.d_noparams/05_crio.conf diff --git a/sensor/internal/utils/testdata/testCNI/crio.d_noparams/06_crio.conf b/pkg/sensor/internal/utils/testdata/testCNI/crio.d_noparams/06_crio.conf similarity index 100% rename from sensor/internal/utils/testdata/testCNI/crio.d_noparams/06_crio.conf rename to pkg/sensor/internal/utils/testdata/testCNI/crio.d_noparams/06_crio.conf diff --git a/sensor/internal/utils/testdata/testCNI/crio_noparams.conf b/pkg/sensor/internal/utils/testdata/testCNI/crio_noparams.conf similarity index 100% rename from sensor/internal/utils/testdata/testCNI/crio_noparams.conf rename to pkg/sensor/internal/utils/testdata/testCNI/crio_noparams.conf diff --git a/sensor/internal/utils/testdata/test_1 b/pkg/sensor/internal/utils/testdata/test_1 similarity index 100% rename from sensor/internal/utils/testdata/test_1 rename to pkg/sensor/internal/utils/testdata/test_1 diff --git a/sensor/internal/utils/testdata/test_2 b/pkg/sensor/internal/utils/testdata/test_2 similarity index 100% rename from sensor/internal/utils/testdata/test_2 rename to pkg/sensor/internal/utils/testdata/test_2 diff --git a/sensor/internal/utils/utils.go b/pkg/sensor/internal/utils/utils.go similarity index 88% rename from sensor/internal/utils/utils.go rename to pkg/sensor/internal/utils/utils.go index d5cc8b24..b9e844a5 100644 --- a/sensor/internal/utils/utils.go +++ b/pkg/sensor/internal/utils/utils.go @@ -9,8 +9,7 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - ds "github.com/kubescape/host-scanner/sensor/datastructures" - // ds "github.com/kubescape/node-agent/sensor/datastructures" + sensorDs "node-agent/pkg/sensor/datastructures" ) var ( @@ -70,8 +69,8 @@ func IsPathExists(filename string) bool { // MakeFileInfo returns a `ds.FileInfo` object for given path // If `readContent` is set to `true`, it adds the file content // On access error, it returns the error as is -func MakeFileInfo(filePath string, readContent bool) (*ds.FileInfo, error) { - ret := ds.FileInfo{Path: filePath} +func MakeFileInfo(filePath string, readContent bool) (*sensorDs.FileInfo, error) { + ret := sensorDs.FileInfo{Path: filePath} logger.L().Debug("making file info", helpers.String("path", filePath)) @@ -84,7 +83,7 @@ func MakeFileInfo(filePath string, readContent bool) (*ds.FileInfo, error) { // Ownership uid, gid, err := GetFileUNIXOwnership(filePath) - ret.Ownership = &ds.FileOwnership{UID: uid, GID: gid} + ret.Ownership = &sensorDs.FileOwnership{UID: uid, GID: gid} if err != nil { ret.Ownership.Err = err.Error() } @@ -103,7 +102,7 @@ func MakeFileInfo(filePath string, readContent bool) (*ds.FileInfo, error) { // MakeChangedRootFileInfo makes a file info object // for the given path on the given root directory. -func MakeChangedRootFileInfo(ctx context.Context, rootDir string, filePath string, readContent bool) (*ds.FileInfo, error) { +func MakeChangedRootFileInfo(ctx context.Context, rootDir string, filePath string, readContent bool) (*sensorDs.FileInfo, error) { fullPath := path.Join(rootDir, filePath) obj, err := MakeFileInfo(fullPath, readContent) @@ -135,12 +134,12 @@ func MakeChangedRootFileInfo(ctx context.Context, rootDir string, filePath strin // MakeContaineredFileInfo makes a file info object // for a given process file system view. -func MakeContaineredFileInfo(ctx context.Context, p *ProcessDetails, filePath string, readContent bool) (*ds.FileInfo, error) { +func MakeContaineredFileInfo(ctx context.Context, p *ProcessDetails, filePath string, readContent bool) (*sensorDs.FileInfo, error) { return MakeChangedRootFileInfo(ctx, p.RootDir(), filePath, readContent) } // MakeHostFileInfo makes a file info object // for the given path on the host file system. -func makeHostFileInfo(ctx context.Context, filePath string, readContent bool) (*ds.FileInfo, error) { +func makeHostFileInfo(ctx context.Context, filePath string, readContent bool) (*sensorDs.FileInfo, error) { return MakeChangedRootFileInfo(ctx, HostFileSystemDefaultLocation, filePath, readContent) } diff --git a/sensor/internal/utils/utils_test.go b/pkg/sensor/internal/utils/utils_test.go similarity index 86% rename from sensor/internal/utils/utils_test.go rename to pkg/sensor/internal/utils/utils_test.go index 2a3a0117..3b3e0953 100644 --- a/sensor/internal/utils/utils_test.go +++ b/pkg/sensor/internal/utils/utils_test.go @@ -5,11 +5,8 @@ import ( "reflect" "testing" - // "github.com/kubescape/node-agent/sensor/datastructures" - "github.com/kubescape/host-scanner/sensor/datastructures" + sensorDs "node-agent/pkg/sensor/datastructures" - // ds "github.com/kubescape/node-agent/sensor/datastructures" - ds "github.com/kubescape/host-scanner/sensor/datastructures" "github.com/stretchr/testify/assert" ) @@ -92,7 +89,7 @@ func Test_MakeFileInfo(t *testing.T) { tests := []struct { name string args args - want *ds.FileInfo + want *sensorDs.FileInfo wantErr bool }{ { @@ -101,8 +98,8 @@ func Test_MakeFileInfo(t *testing.T) { filePath: "./testdata/test_1", readContent: true, }, - want: &datastructures.FileInfo{ - Ownership: &ds.FileOwnership{}, + want: &sensorDs.FileInfo{ + Ownership: &sensorDs.FileOwnership{}, Path: "./testdata/test_1", Content: []byte("not empty file"), Permissions: 420, @@ -115,8 +112,8 @@ func Test_MakeFileInfo(t *testing.T) { filePath: "./testdata/test_2", readContent: true, }, - want: &datastructures.FileInfo{ - Ownership: &ds.FileOwnership{}, + want: &sensorDs.FileInfo{ + Ownership: &sensorDs.FileOwnership{}, Path: "./testdata/test_2", Content: []byte(""), Permissions: 493, diff --git a/sensor/kernelvariables.go b/pkg/sensor/kernelvariables.go similarity index 100% rename from sensor/kernelvariables.go rename to pkg/sensor/kernelvariables.go diff --git a/sensor/kernelvariables_test.go b/pkg/sensor/kernelvariables_test.go similarity index 100% rename from sensor/kernelvariables_test.go rename to pkg/sensor/kernelvariables_test.go diff --git a/sensor/kubelet.go b/pkg/sensor/kubelet.go similarity index 84% rename from sensor/kubelet.go rename to pkg/sensor/kubelet.go index 601521b9..0faa99e8 100644 --- a/sensor/kubelet.go +++ b/pkg/sensor/kubelet.go @@ -4,13 +4,11 @@ import ( "context" "fmt" + sensorDs "node-agent/pkg/sensor/datastructures" + sensorUtils "node-agent/pkg/sensor/internal/utils" + "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - - // ds "github.com/kubescape/node-agent/sensor/datastructures" - ds "github.com/kubescape/host-scanner/sensor/datastructures" - // "github.com/kubescape/node-agent/sensor/internal/utils" - "github.com/kubescape/host-scanner/sensor/internal/utils" "sigs.k8s.io/yaml" ) @@ -35,39 +33,39 @@ var kubeletKubeConfigDefaultPathList = []string{ type KubeletInfo struct { // ServiceFile is a list of files used to configure the kubelet service. // Most of the times it will be a single file, under /etc/systemd/system/kubelet.service.d. - ServiceFiles []ds.FileInfo `json:"serviceFiles,omitempty"` + ServiceFiles []sensorDs.FileInfo `json:"serviceFiles,omitempty"` // Information about kubelete config file - ConfigFile *ds.FileInfo `json:"configFile,omitempty"` + ConfigFile *sensorDs.FileInfo `json:"configFile,omitempty"` // Information about the kubeconfig file of kubelet - KubeConfigFile *ds.FileInfo `json:"kubeConfigFile,omitempty"` + KubeConfigFile *sensorDs.FileInfo `json:"kubeConfigFile,omitempty"` // Information about the client ca file of kubelet (if exist) - ClientCAFile *ds.FileInfo `json:"clientCAFile,omitempty"` + ClientCAFile *sensorDs.FileInfo `json:"clientCAFile,omitempty"` // Raw cmd line of kubelet process CmdLine string `json:"cmdLine"` } -func LocateKubeletProcess() (*utils.ProcessDetails, error) { - return utils.LocateProcessByExecSuffix(kubeletProcessSuffix) +func LocateKubeletProcess() (*sensorUtils.ProcessDetails, error) { + return sensorUtils.LocateProcessByExecSuffix(kubeletProcessSuffix) } func ReadKubeletConfig(kubeletConfArgs string) ([]byte, error) { - conte, err := utils.ReadFileOnHostFileSystem(kubeletConfArgs) + conte, err := sensorUtils.ReadFileOnHostFileSystem(kubeletConfArgs) logger.L().Debug("raw content", helpers.String("cont", string(conte))) return conte, err } -func makeKubeletServiceFilesInfo(ctx context.Context, pid int) []ds.FileInfo { - files, err := utils.GetKubeletServiceFiles(pid) +func makeKubeletServiceFilesInfo(ctx context.Context, pid int) []sensorDs.FileInfo { + files, err := sensorUtils.GetKubeletServiceFiles(pid) if err != nil { logger.L().Ctx(ctx).Warning("failed to getKubeletServiceFiles", helpers.Error(err)) return nil } - serviceFiles := []ds.FileInfo{} + serviceFiles := []sensorDs.FileInfo{} for _, file := range files { info := makeHostFileInfoVerbose(ctx, file, false, helpers.String("in", "makeProcessInfoVerbose")) if info != nil { diff --git a/sensor/kubelet_test.go b/pkg/sensor/kubelet_test.go similarity index 100% rename from sensor/kubelet_test.go rename to pkg/sensor/kubelet_test.go diff --git a/sensor/kubeproxy.go b/pkg/sensor/kubeproxy.go similarity index 70% rename from sensor/kubeproxy.go rename to pkg/sensor/kubeproxy.go index 831f49b8..450dba37 100644 --- a/sensor/kubeproxy.go +++ b/pkg/sensor/kubeproxy.go @@ -4,11 +4,10 @@ import ( "context" "fmt" + sensorDs "node-agent/pkg/sensor/datastructures" + sensorUtils "node-agent/pkg/sensor/internal/utils" + "github.com/kubescape/go-logger/helpers" - // ds "github.com/kubescape/node-agent/sensor/datastructures" - ds "github.com/kubescape/host-scanner/sensor/datastructures" - // "github.com/kubescape/node-agent/sensor/internal/utils" - "github.com/kubescape/host-scanner/sensor/internal/utils" ) const ( @@ -18,7 +17,7 @@ const ( // KubeProxyInfo holds information about kube-proxy process type KubeProxyInfo struct { // Information about the kubeconfig file of kube-proxy - KubeConfigFile *ds.FileInfo `json:"kubeConfigFile,omitempty"` + KubeConfigFile *sensorDs.FileInfo `json:"kubeConfigFile,omitempty"` // Raw cmd line of kubelet process CmdLine string `json:"cmdLine"` @@ -29,7 +28,7 @@ func SenseKubeProxyInfo(ctx context.Context) (*KubeProxyInfo, error) { ret := KubeProxyInfo{} // Get process - proc, err := utils.LocateProcessByExecSuffix(kubeProxyExe) + proc, err := sensorUtils.LocateProcessByExecSuffix(kubeProxyExe) if err != nil { return &ret, fmt.Errorf("failed to locate kube-proxy process: %w", err) } diff --git a/sensor/network.go b/pkg/sensor/network.go similarity index 100% rename from sensor/network.go rename to pkg/sensor/network.go diff --git a/sensor/network_test.go b/pkg/sensor/network_test.go similarity index 100% rename from sensor/network_test.go rename to pkg/sensor/network_test.go diff --git a/sensor/osrelease.go b/pkg/sensor/osrelease.go similarity index 67% rename from sensor/osrelease.go rename to pkg/sensor/osrelease.go index 06d65976..95ae6585 100644 --- a/sensor/osrelease.go +++ b/pkg/sensor/osrelease.go @@ -9,10 +9,8 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - // ds "github.com/kubescape/node-agent/sensor/datastructures" - // "github.com/kubescape/node-agent/sensor/internal/utils" - ds "github.com/kubescape/host-scanner/sensor/datastructures" - "github.com/kubescape/host-scanner/sensor/internal/utils" + sensorDs "node-agent/pkg/sensor/datastructures" + sensorUtils "node-agent/pkg/sensor/internal/utils" ) const ( @@ -25,13 +23,13 @@ const ( func SenseOsRelease() ([]byte, error) { osFileName, err := getOsReleaseFile() if err == nil { - return utils.ReadFileOnHostFileSystem(path.Join(etcDirName, osFileName)) + return sensorUtils.ReadFileOnHostFileSystem(path.Join(etcDirName, osFileName)) } return []byte{}, fmt.Errorf("failed to find os-release file: %v", err) } func getOsReleaseFile() (string, error) { - hostEtcDir := utils.HostPath(etcDirName) + hostEtcDir := sensorUtils.HostPath(etcDirName) etcDir, err := os.Open(hostEtcDir) if err != nil { return "", fmt.Errorf("failed to open etc dir: %v", err) @@ -50,17 +48,17 @@ func getOsReleaseFile() (string, error) { } func SenseKernelVersion() ([]byte, error) { - return utils.ReadFileOnHostFileSystem(path.Join(procDirName, "version")) + return sensorUtils.ReadFileOnHostFileSystem(path.Join(procDirName, "version")) } func getAppArmorStatus() string { statusStr := "unloaded" - hostAppArmorProfilesFileName := utils.HostPath(appArmorProfilesFileName) + hostAppArmorProfilesFileName := sensorUtils.HostPath(appArmorProfilesFileName) profFile, err := os.Open(hostAppArmorProfilesFileName) if err == nil { defer profFile.Close() statusStr = "stopped" - content, err := utils.ReadFileOnHostFileSystem(appArmorProfilesFileName) + content, err := sensorUtils.ReadFileOnHostFileSystem(appArmorProfilesFileName) if err == nil && len(content) > 0 { statusStr = string(content) } @@ -70,11 +68,11 @@ func getAppArmorStatus() string { func getSELinuxStatus() string { statusStr := "not found" - hostAppArmorProfilesFileName := utils.HostPath(seLinuxConfigFileName) + hostAppArmorProfilesFileName := sensorUtils.HostPath(seLinuxConfigFileName) conFile, err := os.Open(hostAppArmorProfilesFileName) if err == nil { defer conFile.Close() - content, err := utils.ReadFileOnHostFileSystem(appArmorProfilesFileName) + content, err := sensorUtils.ReadFileOnHostFileSystem(appArmorProfilesFileName) if err == nil && len(content) > 0 { statusStr = string(content) } @@ -82,8 +80,8 @@ func getSELinuxStatus() string { return statusStr } -func SenseLinuxSecurityHardening() (*ds.LinuxSecurityHardeningStatus, error) { - res := ds.LinuxSecurityHardeningStatus{} +func SenseLinuxSecurityHardening() (*sensorDs.LinuxSecurityHardeningStatus, error) { + res := sensorDs.LinuxSecurityHardeningStatus{} res.AppArmor = getAppArmorStatus() res.SeLinux = getSELinuxStatus() diff --git a/sensor/testdata/clientCAKubeletConf.yaml b/pkg/sensor/testdata/clientCAKubeletConf.yaml similarity index 100% rename from sensor/testdata/clientCAKubeletConf.yaml rename to pkg/sensor/testdata/clientCAKubeletConf.yaml diff --git a/sensor/testdata/clientCAKubeletConf_2.yaml b/pkg/sensor/testdata/clientCAKubeletConf_2.yaml similarity index 100% rename from sensor/testdata/clientCAKubeletConf_2.yaml rename to pkg/sensor/testdata/clientCAKubeletConf_2.yaml diff --git a/sensor/testdata/clientCAKubeletConf_3.yaml b/pkg/sensor/testdata/clientCAKubeletConf_3.yaml similarity index 100% rename from sensor/testdata/clientCAKubeletConf_3.yaml rename to pkg/sensor/testdata/clientCAKubeletConf_3.yaml diff --git a/sensor/testdata/testmakehostfiles/dir/placeholder.json b/pkg/sensor/testdata/testmakehostfiles/dir/placeholder.json similarity index 100% rename from sensor/testdata/testmakehostfiles/dir/placeholder.json rename to pkg/sensor/testdata/testmakehostfiles/dir/placeholder.json diff --git a/sensor/testdata/testmakehostfiles/file1.yaml b/pkg/sensor/testdata/testmakehostfiles/file1.yaml similarity index 100% rename from sensor/testdata/testmakehostfiles/file1.yaml rename to pkg/sensor/testdata/testmakehostfiles/file1.yaml diff --git a/sensor/testdata/testmakehostfiles/file2.yaml b/pkg/sensor/testdata/testmakehostfiles/file2.yaml similarity index 100% rename from sensor/testdata/testmakehostfiles/file2.yaml rename to pkg/sensor/testdata/testmakehostfiles/file2.yaml diff --git a/sensor/testdata/testmakehostfiles/file3.yaml b/pkg/sensor/testdata/testmakehostfiles/file3.yaml similarity index 100% rename from sensor/testdata/testmakehostfiles/file3.yaml rename to pkg/sensor/testdata/testmakehostfiles/file3.yaml diff --git a/sensor/verboseutils.go b/pkg/sensor/verboseutils.go similarity index 75% rename from sensor/verboseutils.go rename to pkg/sensor/verboseutils.go index d33f02a9..f0875b08 100644 --- a/sensor/verboseutils.go +++ b/pkg/sensor/verboseutils.go @@ -9,13 +9,11 @@ import ( "path" "sort" + sensorDs "node-agent/pkg/sensor/datastructures" + sensorUtils "node-agent/pkg/sensor/internal/utils" + "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - - // ds "github.com/kubescape/node-agent/sensor/datastructures" - // "github.com/kubescape/node-agent/sensor/internal/utils" - ds "github.com/kubescape/host-scanner/sensor/datastructures" - "github.com/kubescape/host-scanner/sensor/internal/utils" ) const ( @@ -25,15 +23,16 @@ const ( // makeHostFileInfoVerbose makes a file info object // for the given path on the host file system, and with error logging. // It returns nil on error. -func makeHostFileInfoVerbose(ctx context.Context, path string, readContent bool, failMsgs ...helpers.IDetails) *ds.FileInfo { - return makeChangedRootFileInfoVerbose(ctx, utils.HostFileSystemDefaultLocation, path, readContent, failMsgs...) +func makeHostFileInfoVerbose(ctx context.Context, path string, readContent bool, failMsgs ...helpers.IDetails) *sensorDs.FileInfo { + return makeChangedRootFileInfoVerbose(ctx, sensorUtils.HostFileSystemDefaultLocation, path, readContent, failMsgs...) } // makeContaineredFileInfoFromListVerbose makes a file info object // for a given process file system view, and with error logging. // It tries to find the file in the given list of paths, by the order of the list. // It returns nil on error. -func makeContaineredFileInfoFromListVerbose(ctx context.Context, p *utils.ProcessDetails, filePathList []string, readContent bool, failMsgs ...helpers.IDetails) *ds.FileInfo { +func makeContaineredFileInfoFromListVerbose(ctx context.Context, p *sensorUtils.ProcessDetails, + filePathList []string, readContent bool, failMsgs ...helpers.IDetails) *sensorDs.FileInfo { for _, filePath := range filePathList { fileInfo := makeChangedRootFileInfoVerbose(ctx, p.RootDir(), filePath, readContent, failMsgs...) @@ -47,14 +46,16 @@ func makeContaineredFileInfoFromListVerbose(ctx context.Context, p *utils.Proces // makeContaineredFileInfoVerbose makes a file info object // for a given process file system view, and with error logging. // It returns nil on error. -func makeContaineredFileInfoVerbose(ctx context.Context, p *utils.ProcessDetails, filePath string, readContent bool, failMsgs ...helpers.IDetails) *ds.FileInfo { +func makeContaineredFileInfoVerbose(ctx context.Context, p *sensorUtils.ProcessDetails, filePath string, readContent bool, + failMsgs ...helpers.IDetails) *sensorDs.FileInfo { return makeChangedRootFileInfoVerbose(ctx, p.RootDir(), filePath, readContent, failMsgs...) } // makeChangedRootFileInfoVerbose makes a file info object // for the given path on the given root directory, and with error logging. -func makeChangedRootFileInfoVerbose(ctx context.Context, rootDir string, path string, readContent bool, failMsgs ...helpers.IDetails) *ds.FileInfo { - fileInfo, err := utils.MakeChangedRootFileInfo(ctx, rootDir, path, readContent) +func makeChangedRootFileInfoVerbose(ctx context.Context, rootDir string, path string, readContent bool, + failMsgs ...helpers.IDetails) *sensorDs.FileInfo { + fileInfo, err := sensorUtils.MakeChangedRootFileInfo(ctx, rootDir, path, readContent) if err != nil { logArgs := append([]helpers.IDetails{ helpers.String("path", path), @@ -70,15 +71,16 @@ func makeChangedRootFileInfoVerbose(ctx context.Context, rootDir string, path st // makeHostDirFilesInfo iterate over a directory and make a list of // file infos for all the files inside it. If `recursive` is set to true, // the file infos will be added recursively until `maxRecursionDepth` is reached -func makeHostDirFilesInfoVerbose(ctx context.Context, dir string, recursive bool, fileInfos *[]*ds.FileInfo, recursionLevel int) ([]*ds.FileInfo, error) { - dirInfo, err := os.Open(utils.HostPath(dir)) +func makeHostDirFilesInfoVerbose(ctx context.Context, dir string, recursive bool, fileInfos *[]*sensorDs.FileInfo, + recursionLevel int) ([]*sensorDs.FileInfo, error) { + dirInfo, err := os.Open(sensorUtils.HostPath(dir)) if err != nil { return nil, fmt.Errorf("failed to open dir at %s: %w", dir, err) } defer dirInfo.Close() if fileInfos == nil { - fileInfos = &([]*ds.FileInfo{}) + fileInfos = &([]*sensorDs.FileInfo{}) } var fileNames []string @@ -89,7 +91,7 @@ func makeHostDirFilesInfoVerbose(ctx context.Context, dir string, recursive bool filePath := path.Join(dir, fileNames[i]) // Check if is directory - stats, err := os.Stat(utils.HostPath(filePath)) + stats, err := os.Stat(sensorUtils.HostPath(filePath)) if err != nil { logger.L().Ctx(ctx).Warning("failed to get file stats", helpers.String("in", "makeHostDirFilesInfo"), @@ -126,6 +128,5 @@ func makeHostDirFilesInfoVerbose(ctx context.Context, dir string, recursive bool if errors.Is(err, io.EOF) { err = nil } - return *fileInfos, err } diff --git a/sensor/verboseutils_test.go b/pkg/sensor/verboseutils_test.go similarity index 90% rename from sensor/verboseutils_test.go rename to pkg/sensor/verboseutils_test.go index fe1eb494..45267fbc 100644 --- a/sensor/verboseutils_test.go +++ b/pkg/sensor/verboseutils_test.go @@ -6,13 +6,14 @@ import ( "regexp" "testing" + sensorUtils "node-agent/pkg/sensor/internal/utils" + "github.com/kubescape/go-logger" - "github.com/kubescape/node-agent/sensor/internal/utils" "github.com/stretchr/testify/assert" ) func Test_makeHostDirFilesInfo(t *testing.T) { - utils.HostFileSystemDefaultLocation = "." + sensorUtils.HostFileSystemDefaultLocation = "." fileInfos, err := makeHostDirFilesInfoVerbose(context.TODO(), "testdata/testmakehostfiles", true, nil, 0) assert.NoError(t, err) assert.Len(t, fileInfos, 4)