From 80ffc6bae156910b7ab8171d723fd3229b6b67a8 Mon Sep 17 00:00:00 2001 From: Aleksandar Savchev Date: Fri, 27 Mar 2026 15:32:24 +0200 Subject: [PATCH 1/2] Add worker pool --- pkg/kubernetes/pod/workerpool.go | 238 ++++++++++++++++++++++++++ pkg/kubernetes/pod/workerpool_test.go | 233 +++++++++++++++++++++++++ 2 files changed, 471 insertions(+) create mode 100644 pkg/kubernetes/pod/workerpool.go create mode 100644 pkg/kubernetes/pod/workerpool_test.go diff --git a/pkg/kubernetes/pod/workerpool.go b/pkg/kubernetes/pod/workerpool.go new file mode 100644 index 000000000..b6fb6b22c --- /dev/null +++ b/pkg/kubernetes/pod/workerpool.go @@ -0,0 +1,238 @@ +// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package pod + +import ( + "context" + "errors" + "fmt" + "hash/fnv" + "maps" + "sync" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + + stringgen "github.com/gardener/diki/pkg/internal/stringgen" + "github.com/gardener/diki/pkg/rule" +) + +var _ PodContext = &PodWorkerPool{} + +// SelectNodesFn matches the signature of kubeutils.SelectNodes. +type SelectNodesFn func( + nodes []corev1.Node, + nodesAllocatablePods map[string]int, + labels []string, +) ([]corev1.Node, []rule.CheckResult) + +// SelectPodOfReferenceGroupFn matches the signature of kubeutils.SelectPodOfReferenceGroup. +type SelectPodOfReferenceGroupFn func( + pods []corev1.Pod, + replicaSets []appsv1.ReplicaSet, + nodesAllocatablePods map[string]int, + target rule.Target, +) (map[string][]corev1.Pod, []rule.CheckResult) + +// NodeConstructorFn builds a pod-constructor closure for the given node. +type NodeConstructorFn func(nodeName string) func() *corev1.Pod + +// NamedPodExecutor wraps a PodExecutor and carries the actual pod name and namespace. +// Rules that need to look up the exec pod by name after creation can type-assert the +// PodExecutor returned by PodWorkerPool.Create to *NamedPodExecutor to get the real name. +type NamedPodExecutor struct { + PodExecutor + PodName string + PodNamespace string +} + +type podRecord struct { + name string + namespace string +} + +// PodWorkerPool is a PodContext implementation that creates pods lazily — +// at most one pod per node — and reuses the running pod across multiple rules. +// Delete is a no-op, all cleanup is done via CleanupAll after all rules finish. +type PodWorkerPool struct { + podContext PodContext + selectNodesFn SelectNodesFn + selectPodOfReferenceGroupFn SelectPodOfReferenceGroupFn + nodeConstructorFn NodeConstructorFn + Generator stringgen.StringGenerator + mu sync.Mutex // protects executors, podNames + executors map[string]PodExecutor + podNames map[string]podRecord +} + +// NewPodWorkerPool creates a new PodWorkerPool backed by the given PodContext. +func NewPodWorkerPool( + podContext PodContext, + selectNodesFn SelectNodesFn, + selectPodOfReferenceGroupFn SelectPodOfReferenceGroupFn, + nodeConstructorFn NodeConstructorFn, +) *PodWorkerPool { + return &PodWorkerPool{ + podContext: podContext, + selectNodesFn: selectNodesFn, + selectPodOfReferenceGroupFn: selectPodOfReferenceGroupFn, + nodeConstructorFn: nodeConstructorFn, + Generator: stringgen.Default(), + executors: map[string]PodExecutor{}, + podNames: map[string]podRecord{}, + } +} + +// SelectNodes adjusts allocatable pod counts to prefer nodes that already have a +// pooled executor, delegates to the injected SelectNodesFn, and eagerly creates +// pods on the selected nodes so that subsequent rules see the same pool state. +func (p *PodWorkerPool) SelectNodes( + ctx context.Context, + nodes []corev1.Node, + nodesAllocatablePods map[string]int, + labels []string, +) ([]corev1.Node, []rule.CheckResult) { + defer p.mu.Unlock() + p.mu.Lock() + + adjusted := p.adjustAllocatablePods(nodesAllocatablePods) + selectedNodes, checks := p.selectNodesFn(nodes, adjusted, labels) + + for _, node := range selectedNodes { + _, err := p.create(ctx, p.nodeConstructorFn(node.Name)) + if err != nil { + checks = append(checks, rule.ErroredCheckResult(err.Error(), rule.NewTarget())) + } + } + + return selectedNodes, checks +} + +// SelectPodOfReferenceGroup adjusts allocatable pod counts to prefer nodes that +// already have a pooled executor, delegates to the injected SelectPodOfReferenceGroupFn, +// and eagerly creates pods on the selected nodes. +func (p *PodWorkerPool) SelectPodOfReferenceGroup( + ctx context.Context, + pods []corev1.Pod, + replicaSets []appsv1.ReplicaSet, + nodesAllocatablePods map[string]int, + target rule.Target, +) (map[string][]corev1.Pod, []rule.CheckResult) { + defer p.mu.Unlock() + p.mu.Lock() + + adjusted := p.adjustAllocatablePods(nodesAllocatablePods) + groupedPods, checks := p.selectPodOfReferenceGroupFn(pods, replicaSets, adjusted, target) + + for nodeName := range groupedPods { + _, err := p.create(ctx, p.nodeConstructorFn(nodeName)) + if err != nil { + checks = append(checks, rule.ErroredCheckResult(err.Error(), target)) + } + } + + return groupedPods, checks +} + +// Create returns a PodExecutor for the node targeted by constructorFn. +// If a pod has already been created for that node it is reused; otherwise a new pod +// is created via the underlying PodContext. +// The returned PodExecutor is always a *NamedPodExecutor so callers can retrieve the +// actual pod name via a type assertion. +func (p *PodWorkerPool) Create(ctx context.Context, constructorFn func() *corev1.Pod) (PodExecutor, error) { + defer p.mu.Unlock() + p.mu.Lock() + + return p.create(ctx, constructorFn) +} + +func (p *PodWorkerPool) create(ctx context.Context, constructorFn func() *corev1.Pod) (PodExecutor, error) { + podSpec := constructorFn() + nodeName := podSpec.Spec.NodeSelector["kubernetes.io/hostname"] + + if executor, exists := p.executors[nodeName]; exists { + rec := p.podNames[nodeName] + return &NamedPodExecutor{ + PodExecutor: executor, + PodName: rec.name, + PodNamespace: rec.namespace, + }, nil + } + + h := fnv.New32a() + if _, err := h.Write([]byte(nodeName)); err != nil { + return nil, err + } + podSpec.Name = fmt.Sprintf("diki-pool-%08x-%s", h.Sum32(), p.Generator.Generate(10)) + + modifiedConstructor := func() *corev1.Pod { + return podSpec + } + + executor, err := p.podContext.Create(ctx, modifiedConstructor) + if err != nil { + return nil, err + } + + p.executors[nodeName] = executor + p.podNames[nodeName] = podRecord{name: podSpec.Name, namespace: podSpec.Namespace} + + return &NamedPodExecutor{ + PodExecutor: executor, + PodName: podSpec.Name, + PodNamespace: podSpec.Namespace, + }, nil +} + +// Delete is a no-op. Pod lifecycle is managed by the pool. +func (p *PodWorkerPool) Delete(_ context.Context, _, _ string) error { + return nil +} + +// CleanupAll deletes all pods that were created by this pool. +func (p *PodWorkerPool) CleanupAll(ctx context.Context) error { + p.mu.Lock() + records := make([]podRecord, 0, len(p.podNames)) + for _, rec := range p.podNames { + records = append(records, rec) + } + p.mu.Unlock() + + var ( + errCh = make(chan error, len(records)) + wg sync.WaitGroup + ) + + for _, rec := range records { + wg.Add(1) + go func(rec podRecord) { + defer wg.Done() + if err := p.podContext.Delete(ctx, rec.name, rec.namespace); err != nil { + errCh <- err + } + }(rec) + } + wg.Wait() + close(errCh) + + var errs []error + for err := range errCh { + errs = append(errs, err) + } + return errors.Join(errs...) +} + +// adjustAllocatablePods returns a copy of nodesAllocatablePods where nodes that already +// have a pooled executor are given a high allocatable count. +// Caller must hold p.mu. +func (p *PodWorkerPool) adjustAllocatablePods(nodesAllocatablePods map[string]int) map[string]int { + adjusted := maps.Clone(nodesAllocatablePods) + for nodeName := range p.executors { + if _, exists := adjusted[nodeName]; exists { + adjusted[nodeName] = 1 << 20 // large enough to always win the selection + } + } + return adjusted +} diff --git a/pkg/kubernetes/pod/workerpool_test.go b/pkg/kubernetes/pod/workerpool_test.go new file mode 100644 index 000000000..3c10c27f9 --- /dev/null +++ b/pkg/kubernetes/pod/workerpool_test.go @@ -0,0 +1,233 @@ +// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package pod_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + fakestrgen "github.com/gardener/diki/pkg/internal/stringgen/fake" + "github.com/gardener/diki/pkg/kubernetes/pod" + fakepod "github.com/gardener/diki/pkg/kubernetes/pod/fake" + kubeutils "github.com/gardener/diki/pkg/kubernetes/utils" + "github.com/gardener/diki/pkg/rule" +) + +func makeNode(name string, labels map[string]string) corev1.Node { + return corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + }, + Status: corev1.NodeStatus{ + Allocatable: corev1.ResourceList{ + "pods": resource.MustParse("100"), + }, + }, + } +} + +var _ = Describe("PodWorkerPool", func() { + var ( + pool *pod.PodWorkerPool + fakeCtx *fakepod.FakeSimplePodContext + ) + + BeforeEach(func() { + fakeCtx = fakepod.NewFakeSimplePodContext( + [][]string{{"result1"}, {"result2"}, {"result3"}}, + [][]error{{nil}, {nil}, {nil}}, + ) + nodeConstructorFn := func(nodeName string) func() *corev1.Pod { + return pod.NewPrivilegedPod("", "kube-system", "img", nodeName, nil) + } + pool = pod.NewPodWorkerPool(fakeCtx, kubeutils.SelectNodes, kubeutils.SelectPodOfReferenceGroup, nodeConstructorFn) + pool.Generator = &fakestrgen.FakeRandString{Rune: 'a'} + }) + + Describe("#Create", func() { + It("should create a pod and return a NamedPodExecutor", func() { + executor, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("pod1", "kube-system", "img", "node1", nil)) + + Expect(err).NotTo(HaveOccurred()) + named, ok := executor.(*pod.NamedPodExecutor) + Expect(ok).To(BeTrue()) + // Pool overrides the name with FNV hash of node name + random suffix. + Expect(named.PodName).To(Equal("diki-pool-0f4e2874-aaaaaaaaaa")) + Expect(named.PodNamespace).To(Equal("kube-system")) + }) + + It("should reuse the executor for the same node", func() { + exec1, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("pod1", "kube-system", "img", "node1", nil)) + Expect(err).NotTo(HaveOccurred()) + + exec2, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("pod2", "kube-system", "img", "node1", nil)) + Expect(err).NotTo(HaveOccurred()) + + named1 := exec1.(*pod.NamedPodExecutor) + named2 := exec2.(*pod.NamedPodExecutor) + Expect(named1.PodExecutor).To(BeIdenticalTo(named2.PodExecutor)) + Expect(named2.PodName).To(Equal("diki-pool-0f4e2874-aaaaaaaaaa")) + }) + + It("should create separate pods for different nodes", func() { + exec1, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("pod1", "kube-system", "img", "node1", nil)) + Expect(err).NotTo(HaveOccurred()) + + exec2, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("pod2", "kube-system", "img", "node2", nil)) + Expect(err).NotTo(HaveOccurred()) + + named1 := exec1.(*pod.NamedPodExecutor) + named2 := exec2.(*pod.NamedPodExecutor) + Expect(named1.PodExecutor).NotTo(BeIdenticalTo(named2.PodExecutor)) + }) + + It("should pool pods with empty node name", func() { + exec1, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("pod1", "kube-system", "img", "", nil)) + Expect(err).NotTo(HaveOccurred()) + + exec2, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("pod2", "kube-system", "img", "", nil)) + Expect(err).NotTo(HaveOccurred()) + + named1 := exec1.(*pod.NamedPodExecutor) + named2 := exec2.(*pod.NamedPodExecutor) + Expect(named1.PodExecutor).To(BeIdenticalTo(named2.PodExecutor)) + }) + }) + + Describe("#Delete", func() { + It("should be a no-op", func() { + err := pool.Delete(context.TODO(), "pod1", "kube-system") + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Describe("#SelectNodes", func() { + It("should select nodes and eagerly create pods", func() { + node1 := makeNode("node1", map[string]string{}) + node2 := makeNode("node2", map[string]string{}) + allocatable := map[string]int{"node1": 100, "node2": 100} + + selected, checks := pool.SelectNodes(context.TODO(), []corev1.Node{node1, node2}, allocatable, nil) + + Expect(checks).To(BeEmpty()) + Expect(selected).To(ConsistOf(node1, node2)) + + // Pods should have been eagerly created — Create returns a cache hit. + exec, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("other-name", "kube-system", "img", "node1", nil)) + Expect(err).NotTo(HaveOccurred()) + named := exec.(*pod.NamedPodExecutor) + Expect(named.PodName).To(Equal("diki-pool-0f4e2874-aaaaaaaaaa")) + }) + + It("should select one node per label group", func() { + node1 := makeNode("node1", map[string]string{"pool": "a"}) + node2 := makeNode("node2", map[string]string{"pool": "a"}) + node3 := makeNode("node3", map[string]string{"pool": "b"}) + allocatable := map[string]int{"node1": 100, "node2": 100, "node3": 100} + + selected, checks := pool.SelectNodes(context.TODO(), []corev1.Node{node1, node2, node3}, allocatable, []string{"pool"}) + + Expect(checks).To(BeEmpty()) + Expect(selected).To(HaveLen(2)) + Expect(selected).To(ContainElement(node3)) + }) + + It("should prefer nodes that already have a pooled executor", func() { + // Pre-seed pool with executor on node2. + _, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("seed-pod", "kube-system", "img", "node2", nil)) + Expect(err).NotTo(HaveOccurred()) + + node1 := makeNode("node1", map[string]string{}) + node2 := makeNode("node2", map[string]string{}) + // node1 has 0 allocatable (full), node2 has 0 allocatable (full). + // Without pool boost, neither is selectable. + // With pool boost, node2 gets 1<<20 and becomes selectable. + allocatable := map[string]int{"node1": 0, "node2": 0} + + selected, _ := pool.SelectNodes(context.TODO(), []corev1.Node{node1, node2}, allocatable, nil) + + Expect(selected).To(ConsistOf(node2)) + }) + + It("should emit a warning for nodes missing the label", func() { + node1 := makeNode("node1", map[string]string{"pool": "a"}) + node2 := makeNode("node2", map[string]string{}) + allocatable := map[string]int{"node1": 100, "node2": 100} + + selected, checks := pool.SelectNodes(context.TODO(), []corev1.Node{node1, node2}, allocatable, []string{"pool"}) + + Expect(selected).To(ConsistOf(node1)) + Expect(checks).To(ConsistOf( + rule.WarningCheckResult("Node is missing a label", rule.NewTarget("kind", "Node", "name", "node2", "label", "pool")), + )) + }) + }) + + Describe("#SelectPodOfReferenceGroup", func() { + It("should select pods and eagerly create exec pods on selected nodes", func() { + targetPod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "target-pod", + Namespace: "default", + }, + Spec: corev1.PodSpec{ + NodeName: "node1", + }, + } + allocatable := map[string]int{"node1": 100} + + grouped, checks := pool.SelectPodOfReferenceGroup(context.TODO(), []corev1.Pod{targetPod}, nil, allocatable, rule.NewTarget()) + + Expect(checks).To(BeEmpty()) + Expect(grouped).To(HaveKey("node1")) + + // Exec pod should have been eagerly created. + exec, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("other", "kube-system", "img", "node1", nil)) + Expect(err).NotTo(HaveOccurred()) + named := exec.(*pod.NamedPodExecutor) + Expect(named.PodName).To(Equal("diki-pool-0f4e2874-aaaaaaaaaa")) + }) + }) + + Describe("#CleanupAll", func() { + It("should delete all pooled pods via the underlying PodContext", func() { + _, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("pod1", "kube-system", "img", "node1", nil)) + Expect(err).NotTo(HaveOccurred()) + _, err = pool.Create(context.TODO(), pod.NewPrivilegedPod("pod2", "kube-system", "img", "node2", nil)) + Expect(err).NotTo(HaveOccurred()) + + err = pool.CleanupAll(context.TODO()) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should work with an empty pool", func() { + err := pool.CleanupAll(context.TODO()) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Describe("PodContext interface", func() { + It("should satisfy the PodContext interface", func() { + var _ pod.PodContext = pool + }) + }) + + Describe("NamedPodExecutor", func() { + It("should delegate Execute to the wrapped PodExecutor", func() { + executor, err := pool.Create(context.TODO(), pod.NewPrivilegedPod("pod1", "kube-system", "img", "node1", nil)) + Expect(err).NotTo(HaveOccurred()) + + result, err := executor.Execute(context.TODO(), "cmd", "shell") + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(Equal("result1")) + }) + }) +}) From 7af0e661d2918c2d079e2c58741b4eb40e7d87a0 Mon Sep 17 00:00:00 2001 From: Aleksandar Savchev Date: Fri, 27 Mar 2026 15:32:55 +0200 Subject: [PATCH 2/2] Enable for managed k8s DISA STIG v2r4 --- .../ruleset/disak8sstig/rules/242400.go | 22 ++++++-- .../ruleset/disak8sstig/rules/242451.go | 40 ++++++++++++--- .../ruleset/disak8sstig/rules/242466.go | 40 ++++++++++++--- .../ruleset/disak8sstig/rules/242467.go | 40 ++++++++++++--- .../managedk8s/ruleset/disak8sstig/ruleset.go | 12 ++++- .../ruleset/disak8sstig/v2r4_ruleset.go | 51 +++++++++++++------ .../ruleset/disak8sstig/rules/242393.go | 38 +++++++++----- .../ruleset/disak8sstig/rules/242394.go | 34 +++++++++---- .../ruleset/disak8sstig/rules/242396.go | 35 +++++++++---- .../ruleset/disak8sstig/rules/242404.go | 39 +++++++++----- .../ruleset/disak8sstig/rules/242406.go | 39 +++++++++----- .../ruleset/disak8sstig/rules/242407.go | 39 +++++++++----- .../ruleset/disak8sstig/rules/242447.go | 25 +++++++-- .../ruleset/disak8sstig/rules/242448.go | 25 +++++++-- .../ruleset/disak8sstig/rules/242449.go | 35 ++++++++----- .../ruleset/disak8sstig/rules/242450.go | 35 ++++++++----- .../ruleset/disak8sstig/rules/242452.go | 31 +++++++---- .../ruleset/disak8sstig/rules/242453.go | 31 +++++++---- .../ruleset/disak8sstig/rules/242453_test.go | 44 ++++++++++++++++ 19 files changed, 485 insertions(+), 170 deletions(-) diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242400.go b/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242400.go index a544c0303..431c8eee4 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242400.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242400.go @@ -157,8 +157,16 @@ func (r *Rule242400) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, checkResults...), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) - groupedPods, checks := kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + var ( + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) + groupedPods map[string][]corev1.Pod + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + groupedPods, checks = workerPool.SelectPodOfReferenceGroup(ctx, pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + } else { + groupedPods, checks = kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + } checkResults = append(checkResults, checks...) for nodeName, pods := range groupedPods { checkResults = append(checkResults, r.checkKubeProxy(ctx, pods, replicaSets, nodeName, image.String())...) @@ -179,7 +187,6 @@ func (r *Rule242400) checkKubeProxy( checkResults []rule.CheckResult additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} podName = fmt.Sprintf("diki-%s-%s", r.ID(), sharedrules.Generator.Generate(10)) - execPodTarget = rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") kubeProxyContainerNames = []string{"kube-proxy", "proxy"} ) @@ -194,12 +201,19 @@ func (r *Rule242400) checkKubeProxy( podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", imageName, nodeName, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + execPod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: podName, + Name: actualPodName, Namespace: "kube-system", }, } diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242451.go b/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242451.go index e87f8f91c..db5d27fd3 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242451.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242451.go @@ -91,7 +91,7 @@ func (r *Rule242451) Run(ctx context.Context) (rule.RuleResult, error) { if r.Options.FileOwnerOptions != nil { options = *r.Options.FileOwnerOptions } - if r.Options.NodeGroupByLabels != nil { + if len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } if r.Options.KubeProxy.ClusterObjectSelector != nil { @@ -119,8 +119,6 @@ func (r *Rule242451) Run(ctx context.Context) (rule.RuleResult, error) { if err != nil { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) @@ -128,7 +126,16 @@ func (r *Rule242451) Run(ctx context.Context) (rule.RuleResult, error) { image.WithOptionalTag(version.Get().GitVersion) // kubelet check - selectedShootNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + var ( + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) + selectedShootNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedShootNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedShootNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } checkResults = append(checkResults, checks...) if len(selectedShootNodes) == 0 { @@ -160,7 +167,12 @@ func (r *Rule242451) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, checkResults...), nil } - groupedShootPods, checks := kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + var groupedShootPods map[string][]corev1.Pod + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + groupedShootPods, checks = workerPool.SelectPodOfReferenceGroup(ctx, pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + } else { + groupedShootPods, checks = kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + } checkResults = append(checkResults, checks...) for nodeName, pods := range groupedShootPods { @@ -181,7 +193,6 @@ func (r *Rule242451) checkPods( var ( checkResults []rule.CheckResult podName = fmt.Sprintf("diki-%s-%s", r.ID(), sharedrules.Generator.Generate(10)) - execPodTarget = rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} ) @@ -196,12 +207,19 @@ func (r *Rule242451) checkPods( podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", imageName, nodeName, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + execPod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: podName, + Name: actualPodName, Namespace: "kube-system", }, } @@ -283,7 +301,6 @@ func (r *Rule242451) checkKubelet( pkiDirs = map[string]struct{}{} podName = fmt.Sprintf("diki-%s-%s", r.ID(), sharedrules.Generator.Generate(10)) nodeTarget = rule.NewTarget("name", nodeName, "kind", "Node") - execPodTarget = rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} ) @@ -297,9 +314,16 @@ func (r *Rule242451) checkKubelet( }() podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", imageName, nodeName, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + rawKubeletCommand, err := kubeutils.GetKubeletCommand(ctx, podExecutor) if err != nil { return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242466.go b/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242466.go index 3e9469247..9c41298d8 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242466.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242466.go @@ -83,7 +83,7 @@ func (r *Rule242466) Run(ctx context.Context) (rule.RuleResult, error) { ) if r.Options != nil { - if r.Options.NodeGroupByLabels != nil { + if len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } if r.Options.KubeProxy.ClusterObjectSelector != nil { @@ -105,8 +105,6 @@ func (r *Rule242466) Run(ctx context.Context) (rule.RuleResult, error) { if err != nil { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) @@ -114,7 +112,16 @@ func (r *Rule242466) Run(ctx context.Context) (rule.RuleResult, error) { image.WithOptionalTag(version.Get().GitVersion) // kubelet check - selectedShootNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + var ( + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) + selectedShootNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedShootNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedShootNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } checkResults = append(checkResults, checks...) if len(selectedShootNodes) == 0 { @@ -146,7 +153,12 @@ func (r *Rule242466) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, checkResults...), nil } - groupedShootPods, checks := kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + var groupedShootPods map[string][]corev1.Pod + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + groupedShootPods, checks = workerPool.SelectPodOfReferenceGroup(ctx, pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + } else { + groupedShootPods, checks = kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + } checkResults = append(checkResults, checks...) for nodeName, pods := range groupedShootPods { @@ -167,7 +179,6 @@ func (r *Rule242466) checkPods( var ( checkResults []rule.CheckResult podName = fmt.Sprintf("diki-%s-%s", r.ID(), sharedrules.Generator.Generate(10)) - execPodTarget = rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} ) @@ -182,12 +193,19 @@ func (r *Rule242466) checkPods( podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", imageName, nodeName, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + execPod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: podName, + Name: actualPodName, Namespace: "kube-system", }, } @@ -249,7 +267,6 @@ func (r *Rule242466) checkKubelet( selectedFileStats []intutils.FileStats podName = fmt.Sprintf("diki-%s-%s", r.ID(), sharedrules.Generator.Generate(10)) nodeTarget = rule.NewTarget("name", nodeName, "kind", "Node") - execPodTarget = rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} ) @@ -263,9 +280,16 @@ func (r *Rule242466) checkKubelet( }() podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", imageName, nodeName, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + rawKubeletCommand, err := kubeutils.GetKubeletCommand(ctx, podExecutor) if err != nil { return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242467.go b/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242467.go index 98ce90aed..7474595ae 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242467.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/rules/242467.go @@ -83,7 +83,7 @@ func (r *Rule242467) Run(ctx context.Context) (rule.RuleResult, error) { ) if r.Options != nil { - if r.Options.NodeGroupByLabels != nil { + if len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } if r.Options.KubeProxy.ClusterObjectSelector != nil { @@ -105,8 +105,6 @@ func (r *Rule242467) Run(ctx context.Context) (rule.RuleResult, error) { if err != nil { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) @@ -114,7 +112,16 @@ func (r *Rule242467) Run(ctx context.Context) (rule.RuleResult, error) { image.WithOptionalTag(version.Get().GitVersion) // kubelet check - selectedShootNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + var ( + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) + selectedShootNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedShootNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedShootNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } checkResults = append(checkResults, checks...) if len(selectedShootNodes) == 0 { @@ -146,7 +153,12 @@ func (r *Rule242467) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, checkResults...), nil } - groupedShootPods, checks := kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + var groupedShootPods map[string][]corev1.Pod + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + groupedShootPods, checks = workerPool.SelectPodOfReferenceGroup(ctx, pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + } else { + groupedShootPods, checks = kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, rule.NewTarget()) + } checkResults = append(checkResults, checks...) for nodeName, pods := range groupedShootPods { @@ -167,7 +179,6 @@ func (r *Rule242467) checkPods( var ( checkResults []rule.CheckResult podName = fmt.Sprintf("diki-%s-%s", r.ID(), sharedrules.Generator.Generate(10)) - execPodTarget = rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} ) @@ -182,12 +193,19 @@ func (r *Rule242467) checkPods( podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", imageName, nodeName, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + execPod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: podName, + Name: actualPodName, Namespace: "kube-system", }, } @@ -249,7 +267,6 @@ func (r *Rule242467) checkKubelet( selectedFileStats []intutils.FileStats podName = fmt.Sprintf("diki-%s-%s", r.ID(), sharedrules.Generator.Generate(10)) nodeTarget = rule.NewTarget("name", nodeName, "kind", "Node") - execPodTarget = rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} ) @@ -263,9 +280,16 @@ func (r *Rule242467) checkKubelet( }() podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", imageName, nodeName, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + rawKubeletCommand, err := kubeutils.GetKubeletCommand(ctx, podExecutor) if err != nil { return []rule.CheckResult{rule.ErroredCheckResult(err.Error(), execPodTarget)} diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go b/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go index 6d8d71c2d..4c56d41f2 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go @@ -17,6 +17,7 @@ import ( "github.com/gardener/diki/pkg/config" internalconfig "github.com/gardener/diki/pkg/internal/config" + "github.com/gardener/diki/pkg/kubernetes/pod" "github.com/gardener/diki/pkg/rule" "github.com/gardener/diki/pkg/ruleset" sharedruleset "github.com/gardener/diki/pkg/shared/ruleset" @@ -46,6 +47,7 @@ type Ruleset struct { args Args instanceID string logger *slog.Logger + podWorkerPool *pod.PodWorkerPool } // Args are Ruleset specific arguments. @@ -156,7 +158,15 @@ func (r *Ruleset) RunRule(ctx context.Context, id string) (rule.RuleResult, erro // Run executes all known Rules of the Ruleset. func (r *Ruleset) Run(ctx context.Context) (ruleset.RulesetResult, error) { - return sharedruleset.Run(ctx, r, r.rules, r.numWorkers, r.Logger()) + if r.podWorkerPool != nil { + defer func() { + if cleanupErr := r.podWorkerPool.CleanupAll(ctx); cleanupErr != nil { + r.Logger().Error("pod worker pool cleanup failed", "error", cleanupErr) + } + }() + } + result, err := sharedruleset.Run(ctx, r, r.rules, r.numWorkers, r.Logger()) + return result, err } // AddRules adds Rules to the Ruleset. diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/v2r4_ruleset.go b/pkg/provider/managedk8s/ruleset/disak8sstig/v2r4_ruleset.go index 05d325a07..bcd062085 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/v2r4_ruleset.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/v2r4_ruleset.go @@ -14,16 +14,21 @@ import ( "os" "path/filepath" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/client-go/kubernetes" + "k8s.io/component-base/version" "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/gardener/diki/imagevector" "github.com/gardener/diki/pkg/config" internalconfig "github.com/gardener/diki/pkg/internal/config" "github.com/gardener/diki/pkg/kubernetes/pod" + kubeutils "github.com/gardener/diki/pkg/kubernetes/utils" "github.com/gardener/diki/pkg/provider/managedk8s/ruleset/disak8sstig/rules" "github.com/gardener/diki/pkg/rule" "github.com/gardener/diki/pkg/rule/retry" + "github.com/gardener/diki/pkg/shared/images" "github.com/gardener/diki/pkg/shared/kubernetes/option" disaoption "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/option" "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/retryerrors" @@ -88,6 +93,20 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon return err } + image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) + if err != nil { + return fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) + } + image.WithOptionalTag(version.Get().GitVersion) + + nodeConstructorFn := func(nodeName string) func() *corev1.Pod { + additionalLabels := map[string]string{pod.LabelInstanceID: r.instanceID} + return pod.NewPrivilegedPod("", "kube-system", image.String(), nodeName, additionalLabels) + } + + pool := pod.NewPodWorkerPool(podContext, kubeutils.SelectNodes, kubeutils.SelectPodOfReferenceGroup, nodeConstructorFn) + r.podWorkerPool = pool + clientSet, err := kubernetes.NewForConfig(r.Config) if err != nil { return err @@ -332,7 +351,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242393), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242393, }), retry.WithRetryCondition(rcOpsPod), @@ -344,7 +363,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242394), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242394, }), retry.WithRetryCondition(rcOpsPod), @@ -357,7 +376,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242396), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242396, }), retry.WithRetryCondition(rcOpsPod), @@ -389,7 +408,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242400), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, V1RESTClient: clientSet.CoreV1().RESTClient(), Options: opts242400, }), @@ -417,7 +436,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242404), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242404, }), retry.WithRetryCondition(rcOpsPod), @@ -436,7 +455,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242406), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242406, }), retry.WithRetryCondition(rcFileChecks), @@ -448,7 +467,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242407), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242407, }), retry.WithRetryCondition(rcFileChecks), @@ -675,7 +694,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242447), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242447, }), retry.WithRetryCondition(rcFileChecks), @@ -687,7 +706,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242448), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242448, }), retry.WithRetryCondition(rcFileChecks), @@ -699,7 +718,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242449), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242449, }), retry.WithRetryCondition(rcFileChecks), @@ -711,7 +730,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242450), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242450, }), retry.WithRetryCondition(rcFileChecks), @@ -723,7 +742,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242451), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242451, }), retry.WithRetryCondition(rcFileChecks), @@ -735,7 +754,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242452), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242452, }), retry.WithRetryCondition(rcFileChecks), @@ -747,7 +766,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242453), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242453, }), retry.WithRetryCondition(rcFileChecks), @@ -836,7 +855,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242466), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242466, }), retry.WithRetryCondition(rcFileChecks), @@ -848,7 +867,7 @@ func (r *Ruleset) registerV2R4Rules(ruleOptions map[string]config.RuleOptionsCon Logger: r.Logger().With("rule_id", sharedrules.ID242467), InstanceID: r.instanceID, Client: client, - PodContext: podContext, + PodContext: pool, Options: opts242467, }), retry.WithRetryCondition(rcFileChecks), diff --git a/pkg/shared/ruleset/disak8sstig/rules/242393.go b/pkg/shared/ruleset/disak8sstig/rules/242393.go index 331365d6d..1aac61c84 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242393.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242393.go @@ -70,7 +70,7 @@ func (r *Rule242393) Run(ctx context.Context) (rule.RuleResult, error) { nodeLabels []string ) - if r.Options != nil && r.Options.NodeGroupByLabels != nil { + if r.Options != nil && len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } @@ -83,20 +83,29 @@ func (r *Rule242393) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + slices.SortFunc(selectedNodes, func(n1, n2 corev1.Node) int { return cmp.Compare(n1.Name, n2.Name) }) @@ -104,7 +113,6 @@ func (r *Rule242393) Run(ctx context.Context) (rule.RuleResult, error) { for _, node := range selectedNodes { podName := fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) nodeTarget := kubeutils.TargetWithK8sObject(rule.NewTarget(), metav1.TypeMeta{Kind: "Node"}, node.ObjectMeta) - execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") defer func() { timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() @@ -113,15 +121,19 @@ func (r *Rule242393) Run(ctx context.Context) (rule.RuleResult, error) { r.Logger.Error(err.Error()) } }() - additionalLabels := map[string]string{ - pod.LabelInstanceID: r.InstanceID, - } podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", image.String(), node.Name, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) continue } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + commandResult, err := podExecutor.Execute(ctx, "/bin/sh", `ss -tulpn | grep "LISTEN" | grep -E ":22(\s|$)" || true`) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) diff --git a/pkg/shared/ruleset/disak8sstig/rules/242394.go b/pkg/shared/ruleset/disak8sstig/rules/242394.go index 70efc5e57..707452e0f 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242394.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242394.go @@ -70,7 +70,7 @@ func (r *Rule242394) Run(ctx context.Context) (rule.RuleResult, error) { nodeLabels []string ) - if r.Options != nil && r.Options.NodeGroupByLabels != nil { + if r.Options != nil && len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } @@ -83,20 +83,28 @@ func (r *Rule242394) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + slices.SortFunc(selectedNodes, func(n1, n2 corev1.Node) int { return cmp.Compare(n1.Name, n2.Name) }) @@ -104,7 +112,6 @@ func (r *Rule242394) Run(ctx context.Context) (rule.RuleResult, error) { for _, node := range selectedNodes { podName := fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) nodeTarget := kubeutils.TargetWithK8sObject(rule.NewTarget(), metav1.TypeMeta{Kind: "Node"}, node.ObjectMeta) - execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") defer func() { timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() @@ -118,10 +125,17 @@ func (r *Rule242394) Run(ctx context.Context) (rule.RuleResult, error) { } podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", image.String(), node.Name, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) continue } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + commandResult, err := podExecutor.Execute(ctx, "/bin/sh", `ss -tulpn | grep "LISTEN" | grep -E ":22(\s|$)" || true`) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) diff --git a/pkg/shared/ruleset/disak8sstig/rules/242396.go b/pkg/shared/ruleset/disak8sstig/rules/242396.go index 2bb9b2426..d619e4027 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242396.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242396.go @@ -72,7 +72,7 @@ func (r *Rule242396) Run(ctx context.Context) (rule.RuleResult, error) { checkResults []rule.CheckResult ) - if r.Options != nil && r.Options.NodeGroupByLabels != nil { + if r.Options != nil && len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } @@ -85,20 +85,28 @@ func (r *Rule242396) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + constraintK8s, err := semver.NewConstraint("< 1.12.9") if err != nil { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget())), nil @@ -125,7 +133,6 @@ func (r *Rule242396) checkKubectl( kubectlVersion kubectlversion.Version podName = fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) nodeTarget = rule.NewTarget("kind", "Node", "name", nodeName) - execPodTarget = rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} ) @@ -140,8 +147,14 @@ func (r *Rule242396) checkKubectl( podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", imageName, nodeName, additionalLabels)) if err != nil { - return rule.ErroredCheckResult(err.Error(), execPodTarget) + return rule.ErroredCheckResult(err.Error(), rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod")) + } + + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") commandResult, err := podExecutor.Execute(ctx, "/bin/sh", `kubectl version --client --output=json`) if err != nil { diff --git a/pkg/shared/ruleset/disak8sstig/rules/242404.go b/pkg/shared/ruleset/disak8sstig/rules/242404.go index 4ce822af8..5825b148c 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242404.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242404.go @@ -68,7 +68,7 @@ func (r *Rule242404) Run(ctx context.Context) (rule.RuleResult, error) { nodeLabels []string ) - if r.Options != nil && r.Options.NodeGroupByLabels != nil { + if r.Options != nil && len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } @@ -82,20 +82,28 @@ func (r *Rule242404) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "PodList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + for _, node := range selectedNodes { checkResult := r.checkNode(ctx, node, image.String()) checkResults = append(checkResults, checkResult) @@ -106,9 +114,8 @@ func (r *Rule242404) Run(ctx context.Context) (rule.RuleResult, error) { func (r *Rule242404) checkNode(ctx context.Context, node corev1.Node, privPodImage string) rule.CheckResult { var ( - target = kubeutils.TargetWithK8sObject(rule.NewTarget(), metav1.TypeMeta{Kind: "Node"}, node.ObjectMeta) - podName = fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) - podTarget = rule.NewTarget("kind", "Pod", "namespace", "kube-system", "name", podName) + target = kubeutils.TargetWithK8sObject(rule.NewTarget(), metav1.TypeMeta{Kind: "Node"}, node.ObjectMeta) + podName = fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) ) defer func() { @@ -125,8 +132,14 @@ func (r *Rule242404) checkNode(ctx context.Context, node corev1.Node, privPodIma } podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", privPodImage, node.Name, additionalLabels)) if err != nil { - return rule.ErroredCheckResult(err.Error(), podTarget) + return rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "Pod", "namespace", "kube-system", "name", podName)) + } + + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName } + podTarget := rule.NewTarget("kind", "Pod", "namespace", "kube-system", "name", actualPodName) rawKubeletCommand, err := kubeutils.GetKubeletCommand(ctx, podExecutor) if err != nil { diff --git a/pkg/shared/ruleset/disak8sstig/rules/242406.go b/pkg/shared/ruleset/disak8sstig/rules/242406.go index 77b8b6fc6..2562a7f6e 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242406.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242406.go @@ -10,6 +10,7 @@ import ( "slices" "time" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/validation/field" @@ -79,7 +80,7 @@ func (r *Rule242406) Run(ctx context.Context) (rule.RuleResult, error) { if r.Options.FileOwnerOptions != nil { options = *r.Options.FileOwnerOptions } - if r.Options.NodeGroupByLabels != nil { + if len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } } @@ -99,23 +100,31 @@ func (r *Rule242406) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + for _, node := range selectedNodes { podName := fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) - execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") defer func() { timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() @@ -124,15 +133,19 @@ func (r *Rule242406) Run(ctx context.Context) (rule.RuleResult, error) { r.Logger.Error(err.Error()) } }() - additionalLabels := map[string]string{ - pod.LabelInstanceID: r.InstanceID, - } podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", image.String(), node.Name, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) continue } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + if kubeletServicePath, err = podExecutor.Execute(ctx, "/bin/sh", "systemctl show -P FragmentPath kubelet.service"); err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(fmt.Sprintf("could not find kubelet.service path: %s", err.Error()), execPodTarget)) continue diff --git a/pkg/shared/ruleset/disak8sstig/rules/242407.go b/pkg/shared/ruleset/disak8sstig/rules/242407.go index 9d7b9416c..4e84d3aac 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242407.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242407.go @@ -10,6 +10,7 @@ import ( "slices" "time" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/validation/field" @@ -70,7 +71,7 @@ func (r *Rule242407) Run(ctx context.Context) (rule.RuleResult, error) { expectedFilePermissionsMax = "644" ) - if r.Options != nil && r.Options.NodeGroupByLabels != nil { + if r.Options != nil && len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } @@ -83,23 +84,31 @@ func (r *Rule242407) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + for _, node := range selectedNodes { podName := fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) - execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") defer func() { timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() @@ -108,15 +117,19 @@ func (r *Rule242407) Run(ctx context.Context) (rule.RuleResult, error) { r.Logger.Error(err.Error()) } }() - additionalLabels := map[string]string{ - pod.LabelInstanceID: r.InstanceID, - } podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", image.String(), node.Name, additionalLabels)) if err != nil { + execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) continue } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget := rule.NewTarget("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + if kubeletServicePath, err = podExecutor.Execute(ctx, "/bin/sh", "systemctl show -P FragmentPath kubelet.service"); err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(fmt.Sprintf("could not find kubelet.service path: %s", err.Error()), execPodTarget)) continue diff --git a/pkg/shared/ruleset/disak8sstig/rules/242447.go b/pkg/shared/ruleset/disak8sstig/rules/242447.go index fd419c70c..54b85818d 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242447.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242447.go @@ -97,15 +97,25 @@ func (r *Rule242447) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "ReplicaSetList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) - groupedPods, checks := kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, target) - checkResults = append(checkResults, checks...) image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) + groupedPods map[string][]corev1.Pod + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + groupedPods, checks = workerPool.SelectPodOfReferenceGroup(ctx, pods, replicaSets, nodesAllocatablePods, target) + } else { + groupedPods, checks = kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, target) + } + checkResults = append(checkResults, checks...) + for nodeName, pods := range groupedPods { podName := fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) execPodTarget := target.With("name", podName, "namespace", "kube-system", "kind", "Pod") @@ -119,16 +129,21 @@ func (r *Rule242447) Run(ctx context.Context) (rule.RuleResult, error) { } }() - additionalLabels := map[string]string{pod.LabelInstanceID: r.InstanceID} podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", image.String(), nodeName, additionalLabels)) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) continue } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget = target.With("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + execPod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: podName, + Name: actualPodName, Namespace: "kube-system", }, } diff --git a/pkg/shared/ruleset/disak8sstig/rules/242448.go b/pkg/shared/ruleset/disak8sstig/rules/242448.go index c888b5915..8603abbd8 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242448.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242448.go @@ -130,15 +130,25 @@ func (r *Rule242448) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "ReplicaSetList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) - groupedPods, checks := kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, target) - checkResults = append(checkResults, checks...) image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(allPods, nodes) + groupedPods map[string][]corev1.Pod + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + groupedPods, checks = workerPool.SelectPodOfReferenceGroup(ctx, pods, replicaSets, nodesAllocatablePods, target) + } else { + groupedPods, checks = kubeutils.SelectPodOfReferenceGroup(pods, replicaSets, nodesAllocatablePods, target) + } + checkResults = append(checkResults, checks...) + for nodeName, pods := range groupedPods { podName := fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) execPodTarget := target.With("name", podName, "namespace", "kube-system", "kind", "Pod") @@ -152,16 +162,21 @@ func (r *Rule242448) Run(ctx context.Context) (rule.RuleResult, error) { } }() - additionalLabels := map[string]string{pod.LabelInstanceID: r.InstanceID} podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", image.String(), nodeName, additionalLabels)) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) continue } + actualPodName := podName + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + actualPodName = named.PodName + } + execPodTarget = target.With("name", actualPodName, "namespace", "kube-system", "kind", "Pod") + execPod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: podName, + Name: actualPodName, Namespace: "kube-system", }, } diff --git a/pkg/shared/ruleset/disak8sstig/rules/242449.go b/pkg/shared/ruleset/disak8sstig/rules/242449.go index 55a4f6353..a1df284f2 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242449.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242449.go @@ -11,6 +11,7 @@ import ( "strings" "time" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/validation/field" @@ -70,7 +71,7 @@ func (r *Rule242449) Run(ctx context.Context) (rule.RuleResult, error) { expectedFilePermissionsMax = "644" ) - if r.Options != nil && r.Options.NodeGroupByLabels != nil { + if r.Options != nil && len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } @@ -83,20 +84,29 @@ func (r *Rule242449) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + for _, node := range selectedNodes { podName := fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") @@ -108,15 +118,16 @@ func (r *Rule242449) Run(ctx context.Context) (rule.RuleResult, error) { r.Logger.Error(err.Error()) } }() - additionalLabels := map[string]string{ - pod.LabelInstanceID: r.InstanceID, - } podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", image.String(), node.Name, additionalLabels)) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) continue } + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + execPodTarget = rule.NewTarget("name", named.PodName, "namespace", "kube-system", "kind", "Pod") + } + rawKubeletCommand, err := kubeutils.GetKubeletCommand(ctx, podExecutor) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) diff --git a/pkg/shared/ruleset/disak8sstig/rules/242450.go b/pkg/shared/ruleset/disak8sstig/rules/242450.go index 0c814e85e..5f48f09e3 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242450.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242450.go @@ -11,6 +11,7 @@ import ( "strings" "time" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/validation/field" @@ -79,7 +80,7 @@ func (r *Rule242450) Run(ctx context.Context) (rule.RuleResult, error) { if r.Options.FileOwnerOptions != nil { options = *r.Options.FileOwnerOptions } - if r.Options.NodeGroupByLabels != nil { + if len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } } @@ -99,20 +100,29 @@ func (r *Rule242450) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + additionalLabels = map[string]string{pod.LabelInstanceID: r.InstanceID} + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + for _, node := range selectedNodes { podName := fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) execPodTarget := rule.NewTarget("name", podName, "namespace", "kube-system", "kind", "Pod") @@ -124,15 +134,16 @@ func (r *Rule242450) Run(ctx context.Context) (rule.RuleResult, error) { r.Logger.Error(err.Error()) } }() - additionalLabels := map[string]string{ - pod.LabelInstanceID: r.InstanceID, - } podExecutor, err := r.PodContext.Create(ctx, pod.NewPrivilegedPod(podName, "kube-system", image.String(), node.Name, additionalLabels)) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) continue } + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + execPodTarget = rule.NewTarget("name", named.PodName, "namespace", "kube-system", "kind", "Pod") + } + rawKubeletCommand, err := kubeutils.GetKubeletCommand(ctx, podExecutor) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) diff --git a/pkg/shared/ruleset/disak8sstig/rules/242452.go b/pkg/shared/ruleset/disak8sstig/rules/242452.go index 33323c77a..02b7c5c69 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242452.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242452.go @@ -11,6 +11,7 @@ import ( "strings" "time" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/validation/field" @@ -70,7 +71,7 @@ func (r *Rule242452) Run(ctx context.Context) (rule.RuleResult, error) { expectedFilePermissionsMax = "644" ) - if r.Options != nil && r.Options.NodeGroupByLabels != nil { + if r.Options != nil && len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } @@ -83,20 +84,28 @@ func (r *Rule242452) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + for _, node := range selectedNodes { var ( podName = fmt.Sprintf("diki-%s-%s", r.ID(), Generator.Generate(10)) @@ -121,6 +130,10 @@ func (r *Rule242452) Run(ctx context.Context) (rule.RuleResult, error) { continue } + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + execPodTarget = rule.NewTarget("name", named.PodName, "namespace", "kube-system", "kind", "Pod") + } + rawKubeletCommand, err := kubeutils.GetKubeletCommand(ctx, podExecutor) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) diff --git a/pkg/shared/ruleset/disak8sstig/rules/242453.go b/pkg/shared/ruleset/disak8sstig/rules/242453.go index 97e862d5b..57438b96f 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242453.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242453.go @@ -11,6 +11,7 @@ import ( "strings" "time" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/validation/field" @@ -79,7 +80,7 @@ func (r *Rule242453) Run(ctx context.Context) (rule.RuleResult, error) { if r.Options.FileOwnerOptions != nil { options = *r.Options.FileOwnerOptions } - if r.Options.NodeGroupByLabels != nil { + if len(r.Options.NodeGroupByLabels) > 0 { nodeLabels = slices.Clone(r.Options.NodeGroupByLabels) } } @@ -99,20 +100,28 @@ func (r *Rule242453) Run(ctx context.Context) (rule.RuleResult, error) { return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "NodeList"))), nil } - nodesAllocatablePods := kubeutils.GetNodesAllocatablePodsNum(pods, nodes) - selectedNodes, checks := kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) - checkResults = append(checkResults, checks...) - - if len(selectedNodes) == 0 { - return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil - } - image, err := imagevector.ImageVector().FindImage(images.DikiOpsImageName) if err != nil { return rule.RuleResult{}, fmt.Errorf("failed to find image version for %s: %w", images.DikiOpsImageName, err) } image.WithOptionalTag(version.Get().GitVersion) + var ( + nodesAllocatablePods = kubeutils.GetNodesAllocatablePodsNum(pods, nodes) + selectedNodes []corev1.Node + checks []rule.CheckResult + ) + if workerPool, ok := r.PodContext.(*pod.PodWorkerPool); ok { + selectedNodes, checks = workerPool.SelectNodes(ctx, nodes, nodesAllocatablePods, nodeLabels) + } else { + selectedNodes, checks = kubeutils.SelectNodes(nodes, nodesAllocatablePods, nodeLabels) + } + checkResults = append(checkResults, checks...) + + if len(selectedNodes) == 0 { + return rule.Result(r, rule.ErroredCheckResult("no allocatable nodes could be selected", rule.NewTarget())), nil + } + for _, node := range selectedNodes { var ( selectedFilePaths []string @@ -137,6 +146,10 @@ func (r *Rule242453) Run(ctx context.Context) (rule.RuleResult, error) { continue } + if named, ok := podExecutor.(*pod.NamedPodExecutor); ok { + execPodTarget = rule.NewTarget("name", named.PodName, "namespace", "kube-system", "kind", "Pod") + } + rawKubeletCommand, err := kubeutils.GetKubeletCommand(ctx, podExecutor) if err != nil { checkResults = append(checkResults, rule.ErroredCheckResult(err.Error(), execPodTarget)) diff --git a/pkg/shared/ruleset/disak8sstig/rules/242453_test.go b/pkg/shared/ruleset/disak8sstig/rules/242453_test.go index c979e655a..85f01d533 100644 --- a/pkg/shared/ruleset/disak8sstig/rules/242453_test.go +++ b/pkg/shared/ruleset/disak8sstig/rules/242453_test.go @@ -19,6 +19,7 @@ import ( fakestrgen "github.com/gardener/diki/pkg/internal/stringgen/fake" "github.com/gardener/diki/pkg/kubernetes/pod" fakepod "github.com/gardener/diki/pkg/kubernetes/pod/fake" + kubeutils "github.com/gardener/diki/pkg/kubernetes/utils" "github.com/gardener/diki/pkg/rule" "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/option" "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/rules" @@ -162,4 +163,47 @@ var _ = Describe("#242453", func() { rule.PassedCheckResult("File has expected owners", rule.NewTarget("kind", "Node", "name", "node4", "details", "fileName: /var/lib/kubelet/config/kubelet, ownerUser: 0, ownerGroup: 0")), }), ) + + It("should use PodWorkerPool and reuse pooled pods", func() { + // When using a PodWorkerPool, AdjustedAllocatablePods boosts nodes that already + // have a pool executor. This test verifies the rule works correctly with a pool. + // node1 and node2 both have foo=foo; SelectNodes picks node1 (first allocatable). + // node3 has foo=bar. node4 has no "foo" label → warning. + fakeCtx := fakepod.NewFakeSimplePodContext( + [][]string{ + {kubeletPID, rawKubeletCommand, compliantKubeconfigStats, compliantConfigStats}, // node1 + {kubeletPID, rawKubeletCommand, compliantKubeconfigStats, compliantConfigStats}, // node3 + }, + [][]error{ + {nil, nil, nil, nil}, + {nil, nil, nil, nil}, + }, + ) + workerPool := pod.NewPodWorkerPool(fakeCtx, kubeutils.SelectNodes, kubeutils.SelectPodOfReferenceGroup, func(nodeName string) func() *corev1.Pod { + return pod.NewPrivilegedPod("", "kube-system", "img", nodeName, nil) + }) + + r := &rules.Rule242453{ + Logger: testLogger, + InstanceID: instanceID, + Client: fakeClient, + PodContext: workerPool, + Options: &rules.Options242453{ + NodeGroupByLabels: []string{"foo"}, + }, + } + + ruleResult, err := r.Run(ctx) + Expect(err).To(BeNil()) + + // node1 is selected for foo=foo, node3 for foo=bar. + // node4 missing the label triggers a warning. No check for node2. + Expect(ruleResult.CheckResults).To(ConsistOf( + rule.PassedCheckResult("File has expected owners", rule.NewTarget("kind", "Node", "name", "node1", "details", "fileName: /var/lib/kubelet/kubeconfig, ownerUser: 0, ownerGroup: 0")), + rule.PassedCheckResult("File has expected owners", rule.NewTarget("kind", "Node", "name", "node1", "details", "fileName: /var/lib/kubelet/config/kubelet, ownerUser: 0, ownerGroup: 0")), + rule.PassedCheckResult("File has expected owners", rule.NewTarget("kind", "Node", "name", "node3", "details", "fileName: /var/lib/kubelet/kubeconfig, ownerUser: 0, ownerGroup: 0")), + rule.PassedCheckResult("File has expected owners", rule.NewTarget("kind", "Node", "name", "node3", "details", "fileName: /var/lib/kubelet/config/kubelet, ownerUser: 0, ownerGroup: 0")), + rule.WarningCheckResult("Node is missing a label", rule.NewTarget("kind", "Node", "name", "node4", "label", "foo")), + )) + }) })