Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Rule 2002 to the Security Hardened Shoot Ruleset #360

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,28 +49,38 @@ var _ = Describe("#2001", func() {
})

DescribeTable("Run cases",
func(updateFn func(), expectedResults []rule.CheckResult) {
func(updateFn func(), expectedResults rule.CheckResult) {
updateFn()

Expect(fakeClient.Create(ctx, shoot)).To(Succeed())
res, err := r.Run(ctx)
Expect(err).To(BeNil())
Expect(res).To(Equal(rule.RuleResult{RuleID: ruleID, RuleName: ruleName, CheckResults: expectedResults, Severity: rule.SeverityMedium}))
Expect(res).To(Equal(rule.RuleResult{RuleID: ruleID, RuleName: ruleName, CheckResults: []rule.CheckResult{expectedResults}, Severity: rule.SeverityMedium}))
},

Entry("should error when the shoot is not found",
func() { shoot.Name = "notFoo" }, []rule.CheckResult{{Status: rule.Errored, Message: "shoots.core.gardener.cloud \"foo\" not found", Target: rule.NewTarget("kind", "Shoot", "name", "foo", "namespace", "bar")}}),
func() { shoot.Name = "notFoo" },
rule.CheckResult{Status: rule.Errored, Message: "shoots.core.gardener.cloud \"foo\" not found", Target: rule.NewTarget("kind", "Shoot", "name", "foo", "namespace", "bar")},
),
Entry("should fail when the WorkersSettings field is not specified",
func() { shoot.Spec.Provider = gardencorev1beta1.Provider{} }, []rule.CheckResult{{Status: rule.Failed, Message: "SSH access is not disabled for worker nodes.", Target: rule.NewTarget()}}),
func() { shoot.Spec.Provider = gardencorev1beta1.Provider{} },
rule.CheckResult{Status: rule.Failed, Message: "SSH access is not disabled for worker nodes.", Target: rule.NewTarget()},
),
Entry("should fail when the SSHAccess field is not specified",
func() { shoot.Spec.Provider.WorkersSettings = &gardencorev1beta1.WorkersSettings{} }, []rule.CheckResult{{Status: rule.Failed, Message: "SSH access is not disabled for worker nodes.", Target: rule.NewTarget()}}),
func() { shoot.Spec.Provider.WorkersSettings = &gardencorev1beta1.WorkersSettings{} },
rule.CheckResult{Status: rule.Failed, Message: "SSH access is not disabled for worker nodes.", Target: rule.NewTarget()},
),
Entry("should fail when the SSH access is enabled",
func() {
shoot.Spec.Provider.WorkersSettings = ptr.To(gardencorev1beta1.WorkersSettings{SSHAccess: ptr.To(gardencorev1beta1.SSHAccess{Enabled: true})})
}, []rule.CheckResult{{Status: rule.Failed, Message: "SSH access is enabled for worker nodes.", Target: rule.NewTarget()}}),
},
rule.CheckResult{Status: rule.Failed, Message: "SSH access is enabled for worker nodes.", Target: rule.NewTarget()},
),
Entry("should pass when the SSH access is disabled",
func() {
shoot.Spec.Provider.WorkersSettings = ptr.To(gardencorev1beta1.WorkersSettings{SSHAccess: ptr.To(gardencorev1beta1.SSHAccess{Enabled: false})})
}, []rule.CheckResult{{Status: rule.Passed, Message: "SSH access is disabled for worker nodes.", Target: rule.NewTarget()}}),
},
rule.CheckResult{Status: rule.Passed, Message: "SSH access is disabled for worker nodes.", Target: rule.NewTarget()},
),
)
})
107 changes: 107 additions & 0 deletions pkg/provider/garden/ruleset/securityhardenedshoot/rules/2002.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors
//
// SPDX-License-Identifier: Apache-2.0

package rules

import (
"context"
"fmt"

gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/gardener/diki/pkg/rule"
)

var (
_ rule.Rule = &Rule2002{}
_ rule.Severity = &Rule2002{}
)

type Rule2002 struct {
Client client.Client
ShootName string
ShootNamespace string
}

func (r *Rule2002) ID() string {
return "2002"
}

func (r *Rule2002) Name() string {
return "Shoot clusters must not have Alpha APIs enabled for any Kubernetes component."
}

func (r *Rule2002) Severity() rule.SeverityLevel {
return rule.SeverityMedium
}

