diff --git a/KubeArmor/core/kubeArmor.go b/KubeArmor/core/kubeArmor.go index f0ac48fcd4..c4e455015a 100644 --- a/KubeArmor/core/kubeArmor.go +++ b/KubeArmor/core/kubeArmor.go @@ -778,6 +778,14 @@ func KubeArmor() { } } + var runtimeLabel, socketLabel string + if v, ok := dm.Node.Labels["kubearmor.io/runtime"]; ok { + runtimeLabel = v + } + if v, ok := dm.Node.Labels["kubearmor.io/socket"]; ok { + socketLabel = v + } + if dm.K8sEnabled && cfg.GlobalCfg.Policy { if cfg.GlobalCfg.UseOCIHooks && (strings.Contains(dm.Node.ContainerRuntimeVersion, "cri-o") || @@ -817,6 +825,29 @@ func KubeArmor() { dm.Logger.Printf("Using %s for monitoring containers", cfg.GlobalCfg.CRISocket) + } else if runtimeLabel != "" && socketLabel != "" { // use runtime and CRI socket detected by snitch + + dm.Logger.Print("Using runtime and CRI socket detected by snitch") + + // socketLabel is like "run_containerd_containerd.sock", change it to "/run/containerd/containerd.sock" + socketFilePath := "/" + strings.ReplaceAll(socketLabel, "_", "/") + cfg.GlobalCfg.CRISocket = "unix://" + socketFilePath + + switch runtimeLabel { + case "docker": + // update already deployed containers + dm.GetAlreadyDeployedDockerContainers() + + // monitor docker events + go dm.MonitorDockerEvents() + case "containerd": + // monitor containerd events + go dm.MonitorContainerdEvents() + case "cri-o": + // monitor cri-o events + go dm.MonitorCrioEvents() + } + } else { // CRI socket not set, we'll have to auto detect dm.Logger.Print("CRI socket not set. Trying to detect.") diff --git a/pkg/KubeArmorOperator/cmd/snitch-cmd/main.go b/pkg/KubeArmorOperator/cmd/snitch-cmd/main.go index 07a7fcde90..06a191a402 100644 --- a/pkg/KubeArmorOperator/cmd/snitch-cmd/main.go +++ b/pkg/KubeArmorOperator/cmd/snitch-cmd/main.go @@ -85,9 +85,9 @@ restricts the behavior (such as process execution, file access, and networking operation) of containers at the system level. Socket Detection: -- By default, snitch auto-detects CRI sockets using a predefined search order +- By default, snitch auto-detects CRI sockets by creating client for them and sending a request with snitch's container ID to check if it has the container or not - Use --socket-file to explicitly specify the socket path when multiple runtimes are present -- Example: --socket-file /run/k3s/containerd/containerd. sock +- Example: --socket-file /run/k3s/containerd/containerd.sock `, SilenceUsage: true, @@ -146,7 +146,7 @@ func snitch() { } // Detecting runtime - runtime, socket, nriSocket := runtimepkg.DetectRuntimeViaMap(PathPrefix, o.Runtime, SocketFile, *Logger) + runtime, socket, nriSocket := runtimepkg.DetectRuntimeViaMap(PathPrefix, o.Runtime, SocketFile, *Logger, K8sClient) if runtime != "NA" { Logger.Infof("Detected %s as node runtime, runtime socket=%s", runtime, socket) } else { diff --git a/pkg/KubeArmorOperator/go.mod b/pkg/KubeArmorOperator/go.mod index 50ea533507..01cc9934cc 100644 --- a/pkg/KubeArmorOperator/go.mod +++ b/pkg/KubeArmorOperator/go.mod @@ -7,11 +7,14 @@ replace ( github.com/kubearmor/KubeArmor/KubeArmor => ../../KubeArmor github.com/kubearmor/KubeArmor/deployments => ../../deployments github.com/kubearmor/KubeArmor/pkg/KubeArmorController => ../KubeArmorController + k8s.io/cri-api => k8s.io/cri-api v0.25.16 ) require ( + github.com/containerd/containerd/v2 v2.1.5 + github.com/docker/docker v28.4.0+incompatible github.com/kubearmor/KubeArmor/KubeArmor v0.0.0-20250701060635-600e11526ec1 - github.com/kubearmor/KubeArmor/deployments v0.0.0-20250701060635-600e11526ec1 + github.com/kubearmor/KubeArmor/deployments v0.0.0-00010101000000-000000000000 github.com/kubearmor/KubeArmor/pkg/KubeArmorController v0.0.0-20250701060635-600e11526ec1 github.com/opencontainers/runtime-spec v1.2.1 github.com/spf13/cobra v1.10.1 @@ -75,8 +78,11 @@ require ( ) require ( + github.com/containerd/plugin v1.0.0 // indirect github.com/containers/storage v1.59.1 // indirect github.com/cyphar/filepath-securejoin v0.5.1 // indirect + github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/go-openapi/swag/cmdutils v0.24.0 // indirect github.com/go-openapi/swag/conv v0.24.0 // indirect github.com/go-openapi/swag/fileutils v0.24.0 // indirect @@ -89,6 +95,9 @@ require ( github.com/go-openapi/swag/typeutils v0.24.0 // indirect github.com/go-openapi/swag/yamlutils v0.24.0 // indirect github.com/kubearmor/KubeArmor/protobuf v0.0.0-20250916032022-5864f940ceca // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/sys/atomicwriter v0.1.0 // indirect + github.com/morikuni/aec v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) diff --git a/pkg/KubeArmorOperator/go.sum b/pkg/KubeArmorOperator/go.sum index 80c05c19dd..1b0e2806cb 100644 --- a/pkg/KubeArmorOperator/go.sum +++ b/pkg/KubeArmorOperator/go.sum @@ -3,11 +3,16 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20250520111509-a70c2aa677fa h1:x6kFzdPgBoLbyoNkA/jny0ENpoEz4wqY8lPTQL2DPkg= github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20250520111509-a70c2aa677fa/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA= github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cilium/ebpf v0.19.1-0.20251013125301-c27ff922fc6a h1:fJ1fWcsXJzuYqUXC4I5IOs/Liu+8L5addmXkdO2v55s= github.com/cilium/ebpf v0.19.1-0.20251013125301-c27ff922fc6a/go.mod h1:pzLjFymM+uZPLk/IXZUL63xdx5VXEo+enTzxkZXdycw= @@ -15,10 +20,14 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo= github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= +github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= +github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= github.com/containerd/containerd v1.7.29 h1:90fWABQsaN9mJhGkoVnuzEY+o1XDPbg9BTC9QTAHnuE= github.com/containerd/containerd v1.7.29/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs= github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0= github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI= +github.com/containerd/containerd/v2 v2.1.5 h1:pWSmPxUszaLZKQPvOx27iD4iH+aM6o0BoN9+hg77cro= +github.com/containerd/containerd/v2 v2.1.5/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -31,10 +40,14 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E= github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y= +github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8= github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= +github.com/containers/common v0.51.4 h1:1m3D9lPYgY7sS4Xod962rCEZTsOlR2nuAbYFhzopME4= +github.com/containers/common v0.51.4/go.mod h1:CVSTmQWOs6IbjOZW7ik+7QggrOR3gzKc6gqYfRipl1c= github.com/containers/common v0.64.2 h1:1xepE7QwQggUXxmyQ1Dbh6Cn0yd7ktk14sN3McSWf5I= github.com/containers/common v0.64.2/go.mod h1:o29GfYy4tefUuShm8mOn2AiL5Mpzdio+viHI7n24KJ4= github.com/containers/storage v1.59.1 h1:11Zu68MXsEQGBBd+GadPrHPpWeqjKS8hJDGiAHgIqDs= @@ -48,8 +61,14 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk= +github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-events v0.0.0-20250808211157-605354379745 h1:yOn6Ze6IbYI/KAw2lw/83ELYvZh6hvsygTVkD0dzMC4= github.com/docker/go-events v0.0.0-20250808211157-605354379745/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -137,6 +156,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -161,10 +182,14 @@ github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8 github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= +github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= @@ -175,12 +200,16 @@ github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= +github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= @@ -251,6 +280,10 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= @@ -259,6 +292,8 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6 go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= +go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -339,6 +374,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20250908214217-97024824d090 h1:ywCL7vA2n3vVHyf+bx1ZV/knaTPRI8GIeKY0MEhEeOc= google.golang.org/genproto v0.0.0-20250908214217-97024824d090/go.mod h1:zwJI9HzbJJlw2KXy0wX+lmT2JuZoaKK9JC4ppqmxxjk= +google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g= +google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668= google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -369,6 +406,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= @@ -379,8 +418,8 @@ k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= -k8s.io/cri-api v0.34.1 h1:n2bU++FqqJq0CNjP/5pkOs0nIx7aNpb1Xa053TecQkM= -k8s.io/cri-api v0.34.1/go.mod h1:4qVUjidMg7/Z9YGZpqIDygbkPWkg3mkS1PvOx/kpHTE= +k8s.io/cri-api v0.25.16 h1:NSa4jRRP8zvEKBVfnWvMEaemvAsMXw5UQkLPQCRa2cc= +k8s.io/cri-api v0.25.16/go.mod h1:0vfZsZhEs3LoY5Lqm6Zgx43M633ToNlTUtEQc+VvXhw= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= diff --git a/pkg/KubeArmorOperator/internal/controller/resources.go b/pkg/KubeArmorOperator/internal/controller/resources.go index 9764fdb57a..696f7d5a83 100755 --- a/pkg/KubeArmorOperator/internal/controller/resources.go +++ b/pkg/KubeArmorOperator/internal/controller/resources.go @@ -279,6 +279,7 @@ func genSnitchRole() *rbacv1.ClusterRole { }, Resources: []string{ "nodes", + "pods", }, }, }, @@ -356,6 +357,18 @@ func deploySnitch(nodename string, runtime string) *batchv1.Job { Name: "KUBEARMOR_OCI_HOOKS", Value: strconv.FormatBool(common.EnableOCIHooks), }, + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }}, + }, + { + Name: "POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }}, + }, }, ImagePullPolicy: corev1.PullIfNotPresent, VolumeMounts: []corev1.VolumeMount{ diff --git a/pkg/KubeArmorOperator/runtime/runtime.go b/pkg/KubeArmorOperator/runtime/runtime.go index 232ca1d00f..98d9be1130 100644 --- a/pkg/KubeArmorOperator/runtime/runtime.go +++ b/pkg/KubeArmorOperator/runtime/runtime.go @@ -4,14 +4,29 @@ package runtime import ( + "context" "fmt" "os" "path/filepath" "strings" + containerd "github.com/containerd/containerd/v2/client" + "github.com/containerd/containerd/v2/pkg/namespaces" + "github.com/docker/docker/client" "github.com/kubearmor/KubeArmor/KubeArmor/log" "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/common" "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + criV1 "k8s.io/cri-api/pkg/apis/runtime/v1" + criV1alpha2 "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" +) + +const ( + containerdK8sNs = "k8s.io" + containerdDockerNs = "moby" ) func DetectNRI(pathPrefix string) (string, error) { @@ -25,7 +40,136 @@ func DetectNRI(pathPrefix string) (string, error) { return "NA", fmt.Errorf("NRI not available") } -func DetectRuntimeViaMap(pathPrefix string, k8sRuntime string, explicitSocket string, log zap.SugaredLogger) (string, string, string) { +func dockerSockHasContainer(containerID, sockPath string) bool { + cli, err := client.NewClientWithOpts(client.WithHost("unix://"+sockPath), client.WithAPIVersionNegotiation()) + if err != nil { + log.Warnf("Error in creating docker client: %v", err) + return false + } + defer cli.Close() + + _, err = cli.ContainerInspect(context.Background(), containerID) + if err != nil { + return false + } + + return true +} + +func containerdSockHasContainer(containerID, sockPath string) bool { + client, err := containerd.New(sockPath) + if err != nil { + log.Warnf("Error in creating containerd client: %v", err) + return false + } + defer client.Close() + + k8sNsCtx := namespaces.WithNamespace(context.Background(), containerdK8sNs) + dockerNsCtx := namespaces.WithNamespace(context.Background(), containerdDockerNs) + + // first check in k8s ns + _, err = client.LoadContainer(k8sNsCtx, containerID) + if err != nil { + log.Warn("Container not found in K8s namespace") + } else { + return true + } + + // check in docker ns also + _, err = client.LoadContainer(dockerNsCtx, containerID) + if err != nil { + log.Warn("Container not found in Docker namespace") + return false + } + + return true +} + +func dockershimSockHasContainer(containerID, sockPath string) bool { + conn, err := grpc.Dial("unix://"+sockPath, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Warnf("Error in creating dockershim client: %v", err) + return false + } + + dockershimClient := criV1alpha2.NewRuntimeServiceClient(conn) + + req := &criV1alpha2.ContainerStatusRequest{ + ContainerId: containerID, + Verbose: true, + } + + _, err = dockershimClient.ContainerStatus(context.Background(), req) + if err != nil { + return false + } + + return true +} + +func crioSockHasContainer(containerID, sockPath string) bool { + conn, err := grpc.NewClient("unix://"+sockPath, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Warnf("Error in creating crio client: %v", err) + return false + } + + crioClient := criV1.NewRuntimeServiceClient(conn) + + req := &criV1.ContainerStatusRequest{ + ContainerId: containerID, + Verbose: true, + } + + _, err = crioClient.ContainerStatus(context.Background(), req) + if err != nil { + return false + } + + return true +} + +func runtimeSockHasContainer(containerID, sockPath string) (bool, string) { + // try all in case custom runtime wrapper is used + + if containerdSockHasContainer(containerID, sockPath) { + return true, "containerd" + } + + if crioSockHasContainer(containerID, sockPath) { + return true, "cri-o" + } + + if dockerSockHasContainer(containerID, sockPath) { + return true, "docker" + } + + if dockershimSockHasContainer(containerID, sockPath) { + return true, "docker" + } + + return false, "" +} + +func DetectRuntimeViaMap(pathPrefix string, k8sRuntime string, explicitSocket string, log zap.SugaredLogger, cl *kubernetes.Clientset) (string, string, string) { + + // get container ID of snitch + podName := os.Getenv("POD_NAME") + namespace := os.Getenv("POD_NAMESPACE") + pod, err := cl.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{}) + if err != nil { + log.Errorf("Error in getting pod info: %v\n", err) + return "NA", "NA", "NA" + } + id := pod.Status.ContainerStatuses[0].ContainerID // ://, for example containerd://abcdef123456..... + containerID := id + if idx := strings.Index(id, "://"); idx != -1 { + containerID = id[idx+3:] + } else { + log.Errorf("Unexpected ContainerID format: %s", id) + return "NA", "NA", "NA" + } + if explicitSocket != "" { log.Infof("Using explicit socket file: %s", explicitSocket) @@ -39,17 +183,18 @@ func DetectRuntimeViaMap(pathPrefix string, k8sRuntime string, explicitSocket st checkPath := filepath.Clean(pathPrefix + hostPath) if _, err := os.Stat(checkPath); err == nil || os.IsPermission(err) { - runtime := determineRuntimeFromSocket(hostPath) - - if (runtime == "docker" && strings.Contains(hostPath, "containerd")) || runtime == "containerd" { - if nriPath, err := DetectNRI(pathPrefix); err == nil { - return runtime, hostPath, nriPath - } else { - log.Warnf("NRI detection failed: %s", err) + found, runtime := runtimeSockHasContainer(containerID, checkPath) + if found { + if (runtime == "docker" && strings.Contains(hostPath, "containerd")) || runtime == "containerd" { + if nriPath, err := DetectNRI(pathPrefix); err == nil { + return runtime, hostPath, nriPath + } else { + log.Warnf("NRI detection failed: %s", err) + } } - } - return runtime, hostPath, "" + return runtime, hostPath, "" + } } else { log.Errorf("Explicit socket file not accessible: %s", err) return "NA", "NA", "NA" @@ -57,29 +202,34 @@ func DetectRuntimeViaMap(pathPrefix string, k8sRuntime string, explicitSocket st } log.Infof("Checking for %s socket\n", k8sRuntime) + if k8sRuntime != "" { for _, path := range common.ContainerRuntimeSocketMap[k8sRuntime] { if _, err := os.Stat(pathPrefix + path); err == nil || os.IsPermission(err) { - if (k8sRuntime == "docker" && strings.Contains(path, "containerd")) || k8sRuntime == "containerd" { - if nriPath, err := DetectNRI(pathPrefix); err == nil { - return k8sRuntime, path, nriPath - } else { - log.Warnf("%s", err) + found, k8sRuntime := runtimeSockHasContainer(containerID, pathPrefix+path) + if found { + if (k8sRuntime == "docker" && strings.Contains(path, "containerd")) || k8sRuntime == "containerd" { + if nriPath, err := DetectNRI(pathPrefix); err == nil { + return k8sRuntime, path, nriPath + } else { + log.Warnf("%s", err) + } } + return k8sRuntime, path, "" } - k8sRuntime := determineRuntimeFromSocket(path) - return k8sRuntime, path, "" } else { log.Warnf("%s", err) } } } log.Warn("Couldn't detect k8s runtime location, searching for other runtime sockets") - for runtime, paths := range common.ContainerRuntimeSocketMap { + for _, paths := range common.ContainerRuntimeSocketMap { for _, path := range paths { if _, err := os.Stat(pathPrefix + path); err == nil || os.IsPermission(err) { - runtime = determineRuntimeFromSocket(path) - return runtime, path, "" + found, k8sRuntime := runtimeSockHasContainer(containerID, pathPrefix+path) + if found { + return k8sRuntime, path, "" + } } else { log.Warnf("%s", err) } @@ -88,17 +238,3 @@ func DetectRuntimeViaMap(pathPrefix string, k8sRuntime string, explicitSocket st log.Warn("Couldn't detect runtime") return "NA", "NA", "NA" } - -// Determine runtime from socket path -func determineRuntimeFromSocket(socketPath string) string { - socketPath = strings.ToLower(socketPath) - - if strings.Contains(socketPath, "docker") { - return "docker" - } else if strings.Contains(socketPath, "containerd") { - return "containerd" - } else if strings.Contains(socketPath, "cri-o") || strings.Contains(socketPath, "crio") { - return "cri-o" - } - return "containerd" -} diff --git a/pkg/KubeArmorOperator/runtime/runtime_test.go b/pkg/KubeArmorOperator/runtime/runtime_test.go deleted file mode 100644 index b131ae42f4..0000000000 --- a/pkg/KubeArmorOperator/runtime/runtime_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2026 Authors of KubeArmor - -package runtime - -import ( - "os" - "path/filepath" - "testing" - - "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/common" - "go.uber.org/zap" -) - -func TestDetectRuntimeViaMapWithExplicitSocket(t *testing.T) { - logger := zap.NewNop().Sugar() - - // Test case 1: Valid explicit socket - t.Run("ValidExplicitSocket", func(t *testing.T) { - pathPrefix := t.TempDir() - socketPath := "/var/run/containerd/containerd.sock" - fullPath := filepath.Clean(pathPrefix + socketPath) - - if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil { - t.Fatalf("Failed to create directories: %v", err) - } - if err := os.WriteFile(fullPath, []byte(""), 0o644); err != nil { - t.Fatalf("Failed to create socket file: %v", err) - } - - runtime, socket, _ := DetectRuntimeViaMap(pathPrefix, "", socketPath, *logger) - - if runtime != "containerd" { - t.Errorf("Expected runtime 'containerd', got '%s'", runtime) - } - - if socket != socketPath { - t.Errorf("Expected socket '%s', got '%s'", socketPath, socket) - } - }) - - // Test case 2: Non-existent explicit socket - t.Run("NonExistentExplicitSocket", func(t *testing.T) { - runtime, socket, _ := DetectRuntimeViaMap("", "", "/nonexistent/socket.sock", *logger) - - if runtime != "NA" { - t.Errorf("Expected runtime 'NA', got '%s'", runtime) - } - - if socket != "NA" { - t.Errorf("Expected socket 'NA', got '%s'", socket) - } - }) - - // Test case 3: Relative explicit socket should be rejected - t.Run("RelativeExplicitSocket", func(t *testing.T) { - runtime, socket, _ := DetectRuntimeViaMap("", "", "containerd/containerd.sock", *logger) - - if runtime != "NA" { - t.Errorf("Expected runtime 'NA' for relative path, got '%s'", runtime) - } - - if socket != "NA" { - t.Errorf("Expected socket 'NA' for relative path, got '%s'", socket) - } - }) -} - -func TestDetermineRuntimeFromSocket(t *testing.T) { - testCases := []struct { - socketPath string - expectedRuntime string - }{ - {"/var/run/docker.sock", "docker"}, - {"/run/containerd/containerd.sock", "containerd"}, - {"/var/run/crio/crio.sock", "cri-o"}, - {"/run/k3s/containerd/containerd.sock", "containerd"}, - {"/some/unknown/path.sock", "containerd"}, // fallback - } - - for _, tc := range testCases { - t.Run(tc.socketPath, func(t *testing.T) { - result := determineRuntimeFromSocket(tc.socketPath) - if result != tc.expectedRuntime { - t.Errorf("For socket %s, expected runtime %s, got %s", - tc.socketPath, tc.expectedRuntime, result) - } - }) - } -} - -func TestDetectRuntimeViaMapAutoDetection(t *testing.T) { - logger := zap.NewNop().Sugar() - - originalMap := common.ContainerRuntimeSocketMap - t.Cleanup(func() { - common.ContainerRuntimeSocketMap = originalMap - }) - - socketPath := "/var/run/containerd/containerd.sock" - common.ContainerRuntimeSocketMap = map[string][]string{ - "containerd": {socketPath}, - } - - pathPrefix := t.TempDir() - fullPath := filepath.Clean(pathPrefix + socketPath) - - if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil { - t.Fatalf("Failed to create directories: %v", err) - } - if err := os.WriteFile(fullPath, []byte(""), 0o644); err != nil { - t.Fatalf("Failed to write socket file: %v", err) - } - - runtime, socket, _ := DetectRuntimeViaMap(pathPrefix, "containerd", "", *logger) - - if runtime != "containerd" { - t.Fatalf("Expected runtime 'containerd', got '%s'", runtime) - } - - if socket != socketPath { - t.Fatalf("Expected socket '%s', got '%s'", socketPath, socket) - } -}