func (r *Rule2002) Run(ctx context.Context) (rule.RuleResult, error) {
shoot := &gardencorev1beta1.Shoot{ObjectMeta: metav1.ObjectMeta{Name: r.ShootName, Namespace: r.ShootNamespace}}
if err := r.Client.Get(ctx, client.ObjectKeyFromObject(shoot), shoot); err != nil {
return rule.Result(r, rule.ErroredCheckResult(err.Error(), rule.NewTarget("kind", "Shoot", "name", r.ShootName, "namespace", r.ShootNamespace))), nil
}

var (
kubernetesSpec = shoot.Spec.Kubernetes
allAlpha = "AllAlpha"
components = []string{"kube apiserver", "kube controller manager", "kube scheduler", "kube proxy", "kubelet"}
)

allAlphaFeatureGatesValues := map[string]*bool{}

allAlphaFeatureGatesValues["kube apiserver"] = func() *bool {
if kubernetesSpec.KubeAPIServer != nil && kubernetesSpec.KubeAPIServer.FeatureGates != nil {
return ptr.To(kubernetesSpec.KubeAPIServer.FeatureGates[allAlpha])
}
return nil
}()

allAlphaFeatureGatesValues["kube controller manager"] = func() *bool {
if kubernetesSpec.KubeControllerManager != nil && kubernetesSpec.KubeControllerManager.FeatureGates != nil {
return ptr.To(kubernetesSpec.KubeControllerManager.FeatureGates[allAlpha])
}
return nil
}()

allAlphaFeatureGatesValues["kube scheduler"] = func() *bool {
if kubernetesSpec.KubeScheduler != nil && kubernetesSpec.KubeScheduler.FeatureGates != nil {
return ptr.To(kubernetesSpec.KubeScheduler.FeatureGates[allAlpha])
}
return nil
}()

allAlphaFeatureGatesValues["kube proxy"] = func() *bool {
if kubernetesSpec.KubeProxy != nil && kubernetesSpec.KubeProxy.FeatureGates != nil {
return ptr.To(kubernetesSpec.KubeProxy.FeatureGates[allAlpha])
}
return nil
}()

allAlphaFeatureGatesValues["kubelet"] = func() *bool {
if kubernetesSpec.Kubelet != nil && kubernetesSpec.Kubelet.FeatureGates != nil {
return ptr.To(kubernetesSpec.Kubelet.FeatureGates[allAlpha])
}
return nil
}()

var checkResults []rule.CheckResult

for _, component := range components {

if allAlphaFeatureGatesValues[component] == nil {
checkResults = append(checkResults, rule.PassedCheckResult(fmt.Sprintf("AllAlpha featureGates are not enabled for the %s.", component), rule.NewTarget()))
} else {
if *(allAlphaFeatureGatesValues[component]) {
checkResults = append(checkResults, rule.FailedCheckResult(fmt.Sprintf("AllAlpha featureGates are enabled for the %s.", component), rule.NewTarget()))
} else {
checkResults = append(checkResults, rule.PassedCheckResult(fmt.Sprintf("AllAlpha featureGates are disabled for the %s.", component), rule.NewTarget()))
}
}
}

return rule.Result(r, checkResults...), nil
}
137 changes: 137 additions & 0 deletions pkg/provider/garden/ruleset/securityhardenedshoot/rules/2002_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors
//
// SPDX-License-Identifier: Apache-2.0

package rules_test

import (
"context"

gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/gardener/gardener/pkg/client/kubernetes"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"

"github.com/gardener/diki/pkg/provider/garden/ruleset/securityhardenedshoot/rules"
"github.com/gardener/diki/pkg/rule"
)

var _ = Describe("#2002", func() {
var (
fakeClient client.Client
ctx = context.TODO()
shootName = "foo"
shootNamespace = "bar"

shoot *gardencorev1beta1.Shoot

r rule.Rule
ruleName = "Shoot clusters must not have Alpha APIs enabled for any Kubernetes component."
ruleID = "2002"
severity = rule.SeverityMedium
)

BeforeEach(func() {
fakeClient = fakeclient.NewClientBuilder().WithScheme(kubernetes.GardenScheme).Build()
shoot = &gardencorev1beta1.Shoot{
ObjectMeta: metav1.ObjectMeta{
Name: shootName,
Namespace: shootNamespace,
},
}
r = &rules.Rule2002{
ShootName: shootName,
ShootNamespace: shootNamespace,
Client: fakeClient,
}
})

DescribeTable("Run cases", func(updateFn func(), expectedCheckResult []rule.CheckResult) {
updateFn()

Expect(fakeClient.Create(ctx, shoot)).To(Succeed())

res, err := r.Run(ctx)
Expect(err).To(BeNil())

expectedResult := rule.RuleResult{RuleID: ruleID, RuleName: ruleName, Severity: severity, CheckResults: expectedCheckResult}
Expect(res).To(Equal(expectedResult))
},
Entry("should error when the shoot is not found",
func() { shoot.Name = "notFoo" },
[]rule.CheckResult{
{Status: rule.Errored, Message: "shoots.core.gardener.cloud \"foo\" not found", Target: rule.NewTarget("kind", "Shoot", "name", "foo", "namespace", "bar")},
},
),
Entry("should pass when AllAlpha featureGates are set to default for all components",
func() {},
[]rule.CheckResult{
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube apiserver.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube controller manager.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube scheduler.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube proxy.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kubelet.", Target: rule.NewTarget()},
},
),
Entry("should fail when AllAlpha featureGates are enabled for all components",
func() {
shoot.Spec.Kubernetes.KubeAPIServer = ptr.To(gardencorev1beta1.KubeAPIServerConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": true}}})
shoot.Spec.Kubernetes.KubeControllerManager = ptr.To(gardencorev1beta1.KubeControllerManagerConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": true}}})
shoot.Spec.Kubernetes.KubeScheduler = ptr.To(gardencorev1beta1.KubeSchedulerConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": true}}})
shoot.Spec.Kubernetes.KubeProxy = ptr.To(gardencorev1beta1.KubeProxyConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": true}}})
shoot.Spec.Kubernetes.Kubelet = ptr.To(gardencorev1beta1.KubeletConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": true}}})
},
[]rule.CheckResult{
{Status: rule.Failed, Message: "AllAlpha featureGates are enabled for the kube apiserver.", Target: rule.NewTarget()},
{Status: rule.Failed, Message: "AllAlpha featureGates are enabled for the kube controller manager.", Target: rule.NewTarget()},
{Status: rule.Failed, Message: "AllAlpha featureGates are enabled for the kube scheduler.", Target: rule.NewTarget()},
{Status: rule.Failed, Message: "AllAlpha featureGates are enabled for the kube proxy.", Target: rule.NewTarget()},
{Status: rule.Failed, Message: "AllAlpha featureGates are enabled for the kubelet.", Target: rule.NewTarget()},
},
),
Entry("should pass when AllAlpha featureGates are disabled for all components",
func() {
shoot.Spec.Kubernetes.KubeAPIServer = ptr.To(gardencorev1beta1.KubeAPIServerConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": false}}})
shoot.Spec.Kubernetes.KubeControllerManager = ptr.To(gardencorev1beta1.KubeControllerManagerConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": false}}})
shoot.Spec.Kubernetes.KubeScheduler = ptr.To(gardencorev1beta1.KubeSchedulerConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": false}}})
shoot.Spec.Kubernetes.KubeProxy = ptr.To(gardencorev1beta1.KubeProxyConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": false}}})
shoot.Spec.Kubernetes.Kubelet = ptr.To(gardencorev1beta1.KubeletConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": false}}})
},
[]rule.CheckResult{
{Status: rule.Passed, Message: "AllAlpha featureGates are disabled for the kube apiserver.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are disabled for the kube controller manager.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are disabled for the kube scheduler.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are disabled for the kube proxy.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are disabled for the kubelet.", Target: rule.NewTarget()},
},
),
Entry("should pass when AllAlpha featureGates are disabled for the kube proxy component",
func() {
shoot.Spec.Kubernetes.KubeProxy = ptr.To(gardencorev1beta1.KubeProxyConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": false}}})
},
[]rule.CheckResult{
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube apiserver.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube controller manager.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube scheduler.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are disabled for the kube proxy.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kubelet.", Target: rule.NewTarget()},
},
),
Entry("should fail when AllAlpha featureGates are disabled for the kubelet",
func() {
shoot.Spec.Kubernetes.Kubelet = ptr.To(gardencorev1beta1.KubeletConfig{KubernetesConfig: gardencorev1beta1.KubernetesConfig{FeatureGates: map[string]bool{"AllAlpha": true}}})
},
[]rule.CheckResult{
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube apiserver.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube controller manager.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube scheduler.", Target: rule.NewTarget()},
{Status: rule.Passed, Message: "AllAlpha featureGates are not enabled for the kube proxy.", Target: rule.NewTarget()},
{Status: rule.Failed, Message: "AllAlpha featureGates are enabled for the kubelet.", Target: rule.NewTarget()},
},
),
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,11 @@ func (r *Ruleset) registerV01Rules(ruleOptions map[string]config.RuleOptionsConf
ShootName: r.args.ShootName,
ShootNamespace: r.args.ProjectNamespace,
},
rule.NewSkipRule(
"2002",
"Shoot clusters must not have Alpha APIs enabled for any Kubernetes component.",
"Not implemented.",
rule.NotImplemented,
rule.SkipRuleWithSeverity(rule.SeverityMedium),
),
&rules.Rule2002{
Client: c,
ShootName: r.args.ShootName,
ShootNamespace: r.args.ProjectNamespace,
},
&rules.Rule2003{
Client: c,
ShootName: r.args.ShootName,
Expand Down