From 47c41fff480a4406741bcdc0c5f4134a445922f6 Mon Sep 17 00:00:00 2001 From: georgibaltiev Date: Tue, 3 Feb 2026 17:11:16 +0200 Subject: [PATCH 1/4] update: DISA versioning in the documentation --- docs/providers/gardener.md | 2 +- docs/providers/managedk8s.md | 2 +- docs/providers/virtualgarden.md | 2 +- pkg/provider/gardener/ruleset/disak8sstig/ruleset.go | 2 +- pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go | 2 +- pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/providers/gardener.md b/docs/providers/gardener.md index 6fe4a1502..6721a2d14 100644 --- a/docs/providers/gardener.md +++ b/docs/providers/gardener.md @@ -8,8 +8,8 @@ The `Gardener` provider is capable of accessing a `seed/shoot` environment and r The `Gardener` provider implements the following `rulesets`: - [DISA Kubernetes Security Technical Implementation Guide](../rulesets/disa-k8s-stig/ruleset.md) + - v2r5 - v2r4 - - v2r3 ### Configuration diff --git a/docs/providers/managedk8s.md b/docs/providers/managedk8s.md index e4bbb8201..4ba1a7141 100644 --- a/docs/providers/managedk8s.md +++ b/docs/providers/managedk8s.md @@ -10,8 +10,8 @@ The `Managed Kubernetes` provider is capable of accessing a managed Kubernetes e The `Managed Kubernetes` provider implements the following `rulesets`: - [DISA Kubernetes Security Technical Implementation Guide](../rulesets/disa-k8s-stig/ruleset.md) + - v2r5 - v2r4 - - v2r3 - [Security Hardened Kubernetes Cluster](../rulesets/security-hardened-k8s/ruleset.md) - v0.1.0 diff --git a/docs/providers/virtualgarden.md b/docs/providers/virtualgarden.md index 6e487f41f..7c8350094 100644 --- a/docs/providers/virtualgarden.md +++ b/docs/providers/virtualgarden.md @@ -8,8 +8,8 @@ The `Virtual Garden` provider is capable of accessing a `runtime/virtual garden` The `Gardener` provider implements the following `rulesets`: - [DISA Kubernetes Security Technical Implementation Guide](../rulesets/disa-k8s-stig/ruleset.md) + - v2r5 - v2r4 - - v2r3 ### Configuration diff --git a/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go b/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go index b1ef36fbb..be1fa89f7 100644 --- a/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go @@ -33,7 +33,7 @@ var ( _ ruleset.Ruleset = &Ruleset{} // SupportedVersions is a list of available versions for the DISA Kubernetes STIG Ruleset. // Versions are sorted from newest to oldest. - SupportedVersions = []string{"v2r4", "v2r3"} + SupportedVersions = []string{"v2r5", "v2r4"} ) // Ruleset implements DISA Kubernetes STIG. diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go b/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go index e6ff7020f..a60fc5e4e 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go @@ -33,7 +33,7 @@ var ( _ ruleset.Ruleset = &Ruleset{} // SupportedVersions is a list of available versions for the DISA Kubernetes STIG Ruleset. // Versions are sorted from newest to oldest. - SupportedVersions = []string{"v2r4", "v2r3"} + SupportedVersions = []string{"v2r5", "v2r4"} ) // Ruleset implements DISA Kubernetes STIG. diff --git a/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go b/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go index 76c1086bf..6ac7946fc 100644 --- a/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go @@ -33,7 +33,7 @@ var ( _ ruleset.Ruleset = &Ruleset{} // SupportedVersions is a list of available versions for the DISA Kubernetes STIG Ruleset. // Versions are sorted from newest to oldest. - SupportedVersions = []string{"v2r4", "v2r3"} + SupportedVersions = []string{"v2r5", "v2r4"} ) // Ruleset implements DISA Kubernetes STIG. From 9602a223690cf9f70dff6fb89552957f98504f61 Mon Sep 17 00:00:00 2001 From: georgibaltiev Date: Tue, 3 Feb 2026 17:12:45 +0200 Subject: [PATCH 2/4] remove: support for version V2R3 --- .../gardener/ruleset/disak8sstig/ruleset.go | 7 - .../ruleset/disak8sstig/v2r3_ruleset.go | 725 -------------- .../managedk8s/ruleset/disak8sstig/ruleset.go | 7 - .../ruleset/disak8sstig/v2r3_ruleset.go | 941 ------------------ .../ruleset/disak8sstig/ruleset.go | 7 - .../ruleset/disak8sstig/v2r3_ruleset.go | 804 --------------- 6 files changed, 2491 deletions(-) delete mode 100644 pkg/provider/gardener/ruleset/disak8sstig/v2r3_ruleset.go delete mode 100644 pkg/provider/managedk8s/ruleset/disak8sstig/v2r3_ruleset.go delete mode 100644 pkg/provider/virtualgarden/ruleset/disak8sstig/v2r3_ruleset.go diff --git a/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go b/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go index be1fa89f7..178265e5e 100644 --- a/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go @@ -128,13 +128,6 @@ func FromGenericConfig(rulesetConfig config.RulesetConfig, additionalOpsPodLabel } switch rulesetConfig.Version { - case "v2r3": - if err := ruleset.validateV2R3RuleOptions(indexedRuleOptions, fldPath.Child("ruleOptions")); err != nil { - return nil, err - } - if err := ruleset.registerV2R3Rules(ruleOptions); err != nil { - return nil, err - } case "v2r4": if err := ruleset.validateV2R4RuleOptions(indexedRuleOptions, fldPath.Child("ruleOptions")); err != nil { return nil, err diff --git a/pkg/provider/gardener/ruleset/disak8sstig/v2r3_ruleset.go b/pkg/provider/gardener/ruleset/disak8sstig/v2r3_ruleset.go deleted file mode 100644 index 51632da3a..000000000 --- a/pkg/provider/gardener/ruleset/disak8sstig/v2r3_ruleset.go +++ /dev/null @@ -1,725 +0,0 @@ -// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors -// -// SPDX-License-Identifier: Apache-2.0 - -package disak8sstig - -import ( - "encoding/json" - "fmt" - - resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" - kubernetesgardener "github.com/gardener/gardener/pkg/client/kubernetes" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - - "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/provider/gardener/ruleset/disak8sstig/rules" - "github.com/gardener/diki/pkg/rule" - "github.com/gardener/diki/pkg/rule/retry" - "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" - sharedrules "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/rules" -) - -func validateV2R3Options[O rules.RuleOption](options any, fldPath *field.Path) field.ErrorList { - parsedOptions, err := getV2R3OptionOrNil[O](options) - if err != nil { - return field.ErrorList{ - field.InternalError(fldPath, err), - } - } - - if parsedOptions == nil { - return nil - } - - if val, ok := any(parsedOptions).(option.Option); ok { - return val.Validate(fldPath) - } - - return nil -} - -func parseV2R3Options[O rules.RuleOption](options any) (*O, error) { - optionsByte, err := json.Marshal(options) - if err != nil { - return nil, err - } - - var parsedOptions O - if err := json.Unmarshal(optionsByte, &parsedOptions); err != nil { - return nil, err - } - - return &parsedOptions, nil -} - -func getV2R3OptionOrNil[O rules.RuleOption](options any) (*O, error) { - if options == nil { - return nil, nil - } - return parseV2R3Options[O](options) -} - -func (r *Ruleset) validateV2R3RuleOptions(ruleOptions map[string]internalconfig.IndexedRuleOptionsConfig, fldPath *field.Path) error { - allErrs := field.ErrorList{} - - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242390](ruleOptions[sharedrules.ID242390].Args, fldPath.Index(ruleOptions[sharedrules.ID242390].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[rules.Options242400](ruleOptions[sharedrules.ID242400].Args, fldPath.Index(ruleOptions[sharedrules.ID242400].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.Options242414](ruleOptions[sharedrules.ID242414].Args, fldPath.Index(ruleOptions[sharedrules.ID242414].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.Options242415](ruleOptions[sharedrules.ID242415].Args, fldPath.Index(ruleOptions[sharedrules.ID242415].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.Options242442](ruleOptions[sharedrules.ID242442].Args, fldPath.Index(ruleOptions[sharedrules.ID242442].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242445].Args, fldPath.Index(ruleOptions[sharedrules.ID242445].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242446].Args, fldPath.Index(ruleOptions[sharedrules.ID242446].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[rules.Options242451](ruleOptions[sharedrules.ID242451].Args, fldPath.Index(ruleOptions[sharedrules.ID242451].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[rules.Options242466](ruleOptions[sharedrules.ID242466].Args, fldPath.Index(ruleOptions[sharedrules.ID242466].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[rules.Options242467](ruleOptions[sharedrules.ID242467].Args, fldPath.Index(ruleOptions[sharedrules.ID242467].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options245543](ruleOptions[sharedrules.ID245543].Args, fldPath.Index(ruleOptions[sharedrules.ID245543].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options254800](ruleOptions[sharedrules.ID254800].Args, fldPath.Index(ruleOptions[sharedrules.ID254800].Index).Child("args"))...) - - return allErrs.ToAggregate() -} - -func (r *Ruleset) registerV2R3Rules(ruleOptions map[string]config.RuleOptionsConfig) error { // TODO: add to FromGenericConfig - shootClient, err := client.New(r.ShootConfig, client.Options{Scheme: kubernetesgardener.ShootScheme}) - if err != nil { - return err - } - - seedClient, err := client.New(r.SeedConfig, client.Options{Scheme: kubernetesgardener.SeedScheme}) - if err != nil { - return err - } - - shootPodContext, err := pod.NewSimplePodContext(shootClient, r.ShootConfig, r.AdditionalOpsPodLabels) - if err != nil { - return err - } - - seedPodContext, err := pod.NewSimplePodContext(seedClient, r.SeedConfig, r.AdditionalOpsPodLabels) - if err != nil { - return err - } - - shootClientSet, err := kubernetes.NewForConfig(r.ShootConfig) - if err != nil { - return err - } - - opts242390, err := getV2R3OptionOrNil[sharedrules.Options242390](ruleOptions[sharedrules.ID242390].Args) - if err != nil { - return fmt.Errorf("rule option 242390 error: %s", err.Error()) - } - opts242400, err := getV2R3OptionOrNil[rules.Options242400](ruleOptions[sharedrules.ID242400].Args) - if err != nil { - return fmt.Errorf("rule option 242400 error: %s", err.Error()) - } - opts242414, err := getV2R3OptionOrNil[disaoption.Options242414](ruleOptions[sharedrules.ID242414].Args) - if err != nil { - return fmt.Errorf("rule option 242414 error: %s", err.Error()) - } - opts242415, err := getV2R3OptionOrNil[disaoption.Options242415](ruleOptions[sharedrules.ID242415].Args) - if err != nil { - return fmt.Errorf("rule option 242415 error: %s", err.Error()) - } - opts242442, err := getV2R3OptionOrNil[disaoption.Options242442](ruleOptions[sharedrules.ID242442].Args) - if err != nil { - return fmt.Errorf("rule option 242442 error: %s", err.Error()) - } - opts242445, err := getV2R3OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242445].Args) - if err != nil { - return fmt.Errorf("rule option 242445 error: %s", err.Error()) - } - opts242446, err := getV2R3OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242446].Args) - if err != nil { - return fmt.Errorf("rule option 242446 error: %s", err.Error()) - } - opts242451, err := getV2R3OptionOrNil[rules.Options242451](ruleOptions[sharedrules.ID242451].Args) - if err != nil { - return fmt.Errorf("rule option 242451 error: %s", err.Error()) - } - opts242466, err := getV2R3OptionOrNil[rules.Options242466](ruleOptions[sharedrules.ID242466].Args) - if err != nil { - return fmt.Errorf("rule option 242466 error: %s", err.Error()) - } - opts242467, err := getV2R3OptionOrNil[rules.Options242467](ruleOptions[sharedrules.ID242467].Args) - if err != nil { - return fmt.Errorf("rule option 242467 error: %s", err.Error()) - } - opts245543, err := getV2R3OptionOrNil[sharedrules.Options245543](ruleOptions[sharedrules.ID245543].Args) - if err != nil { - return fmt.Errorf("rule option 245543 error: %s", err.Error()) - } - opts254800, err := getV2R3OptionOrNil[sharedrules.Options254800](ruleOptions[sharedrules.ID254800].Args) - if err != nil { - return fmt.Errorf("rule option 254800 error: %s", err.Error()) - } - - rcOpsPod := retry.RetryConditionFromRegex( - *retryerrors.OpsPodNotFoundRegexp, - ) - rcFileChecks := retry.RetryConditionFromRegex( - *retryerrors.ContainerNotFoundOnNodeRegexp, - *retryerrors.ContainerFileNotFoundOnNodeRegexp, - *retryerrors.ContainerNotReadyRegexp, - *retryerrors.OpsPodNotFoundRegexp, - *retryerrors.ObjectNotFoundRegexp, - ) - - // Gardener images use distroless nonroot user with ID 65532 - // https://github.com/GoogleContainerTools/distroless/blob/main/base/base.bzl#L8 - gardenerFileOwnerOptions := &disaoption.FileOwnerOptions{ - ExpectedFileOwner: disaoption.ExpectedOwner{ - Users: []string{"0", "65532"}, - Groups: []string{"0", "65532"}, - }, - } - workerPoolGroupByLabels := []string{"worker.gardener.cloud/pool"} - - rules := []rule.Rule{ - &sharedrules.Rule242376{Client: seedClient, Namespace: r.shootNamespace}, - &rules.Rule242377{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242378{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242379{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242380{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242381{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242382{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242383{Client: shootClient}, - rule.NewSkipRule( - sharedrules.ID242384, - "The Kubernetes Scheduler must have secure binding.", - "The Kubernetes Scheduler runs in a container which already has limited access to network interfaces. In addition ingress traffic to the Kubernetes Scheduler is restricted via network policies, making an unintended exposure less likely.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242385, - "The Kubernetes Controller Manager must have secure binding.", - "The Kubernetes Controller Manager runs in a container which already has limited access to network interfaces. In addition ingress traffic to the Kubernetes Controller Manager is restricted via network policies, making an unintended exposure less likely.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242386{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242387{ - Client: shootClient, - V1RESTClient: shootClientSet.CoreV1().RESTClient(), - }, - &sharedrules.Rule242388{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242389{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242390{Client: seedClient, Namespace: r.shootNamespace, Options: opts242390}, - &sharedrules.Rule242391{ - Client: shootClient, - V1RESTClient: shootClientSet.CoreV1().RESTClient(), - }, - &sharedrules.Rule242392{ - Client: shootClient, - V1RESTClient: shootClientSet.CoreV1().RESTClient(), - }, - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242393)), - retry.WithBaseRule(&sharedrules.Rule242393{ - Logger: r.Logger().With("rule_id", sharedrules.ID242393), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242393{ - NodeGroupByLabels: workerPoolGroupByLabels, - }, - }), - retry.WithRetryCondition(rcOpsPod), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242394)), - retry.WithBaseRule(&sharedrules.Rule242394{ - Logger: r.Logger().With("rule_id", sharedrules.ID242394), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242394{ - NodeGroupByLabels: workerPoolGroupByLabels, - }, - }), - retry.WithRetryCondition(rcOpsPod), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - &sharedrules.Rule242395{Client: shootClient}, - rule.NewSkipRule( - sharedrules.ID242396, - "Kubernetes Kubectl cp command must give expected access and results.", - `"kubectl" is not installed into control plane pods or worker nodes and Gardener does not offer Kubernetes v1.12 or older.`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242397{ - Client: shootClient, - V1RESTClient: shootClientSet.CoreV1().RESTClient(), - }, - rule.NewSkipRule( - sharedrules.ID242398, - "Kubernetes DynamicAuditing must not be enabled.", - // feature-gates.DynamicAuditing removed in v1.19. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ - "Option feature-gates.DynamicAuditing removed in Kubernetes v1.19.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242399, - "Kubernetes DynamicKubeletConfig must not be enabled.", - // feature-gates.DynamicKubeletConfig removed in v1.26. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ - "Option feature-gates.DynamicKubeletConfig removed in Kubernetes v1.26.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242400)), - retry.WithBaseRule(&rules.Rule242400{ - Logger: r.Logger().With("rule_id", sharedrules.ID242400), - InstanceID: r.instanceID, - ControlPlaneClient: seedClient, - ClusterClient: shootClient, - ClusterPodContext: shootPodContext, - ClusterV1RESTClient: shootClientSet.CoreV1().RESTClient(), - ControlPlaneNamespace: r.shootNamespace, - Options: opts242400, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - &sharedrules.Rule242402{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242403{Client: seedClient, Namespace: r.shootNamespace}, - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242404)), - retry.WithBaseRule(&sharedrules.Rule242404{ - Logger: r.Logger().With("rule_id", sharedrules.ID242404), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242404{ - NodeGroupByLabels: workerPoolGroupByLabels, - }, - }), - retry.WithRetryCondition(rcOpsPod), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - rule.NewSkipRule( - sharedrules.ID242405, - "Kubernetes manifests must be owned by root.", - "Gardener does not deploy any control plane component as systemd processes or static pod.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242406)), - retry.WithBaseRule(&sharedrules.Rule242406{ - Logger: r.Logger().With("rule_id", sharedrules.ID242406), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242406{ - NodeGroupByLabels: workerPoolGroupByLabels, - FileOwnerOptions: gardenerFileOwnerOptions, - }, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242407)), - retry.WithBaseRule(&sharedrules.Rule242407{ - Logger: r.Logger().With("rule_id", sharedrules.ID242407), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242407{ - NodeGroupByLabels: workerPoolGroupByLabels, - }, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - rule.NewSkipRule( - sharedrules.ID242408, - "The Kubernetes manifest files must have least privileges.", - `Gardener does not deploy any control plane component as systemd processes or static pod.`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242409{Client: seedClient, Namespace: r.shootNamespace}, - rule.NewSkipRule( - sharedrules.ID242410, - "The Kubernetes API Server must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242411, - "The Kubernetes Scheduler must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242412, - "The Kubernetes Controllers must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242413, - "The Kubernetes etcd must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &rules.Rule242414{ - ClusterClient: shootClient, - ControlPlaneClient: seedClient, - ControlPlaneNamespace: r.shootNamespace, - Options: opts242414, - }, - &rules.Rule242415{ - ClusterClient: shootClient, - ControlPlaneClient: seedClient, - ControlPlaneNamespace: r.shootNamespace, - Options: opts242415, - }, - &sharedrules.Rule242417{ - Client: shootClient, - Options: &sharedrules.Options242417{ - AcceptedPods: []sharedrules.AcceptedPods242417{ - { - AcceptedNamespacedObject: option.AcceptedNamespacedObject{ - NamespacedObjectSelector: option.NamespacedObjectSelector{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: map[string]string{resourcesv1alpha1.ManagedBy: "gardener"}, - }, - NamespaceLabelSelector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"kubernetes.io/metadata.name": "kube-system"}, - }, - }, - Justification: "Gardener managed pods are not user pods", - }, - Status: "Passed", - }, - }, - }, - }, - &sharedrules.Rule242418{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242419{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242420{ - Client: shootClient, - V1RESTClient: shootClientSet.CoreV1().RESTClient(), - }, - &sharedrules.Rule242421{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242422{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242423{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242424{ - Client: shootClient, - V1RESTClient: shootClientSet.CoreV1().RESTClient(), - }, - &sharedrules.Rule242425{ - Client: shootClient, - V1RESTClient: shootClientSet.CoreV1().RESTClient(), - }, - &sharedrules.Rule242426{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242427{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242428{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242429{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242430{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242431{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242432{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242433{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242434{ - Client: shootClient, - V1RESTClient: shootClientSet.CoreV1().RESTClient(), - }, - &sharedrules.Rule242436{Client: seedClient, Namespace: r.shootNamespace}, - rule.NewSkipRule( - sharedrules.ID242437, - "Kubernetes must have a pod security policy set.", - "PSPs are removed in K8s version 1.25.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - &sharedrules.Rule242438{Client: seedClient, Namespace: r.shootNamespace}, - &rules.Rule242442{ClusterClient: shootClient, ControlPlaneClient: seedClient, ControlPlaneNamespace: r.shootNamespace, Options: opts242442}, - rule.NewSkipRule( - sharedrules.ID242443, - "Kubernetes must contain the latest updates as authorized by IAVMs, CTOs, DTMs, and STIGs.", - "Scanning/patching security vulnerabilities should be enforced organizationally. Security vulnerability scanning should be automated and maintainers should be informed automatically.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242444, - "Kubernetes component manifests must be owned by root.", - `Rule is duplicate of "242405"`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242445)), - retry.WithBaseRule(&sharedrules.Rule242445{ - Logger: r.Logger().With("rule_id", sharedrules.ID242445), - InstanceID: r.instanceID, - Client: seedClient, - PodContext: seedPodContext, - Namespace: r.shootNamespace, - Options: opts242445, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242446)), - retry.WithBaseRule(&sharedrules.Rule242446{ - Logger: r.Logger().With("rule_id", sharedrules.ID242446), - InstanceID: r.instanceID, - Client: seedClient, - PodContext: seedPodContext, - Namespace: r.shootNamespace, - Options: opts242446, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242447)), - retry.WithBaseRule(&sharedrules.Rule242447{ - Logger: r.Logger().With("rule_id", sharedrules.ID242447), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242448)), - retry.WithBaseRule(&sharedrules.Rule242448{ - Logger: r.Logger().With("rule_id", sharedrules.ID242448), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242448{ - FileOwnerOptions: gardenerFileOwnerOptions, - }, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242449)), - retry.WithBaseRule(&sharedrules.Rule242449{ - Logger: r.Logger().With("rule_id", sharedrules.ID242449), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242449{ - NodeGroupByLabels: workerPoolGroupByLabels, - }, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242450)), - retry.WithBaseRule(&sharedrules.Rule242450{ - Logger: r.Logger().With("rule_id", sharedrules.ID242450), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242450{ - NodeGroupByLabels: workerPoolGroupByLabels, - FileOwnerOptions: gardenerFileOwnerOptions, - }, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242451)), - retry.WithBaseRule(&rules.Rule242451{ - Logger: r.Logger().With("rule_id", sharedrules.ID242451), - InstanceID: r.instanceID, - ControlPlaneClient: seedClient, - ClusterClient: shootClient, - ControlPlanePodContext: seedPodContext, - ClusterPodContext: shootPodContext, - ControlPlaneNamespace: r.shootNamespace, - Options: opts242451, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242452)), - retry.WithBaseRule(&sharedrules.Rule242452{ - Logger: r.Logger().With("rule_id", sharedrules.ID242452), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242452{ - NodeGroupByLabels: workerPoolGroupByLabels, - }, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242453)), - retry.WithBaseRule(&sharedrules.Rule242453{ - Logger: r.Logger().With("rule_id", sharedrules.ID242453), - InstanceID: r.instanceID, - Client: shootClient, - PodContext: shootPodContext, - Options: &sharedrules.Options242453{ - NodeGroupByLabels: workerPoolGroupByLabels, - FileOwnerOptions: gardenerFileOwnerOptions, - }, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - rule.NewSkipRule( - sharedrules.ID242454, - "Kubernetes kubeadm.conf must be owned by root.", - `Gardener does not use "kubeadm" and also does not store any "main config" anywhere in seed or shoot (flow/component logic built-in/in-code).`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242455, - "Kubernetes kubeadm.conf must have file permissions set to 644 or more restrictive.", - `Gardener does not use "kubeadm" and also does not store any "main config" anywhere in seed or shoot (flow/component logic built-in/in-code).`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242456, - "Kubernetes kubelet config must have file permissions set to 644 or more restrictive.", - `Rule is duplicate of "242452".`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242457, - "Kubernetes kubelet config must be owned by root.", - `Rule is duplicate of "242453".`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242459)), - retry.WithBaseRule(&sharedrules.Rule242459{ - Logger: r.Logger().With("rule_id", sharedrules.ID242459), - InstanceID: r.instanceID, - Client: seedClient, - PodContext: seedPodContext, - Namespace: r.shootNamespace, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242460)), - retry.WithBaseRule(&sharedrules.Rule242460{ - Logger: r.Logger().With("rule_id", sharedrules.ID242460), - InstanceID: r.instanceID, - Client: seedClient, - PodContext: seedPodContext, - Namespace: r.shootNamespace, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - &sharedrules.Rule242461{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242462{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242463{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule242464{Client: seedClient, Namespace: r.shootNamespace}, - rule.NewSkipRule( - sharedrules.ID242465, - "Kubernetes API Server audit log path must be set.", - `Rule is duplicate of "242402"`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242466)), - retry.WithBaseRule(&rules.Rule242466{ - Logger: r.Logger().With("rule_id", sharedrules.ID242466), - InstanceID: r.instanceID, - ControlPlaneClient: seedClient, - ClusterClient: shootClient, - ControlPlanePodContext: seedPodContext, - ClusterPodContext: shootPodContext, - ControlPlaneNamespace: r.shootNamespace, - Options: opts242466, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242467)), - retry.WithBaseRule(&rules.Rule242467{ - Logger: r.Logger().With("rule_id", sharedrules.ID242467), - InstanceID: r.instanceID, - ControlPlaneClient: seedClient, - ClusterClient: shootClient, - ControlPlanePodContext: seedPodContext, - ClusterPodContext: shootPodContext, - ControlPlaneNamespace: r.shootNamespace, - Options: opts242467, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - &sharedrules.Rule245541{ - Client: shootClient, - V1RESTClient: shootClientSet.CoreV1().RESTClient(), - }, - &sharedrules.Rule245542{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule245543{Client: seedClient, Namespace: r.shootNamespace, Options: opts245543}, - &sharedrules.Rule245544{Client: seedClient, Namespace: r.shootNamespace}, - &sharedrules.Rule254800{Client: seedClient, Namespace: r.shootNamespace, Options: opts254800}, - rule.NewSkipRule( - // featureGates.PodSecurity made GA in v1.25 and removed in v1.28. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ - sharedrules.ID254801, - "Kubernetes must enable PodSecurity admission controller on static pods and Kubelets.", - "Option featureGates.PodSecurity was made GA in v1.25 and removed in v1.28.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - } - - for i, r := range rules { - var severityLevel rule.SeverityLevel - if severity, ok := r.(rule.Severity); !ok { - return fmt.Errorf("rule %s does not implement rule.Severity", r.ID()) - } else { - severityLevel = severity.Severity() - } - - opt, found := ruleOptions[r.ID()] - if found && opt.Skip != nil && opt.Skip.Enabled { - rules[i] = rule.NewSkipRule(r.ID(), r.Name(), opt.Skip.Justification, rule.Accepted, rule.SkipRuleWithSeverity(severityLevel)) - } - } - - // check that the registered rules equal - // the number of rules in that ruleset version - if len(rules) != 91 { - return fmt.Errorf("revision expects 91 registered rules, but got: %d", len(rules)) - } - - return r.AddRules(rules...) -} diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go b/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go index a60fc5e4e..6d62e7c7b 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go @@ -123,13 +123,6 @@ func FromGenericConfig(rulesetConfig config.RulesetConfig, additionalOpsPodLabel } switch rulesetConfig.Version { - case "v2r3": - if err := ruleset.validateV2R3RuleOptions(indexedRuleOptions, fldPath.Child("ruleOptions")); err != nil { - return nil, err - } - if err := ruleset.registerV2R3Rules(ruleOptions); err != nil { - return nil, err - } case "v2r4": if err := ruleset.validateV2R4RuleOptions(indexedRuleOptions, fldPath.Child("ruleOptions")); err != nil { return nil, err diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/v2r3_ruleset.go b/pkg/provider/managedk8s/ruleset/disak8sstig/v2r3_ruleset.go deleted file mode 100644 index 4febd771f..000000000 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/v2r3_ruleset.go +++ /dev/null @@ -1,941 +0,0 @@ -// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors -// -// SPDX-License-Identifier: Apache-2.0 - -package disak8sstig - -import ( - "crypto/tls" - "crypto/x509" - "encoding/json" - "errors" - "fmt" - "net/http" - "os" - "path/filepath" - - "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - - "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/provider/managedk8s/ruleset/disak8sstig/rules" - "github.com/gardener/diki/pkg/rule" - "github.com/gardener/diki/pkg/rule/retry" - "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" - sharedrules "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/rules" -) - -func validateV2R3Options[O rules.RuleOption](options any, fldPath *field.Path) field.ErrorList { - parsedOptions, err := getV2R3OptionOrNil[O](options) - if err != nil { - return field.ErrorList{ - field.InternalError(fldPath, err), - } - } - - if parsedOptions == nil { - return nil - } - - if val, ok := any(parsedOptions).(option.Option); ok { - return val.Validate(fldPath) - } - - return nil -} - -func (r *Ruleset) validateV2R3RuleOptions(ruleOptions map[string]internalconfig.IndexedRuleOptionsConfig, fldPath *field.Path) error { - allErrs := field.ErrorList{} - - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242383](ruleOptions[sharedrules.ID242383].Args, fldPath.Index(ruleOptions[sharedrules.ID242383].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242393](ruleOptions[sharedrules.ID242393].Args, fldPath.Index(ruleOptions[sharedrules.ID242393].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242394](ruleOptions[sharedrules.ID242394].Args, fldPath.Index(ruleOptions[sharedrules.ID242394].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242396](ruleOptions[sharedrules.ID242396].Args, fldPath.Index(ruleOptions[sharedrules.ID242396].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[rules.Options242400](ruleOptions[sharedrules.ID242400].Args, fldPath.Index(ruleOptions[sharedrules.ID242400].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242404](ruleOptions[sharedrules.ID242404].Args, fldPath.Index(ruleOptions[sharedrules.ID242404].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242406](ruleOptions[sharedrules.ID242406].Args, fldPath.Index(ruleOptions[sharedrules.ID242406].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242407](ruleOptions[sharedrules.ID242407].Args, fldPath.Index(ruleOptions[sharedrules.ID242407].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.Options242414](ruleOptions[sharedrules.ID242414].Args, fldPath.Index(ruleOptions[sharedrules.ID242414].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.Options242415](ruleOptions[sharedrules.ID242415].Args, fldPath.Index(ruleOptions[sharedrules.ID242415].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242417](ruleOptions[sharedrules.ID242417].Args, fldPath.Index(ruleOptions[sharedrules.ID242417].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[rules.Options242442](ruleOptions[sharedrules.ID242442].Args, fldPath.Index(ruleOptions[sharedrules.ID242442].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[option.ClusterObjectSelector](ruleOptions[sharedrules.ID242447].Args, fldPath.Index(ruleOptions[sharedrules.ID242447].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242448](ruleOptions[sharedrules.ID242448].Args, fldPath.Index(ruleOptions[sharedrules.ID242448].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242449](ruleOptions[sharedrules.ID242449].Args, fldPath.Index(ruleOptions[sharedrules.ID242449].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242450](ruleOptions[sharedrules.ID242450].Args, fldPath.Index(ruleOptions[sharedrules.ID242450].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[rules.Options242451](ruleOptions[sharedrules.ID242451].Args, fldPath.Index(ruleOptions[sharedrules.ID242451].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242452](ruleOptions[sharedrules.ID242452].Args, fldPath.Index(ruleOptions[sharedrules.ID242452].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242453](ruleOptions[sharedrules.ID242453].Args, fldPath.Index(ruleOptions[sharedrules.ID242453].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[rules.Options242466](ruleOptions[sharedrules.ID242466].Args, fldPath.Index(ruleOptions[sharedrules.ID242466].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[rules.Options242467](ruleOptions[sharedrules.ID242467].Args, fldPath.Index(ruleOptions[sharedrules.ID242467].Index).Child("args"))...) - - return allErrs.ToAggregate() -} - -func (r *Ruleset) registerV2R3Rules(ruleOptions map[string]config.RuleOptionsConfig) error { // TODO: add to FromGenericConfig - client, err := client.New(r.Config, client.Options{}) - if err != nil { - return err - } - - podContext, err := pod.NewSimplePodContext(client, r.Config, r.AdditionalOpsPodLabels) - if err != nil { - return err - } - - clientSet, err := kubernetes.NewForConfig(r.Config) - if err != nil { - return err - } - - authorityCertPool := x509.NewCertPool() - - switch { - case len(r.Config.CAData) > 0: - ok := authorityCertPool.AppendCertsFromPEM(r.Config.CAData) - if !ok { - return errors.New("failed to parse kube-apiserver CA data from config") - } - case len(r.Config.CAFile) > 0: - caFileData, err := os.ReadFile(filepath.Clean(r.Config.CAFile)) - if err != nil { - return fmt.Errorf("failed to read CA file: %w", err) - } - ok := authorityCertPool.AppendCertsFromPEM(caFileData) - if !ok { - return errors.New("failed to parse kube-apiserver CA data from config") - } - default: - authorityCertPool = nil - } - - opts242383, err := getV2R3OptionOrNil[sharedrules.Options242383](ruleOptions[sharedrules.ID242383].Args) - if err != nil { - return fmt.Errorf("rule option 242383 error: %s", err.Error()) - } - opts242393, err := getV2R3OptionOrNil[sharedrules.Options242393](ruleOptions[sharedrules.ID242393].Args) - if err != nil { - return fmt.Errorf("rule option 242393 error: %s", err.Error()) - } - opts242394, err := getV2R3OptionOrNil[sharedrules.Options242394](ruleOptions[sharedrules.ID242394].Args) - if err != nil { - return fmt.Errorf("rule option 242394 error: %s", err.Error()) - } - opts242396, err := getV2R3OptionOrNil[sharedrules.Options242396](ruleOptions[sharedrules.ID242396].Args) - if err != nil { - return fmt.Errorf("rule option 242396 error: %s", err.Error()) - } - opts242400, err := getV2R3OptionOrNil[rules.Options242400](ruleOptions[sharedrules.ID242400].Args) - if err != nil { - return fmt.Errorf("rule option 242400 error: %s", err.Error()) - } - opts242404, err := getV2R3OptionOrNil[sharedrules.Options242404](ruleOptions[sharedrules.ID242404].Args) - if err != nil { - return fmt.Errorf("rule option 242404 error: %s", err.Error()) - } - opts242406, err := getV2R3OptionOrNil[sharedrules.Options242406](ruleOptions[sharedrules.ID242406].Args) - if err != nil { - return fmt.Errorf("rule option 242406 error: %s", err.Error()) - } - opts242407, err := getV2R3OptionOrNil[sharedrules.Options242407](ruleOptions[sharedrules.ID242407].Args) - if err != nil { - return fmt.Errorf("rule option 242407 error: %s", err.Error()) - } - opts242414, err := getV2R3OptionOrNil[disaoption.Options242414](ruleOptions[sharedrules.ID242414].Args) - if err != nil { - return fmt.Errorf("rule option 242414 error: %s", err.Error()) - } - opts242415, err := getV2R3OptionOrNil[disaoption.Options242415](ruleOptions[sharedrules.ID242415].Args) - if err != nil { - return fmt.Errorf("rule option 242415 error: %s", err.Error()) - } - opts242417, err := getV2R3OptionOrNil[sharedrules.Options242417](ruleOptions[sharedrules.ID242417].Args) - if err != nil { - return fmt.Errorf("rule option 242417 error: %s", err.Error()) - } - opts242442, err := getV2R3OptionOrNil[rules.Options242442](ruleOptions[sharedrules.ID242442].Args) - if err != nil { - return fmt.Errorf("rule option 242442 error: %s", err.Error()) - } - opts242447, err := getV2R3OptionOrNil[option.ClusterObjectSelector](ruleOptions[sharedrules.ID242447].Args) - if err != nil { - return fmt.Errorf("rule option 242447 error: %s", err.Error()) - } - opts242448, err := getV2R3OptionOrNil[sharedrules.Options242448](ruleOptions[sharedrules.ID242448].Args) - if err != nil { - return fmt.Errorf("rule option 242448 error: %s", err.Error()) - } - opts242449, err := getV2R3OptionOrNil[sharedrules.Options242449](ruleOptions[sharedrules.ID242449].Args) - if err != nil { - return fmt.Errorf("rule option 242449 error: %s", err.Error()) - } - opts242450, err := getV2R3OptionOrNil[sharedrules.Options242450](ruleOptions[sharedrules.ID242450].Args) - if err != nil { - return fmt.Errorf("rule option 242450 error: %s", err.Error()) - } - opts242451, err := getV2R3OptionOrNil[rules.Options242451](ruleOptions[sharedrules.ID242451].Args) - if err != nil { - return fmt.Errorf("rule option 242451 error: %s", err.Error()) - } - opts242452, err := getV2R3OptionOrNil[sharedrules.Options242452](ruleOptions[sharedrules.ID242452].Args) - if err != nil { - return fmt.Errorf("rule option 242452 error: %s", err.Error()) - } - opts242453, err := getV2R3OptionOrNil[sharedrules.Options242453](ruleOptions[sharedrules.ID242453].Args) - if err != nil { - return fmt.Errorf("rule option 242453 error: %s", err.Error()) - } - opts242466, err := getV2R3OptionOrNil[rules.Options242466](ruleOptions[sharedrules.ID242466].Args) - if err != nil { - return fmt.Errorf("rule option 242466 error: %s", err.Error()) - } - opts242467, err := getV2R3OptionOrNil[rules.Options242467](ruleOptions[sharedrules.ID242467].Args) - if err != nil { - return fmt.Errorf("rule option 242467 error: %s", err.Error()) - } - - rcOpsPod := retry.RetryConditionFromRegex( - *retryerrors.OpsPodNotFoundRegexp, - ) - rcFileChecks := retry.RetryConditionFromRegex( - *retryerrors.ContainerNotFoundOnNodeRegexp, - *retryerrors.ContainerFileNotFoundOnNodeRegexp, - *retryerrors.ContainerNotReadyRegexp, - *retryerrors.OpsPodNotFoundRegexp, - *retryerrors.ObjectNotFoundRegexp, - ) - - const ( - noControlPlaneMsg = "The Managed Kubernetes cluster does not have access to control plane components." - ) - rules := []rule.Rule{ - rule.NewSkipRule( - sharedrules.ID242376, - "The Kubernetes Controller Manager must use TLS 1.2, at a minimum, to protect the confidentiality of sensitive data during electronic dissemination.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242377, - "The Kubernetes Scheduler must use TLS 1.2, at a minimum, to protect the confidentiality of sensitive data during electronic dissemination.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242378, - "The Kubernetes API Server must use TLS 1.2, at a minimum, to protect the confidentiality of sensitive data during electronic dissemination.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242379, - "The Kubernetes etcd must use TLS to protect the confidentiality of sensitive data during electronic dissemination.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242380, - "The Kubernetes etcd must use TLS to protect the confidentiality of sensitive data during electronic dissemination.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242381, - "The Kubernetes Controller Manager must create unique service accounts for each work payload.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID242382, - "The Kubernetes API Server must enable Node,RBAC as the authorization mode.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242383{ - Client: client, - Options: opts242383, - }, - rule.NewSkipRule( - sharedrules.ID242384, - "The Kubernetes Scheduler must have secure binding.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242385, - "The Kubernetes Controller Manager must have secure binding.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242386, - "The Kubernetes API server must have the insecure port flag disabled.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - &sharedrules.Rule242387{ - Client: client, - V1RESTClient: clientSet.CoreV1().RESTClient(), - }, - rule.NewSkipRule( - sharedrules.ID242388, - "The Kubernetes API server must have the insecure bind address not set.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID242389, - "The Kubernetes API server must have the secure port set.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &rules.Rule242390{ - KAPIExternalURL: r.Config.Host, - Client: &http.Client{ - Transport: &http.Transport{ - // the TLS MinVersion warnings are ignored in order to avoid version conflicts - TLSClientConfig: &tls.Config{ // #nosec: G402 - RootCAs: authorityCertPool, - InsecureSkipVerify: r.Config.Insecure, // #nosec: G402 - }, - }, - }, - }, - &sharedrules.Rule242391{ - Client: client, - V1RESTClient: clientSet.CoreV1().RESTClient(), - }, - &sharedrules.Rule242392{ - Client: client, - V1RESTClient: clientSet.CoreV1().RESTClient(), - }, - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242393)), - retry.WithBaseRule(&sharedrules.Rule242393{ - Logger: r.Logger().With("rule_id", sharedrules.ID242393), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242393, - }), - retry.WithRetryCondition(rcOpsPod), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242394)), - retry.WithBaseRule(&sharedrules.Rule242394{ - Logger: r.Logger().With("rule_id", sharedrules.ID242394), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242394, - }), - retry.WithRetryCondition(rcOpsPod), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - &sharedrules.Rule242395{Client: client}, - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242396)), - retry.WithBaseRule(&sharedrules.Rule242396{ - Logger: r.Logger().With("rule_id", sharedrules.ID242396), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242396, - }), - retry.WithRetryCondition(rcOpsPod), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - &sharedrules.Rule242397{ - Client: client, - V1RESTClient: clientSet.CoreV1().RESTClient(), - }, - rule.NewSkipRule( - // feature-gates.DynamicAuditing removed in v1.19. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ - sharedrules.ID242398, - "Kubernetes DynamicAuditing must not be enabled.", - "Option feature-gates.DynamicAuditing was removed in Kubernetes v1.19.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242399, - "Kubernetes DynamicKubeletConfig must not be enabled.", - // feature-gates.DynamicKubeletConfig removed in v1.26. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ - "Option feature-gates.DynamicKubeletConfig removed in Kubernetes v1.26.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242400)), - retry.WithBaseRule(&rules.Rule242400{ - Logger: r.Logger().With("rule_id", sharedrules.ID242400), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - V1RESTClient: clientSet.CoreV1().RESTClient(), - Options: opts242400, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - - rule.NewSkipRule( - sharedrules.ID242402, - "The Kubernetes API Server must have an audit log path set.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242403, - "Kubernetes API Server must generate audit records that identify what type of event has occurred, identify the source of the event, contain the event results, identify any users, and identify any containers associated with the event.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242404)), - retry.WithBaseRule(&sharedrules.Rule242404{ - Logger: r.Logger().With("rule_id", sharedrules.ID242404), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242404, - }), - retry.WithRetryCondition(rcOpsPod), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - rule.NewSkipRule( - sharedrules.ID242405, - "Kubernetes manifests must be owned by root.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242406)), - retry.WithBaseRule(&sharedrules.Rule242406{ - Logger: r.Logger().With("rule_id", sharedrules.ID242406), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242406, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242407)), - retry.WithBaseRule(&sharedrules.Rule242407{ - Logger: r.Logger().With("rule_id", sharedrules.ID242407), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242407, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - rule.NewSkipRule( - sharedrules.ID242408, - "The Kubernetes manifest files must have least privileges.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242409, - "Kubernetes Controller Manager must disable profiling.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242410, - "The Kubernetes API Server must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242411, - "The Kubernetes Scheduler must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242412, - "The Kubernetes Controllers must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242413, - "The Kubernetes etcd must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &rules.Rule242414{ - Client: client, - Options: opts242414, - }, - &rules.Rule242415{ - Client: client, - Options: opts242415, - }, - &sharedrules.Rule242417{ - Client: client, - Options: opts242417, - }, - rule.NewSkipRule( - sharedrules.ID242418, - "The Kubernetes API server must use approved cipher suites.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242419, - "Kubernetes API Server must have the SSL Certificate Authority set.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242420{ - Client: client, - V1RESTClient: clientSet.CoreV1().RESTClient(), - }, - rule.NewSkipRule( - sharedrules.ID242421, - "Kubernetes Controller Manager must have the SSL Certificate Authority set.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242422, - "Kubernetes API Server must have a certificate for communication.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242423, - "Kubernetes etcd must enable client authentication to secure service.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242424{ - Client: client, - V1RESTClient: clientSet.CoreV1().RESTClient(), - }, - &sharedrules.Rule242425{ - Client: client, - V1RESTClient: clientSet.CoreV1().RESTClient(), - }, - rule.NewSkipRule( - sharedrules.ID242426, - "Kubernetes etcd must enable client authentication to secure service.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242427, - "Kubernetes etcd must have a key file for secure communication.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242428, - "Kubernetes etcd must have a certificate for communication.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242429, - "Kubernetes etcd must have the SSL Certificate Authority set.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242430, - "Kubernetes etcd must have a certificate for communication.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242431, - "Kubernetes etcd must have a key file for secure communication.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242432, - "Kubernetes etcd must have peer-cert-file set for secure communication.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242433, - "Kubernetes etcd must have a peer-key-file set for secure communication.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242434{ - Client: client, - V1RESTClient: clientSet.CoreV1().RESTClient(), - }, - rule.NewSkipRule( - sharedrules.ID242436, - "The Kubernetes API server must have the ValidatingAdmissionWebhook enabled.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID242437, - "Kubernetes must have a pod security policy set.", - "PSPs are removed in K8s version 1.25.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID242438, - "Kubernetes API Server must configure timeouts to limit attack surface.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &rules.Rule242442{ - // We check only system (kube-proxy) pods in this rule, since there can be a user case to run different versions of images. - Client: client, - Options: opts242442, - }, - rule.NewSkipRule( - sharedrules.ID242443, - "Kubernetes must contain the latest updates as authorized by IAVMs, CTOs, DTMs, and STIGs.", - "Scanning/patching security vulnerabilities should be enforced organizationally. Security vulnerability scanning should be automated and maintainers should be informed automatically.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242444, - "Kubernetes component manifests must be owned by root.", - "Rule is duplicate of 242405. "+noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242445, - "The Kubernetes component etcd must be owned by etcd.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242446, - "The Kubernetes conf files must be owned by root.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242447)), - retry.WithBaseRule(&sharedrules.Rule242447{ - Logger: r.Logger().With("rule_id", sharedrules.ID242447), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242447, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242448)), - retry.WithBaseRule(&sharedrules.Rule242448{ - Logger: r.Logger().With("rule_id", sharedrules.ID242448), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242448, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242449)), - retry.WithBaseRule(&sharedrules.Rule242449{ - Logger: r.Logger().With("rule_id", sharedrules.ID242449), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242449, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242450)), - retry.WithBaseRule(&sharedrules.Rule242450{ - Logger: r.Logger().With("rule_id", sharedrules.ID242450), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242450, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242451)), - retry.WithBaseRule(&rules.Rule242451{ - Logger: r.Logger().With("rule_id", sharedrules.ID242451), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242451, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242452)), - retry.WithBaseRule(&sharedrules.Rule242452{ - Logger: r.Logger().With("rule_id", sharedrules.ID242452), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242452, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242453)), - retry.WithBaseRule(&sharedrules.Rule242453{ - Logger: r.Logger().With("rule_id", sharedrules.ID242453), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242453, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - rule.NewSkipRule( - sharedrules.ID242454, - "The Kubernetes kubeadm.conf must be owned by root.", - `Gardener does not use kubeadm and also does not store any "main config" anywhere (flow/component logic built-in/in-code).`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242455, - "The Kubernetes kubeadm.conf must have file permissions set to 644 or more restrictive.", - `Gardener does not use kubeadm and also does not store any "main config" anywhere (flow/component logic built-in/in-code).`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242456, - "The Kubernetes kubelet config must have file permissions set to 644 or more restrictive.", - "Duplicate of 242452.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242457, - "The Kubernetes kubelet config must be owned by root.", - "Duplicate of 242453.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242459, - "The Kubernetes etcd must have file permissions set to 644 or more restrictive.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242460, - "The Kubernetes admin kubeconfig must have file permissions set to 644 or more restrictive.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242461, - "Kubernetes API Server audit logs must be enabled.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242462, - "The Kubernetes API Server must be set to audit log max size.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242463, - "The Kubernetes API Server must be set to audit log maximum backup.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242464, - "The Kubernetes API Server audit log retention must be set.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242465, - "The Kubernetes API Server audit log path must be set.", - "Duplicate of 242402. "+noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242466)), - retry.WithBaseRule(&rules.Rule242466{ - Logger: r.Logger().With("rule_id", sharedrules.ID242466), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242466, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242467)), - retry.WithBaseRule(&rules.Rule242467{ - Logger: r.Logger().With("rule_id", sharedrules.ID242467), - InstanceID: r.instanceID, - Client: client, - PodContext: podContext, - Options: opts242467, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - &sharedrules.Rule245541{ - Client: client, - V1RESTClient: clientSet.CoreV1().RESTClient(), - }, - rule.NewSkipRule( - sharedrules.ID245542, - "Kubernetes API Server must disable basic authentication to protect information in transit.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID245543, - "Kubernetes API Server must disable token authentication to protect information in transit.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID245544, - "Kubernetes endpoints must use approved organizational certificate and key pair to protect information in transit.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID254800, - "Kubernetes must have a Pod Security Admission control file configured.", - noControlPlaneMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - // featureGates.PodSecurity made GA in v1.25 and removed in v1.28. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ - sharedrules.ID254801, - "Kubernetes must enable PodSecurity admission controller on static pods and Kubelets.", - "Option featureGates.PodSecurity was made GA in v1.25 and removed in v1.28.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - } - - for i, r := range rules { - var severityLevel rule.SeverityLevel - if severity, ok := r.(rule.Severity); !ok { - return fmt.Errorf("rule %s does not implement rule.Severity", r.ID()) - } else { - severityLevel = severity.Severity() - } - - opt, found := ruleOptions[r.ID()] - if found && opt.Skip != nil && opt.Skip.Enabled { - rules[i] = rule.NewSkipRule(r.ID(), r.Name(), opt.Skip.Justification, rule.Accepted, rule.SkipRuleWithSeverity(severityLevel)) - } - } - - // check that the registered rules equal - // the number of rules in that ruleset version - if len(rules) != 91 { - return fmt.Errorf("revision expects 91 registered rules, but got: %d", len(rules)) - } - - return r.AddRules(rules...) -} - -func parseV2R3Options[O rules.RuleOption](options any) (*O, error) { - optionsByte, err := json.Marshal(options) - if err != nil { - return nil, err - } - - var parsedOptions O - if err := json.Unmarshal(optionsByte, &parsedOptions); err != nil { - return nil, err - } - - return &parsedOptions, nil -} - -func getV2R3OptionOrNil[O rules.RuleOption](options any) (*O, error) { - if options == nil { - return nil, nil - } - return parseV2R3Options[O](options) -} diff --git a/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go b/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go index 6ac7946fc..3832acd33 100644 --- a/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go @@ -123,13 +123,6 @@ func FromGenericConfig(rulesetConfig config.RulesetConfig, additionalOpsPodLabel } switch rulesetConfig.Version { - case "v2r3": - if err := ruleset.validateV2R3RuleOptions(indexedRuleOptions, fldPath.Child("ruleOptions")); err != nil { - return nil, err - } - if err := ruleset.registerV2R3Rules(ruleOptions); err != nil { - return nil, err - } case "v2r4": if err := ruleset.validateV2R4RuleOptions(indexedRuleOptions, fldPath.Child("ruleOptions")); err != nil { return nil, err diff --git a/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r3_ruleset.go b/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r3_ruleset.go deleted file mode 100644 index d5fab8fe1..000000000 --- a/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r3_ruleset.go +++ /dev/null @@ -1,804 +0,0 @@ -// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors -// -// SPDX-License-Identifier: Apache-2.0 - -package disak8sstig - -import ( - "encoding/json" - "fmt" - - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/validation/field" - "sigs.k8s.io/controller-runtime/pkg/client" - - "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/provider/virtualgarden/ruleset/disak8sstig/rules" - "github.com/gardener/diki/pkg/rule" - "github.com/gardener/diki/pkg/rule/retry" - "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" - sharedrules "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/rules" -) - -func validateV2R3Options[O rules.RuleOption](options any, fldPath *field.Path) field.ErrorList { - parsedOptions, err := getV2R3OptionOrNil[O](options) - if err != nil { - return field.ErrorList{ - field.InternalError(fldPath, err), - } - } - - if parsedOptions == nil { - return nil - } - - if val, ok := any(parsedOptions).(option.Option); ok { - return val.Validate(fldPath) - } - - return nil -} - -func (r *Ruleset) validateV2R3RuleOptions(ruleOptions map[string]internalconfig.IndexedRuleOptionsConfig, fldPath *field.Path) error { - allErrs := field.ErrorList{} - - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options242390](ruleOptions[sharedrules.ID242390].Args, fldPath.Index(ruleOptions[sharedrules.ID242390].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.Options242442](ruleOptions[sharedrules.ID242442].Args, fldPath.Index(ruleOptions[sharedrules.ID242442].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242445].Args, fldPath.Index(ruleOptions[sharedrules.ID242445].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242446].Args, fldPath.Index(ruleOptions[sharedrules.ID242446].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242451].Args, fldPath.Index(ruleOptions[sharedrules.ID242451].Index).Child("args"))...) - allErrs = append(allErrs, validateV2R3Options[sharedrules.Options245543](ruleOptions[sharedrules.ID245543].Args, fldPath.Index(ruleOptions[sharedrules.ID245543].Index).Child("args"))...) - - return allErrs.ToAggregate() -} - -func (r *Ruleset) registerV2R3Rules(ruleOptions map[string]config.RuleOptionsConfig) error { // TODO: add to FromGenericConfig - runtimeClient, err := client.New(r.RuntimeConfig, client.Options{}) - if err != nil { - return err - } - - runtimePodContext, err := pod.NewSimplePodContext(runtimeClient, r.RuntimeConfig, r.AdditionalOpsPodLabels) - if err != nil { - return err - } - opts242390, err := getV2R3OptionOrNil[sharedrules.Options242390](ruleOptions[sharedrules.ID242390].Args) - if err != nil { - return fmt.Errorf("rule option 242390 error: %s", err.Error()) - } - opts242442, err := getV2R3OptionOrNil[disaoption.Options242442](ruleOptions[sharedrules.ID242442].Args) - if err != nil { - return fmt.Errorf("rule option 242442 error: %s", err.Error()) - } - opts242445, err := getV2R3OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242445].Args) - if err != nil { - return fmt.Errorf("rule option 242445 error: %s", err.Error()) - } - opts242446, err := getV2R3OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242446].Args) - if err != nil { - return fmt.Errorf("rule option 242446 error: %s", err.Error()) - } - opts242451, err := getV2R3OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242451].Args) - if err != nil { - return fmt.Errorf("rule option 242451 error: %s", err.Error()) - } - opts245543, err := getV2R3OptionOrNil[sharedrules.Options245543](ruleOptions[sharedrules.ID245543].Args) - if err != nil { - return fmt.Errorf("rule option 245543 error: %s", err.Error()) - } - - rcFileChecks := retry.RetryConditionFromRegex( - *retryerrors.ContainerNotFoundOnNodeRegexp, - *retryerrors.ContainerFileNotFoundOnNodeRegexp, - *retryerrors.ContainerNotReadyRegexp, - *retryerrors.OpsPodNotFoundRegexp, - *retryerrors.ObjectNotFoundRegexp, - ) - - const ( - ns = "garden" - etcdMain = "virtual-garden-etcd-main" - etcdEvents = "virtual-garden-etcd-events" - kcmDeploymentName = "virtual-garden-kube-controller-manager" - kcmContainerName = "kube-controller-manager" - apiserverDeploymentName = "virtual-garden-kube-apiserver" - apiserverContainerName = "kube-apiserver" - noKubeletsMsg = "The Virtual Garden cluster does not have any nodes therefore there are no kubelets to check." - noPodsMsg = "The Virtual Garden cluster does not have any nodes therefore there cluster does not have any pods." - ) - rules := []rule.Rule{ - &sharedrules.Rule242376{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: kcmDeploymentName, - ContainerName: kcmContainerName, - }, - rule.NewSkipRule( - sharedrules.ID242377, - "The Kubernetes Scheduler must use TLS 1.2, at a minimum, to protect the confidentiality of sensitive data during electronic dissemination.", - "The Virtual Garden cluster does not make use of a Kubernetes Scheduler.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242378{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242379{ - Client: runtimeClient, - Namespace: ns, - StatefulSetETCDMain: etcdMain, - StatefulSetETCDEvents: etcdEvents, - }, - &sharedrules.Rule242380{ - Client: runtimeClient, - Namespace: ns, - StatefulSetETCDMain: etcdMain, - StatefulSetETCDEvents: etcdEvents, - }, - &sharedrules.Rule242381{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: kcmDeploymentName, - ContainerName: kcmContainerName, - }, - &sharedrules.Rule242382{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - ExpectedStartModes: []string{"RBAC", "Webhook"}, - }, - rule.NewSkipRule( - sharedrules.ID242383, - "User-managed resources must be created in dedicated namespaces.", - "By design the Garden cluster provides separate namespaces for user projects and users do not have access to system namespaces.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID242384, - "The Kubernetes Scheduler must have secure binding.", - "The Virtual Garden cluster does not make use of a Kubernetes Scheduler.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242385, - "The Kubernetes Controller Manager must have secure binding.", - "The Kubernetes Controller Manager runs in a container which already has limited access to network interfaces. In addition ingress traffic to the Kubernetes Controller Manager is restricted via network policies, making an unintended exposure less likely.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242386{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - rule.NewSkipRule( - sharedrules.ID242387, - "The Kubernetes Kubelet must have the read-only port flag disabled.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - &sharedrules.Rule242388{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242389{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242390{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - Options: opts242390, - }, - rule.NewSkipRule( - sharedrules.ID242391, - "The Kubernetes Kubelet must have anonymous authentication disabled.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID242392, - "The Kubernetes kubelet must enable explicit authorization.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID242393, - "Kubernetes Worker Nodes must not have sshd service running.", - "The Virtual Garden cluster does not have any nodes.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242394, - "Kubernetes Worker Nodes must not have the sshd service enabled.", - "The Virtual Garden cluster does not have any nodes.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242395, - "Kubernetes dashboard must not be enabled.", - "The Virtual Garden cluster does not have any nodes therefore it does not deploy a Kubernetes dashboard.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242396, - "Kubernetes Kubectl cp command must give expected access and results.", - "The Virtual Garden cluster does not have any nodes therefore it does not install kubectl.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242397, - "Kubernetes kubelet static PodPath must not enable static pods.", - "The Virtual Garden cluster does not have any nodes therefore there are no kubelets to check.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - // feature-gates.DynamicAuditing removed in v1.19. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ - sharedrules.ID242398, - "Kubernetes DynamicAuditing must not be enabled.", - "Option feature-gates.DynamicAuditing was removed in Kubernetes v1.19.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242399, - "Kubernetes DynamicKubeletConfig must not be enabled.", - // feature-gates.DynamicKubeletConfig removed in v1.26. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ - "Option feature-gates.DynamicKubeletConfig removed in Kubernetes v1.26.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &rules.Rule242400{ - Client: runtimeClient, - Namespace: ns, - }, - &sharedrules.Rule242402{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242403{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - }, - rule.NewSkipRule( - sharedrules.ID242404, - "Kubernetes Kubelet must deny hostname override.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242405, - "Kubernetes manifests must be owned by root.", - "Gardener does not deploy any control plane component as systemd processes or static pod.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242406, - "Kubernetes kubelet configuration file must be owned by root.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242407, - "The Kubernetes KubeletConfiguration files must have file permissions set to 644 or more restrictive.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242408, - "The Kubernetes manifest files must have least privileges.", - "Gardener does not deploy any control plane component as systemd processes or static pod.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242409{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: kcmDeploymentName, - ContainerName: kcmContainerName, - }, - rule.NewSkipRule( - sharedrules.ID242410, - "The Kubernetes API Server must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242411, - "The Kubernetes Scheduler must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - "The Virtual Garden cluster does not make use of a Kubernetes Scheduler.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242412, - "The Kubernetes Controllers must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242413, - "The Kubernetes etcd must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", - "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242414, - "The Kubernetes cluster must use non-privileged host ports for user pods.", - noPodsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242415, - "Secrets in Kubernetes must not be stored as environment variables.", - noPodsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID242417, - "Kubernetes must separate user functionality.", - noPodsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242418{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242419{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - rule.NewSkipRule( - sharedrules.ID242420, - "Kubernetes Kubelet must have the SSL Certificate Authority set.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242421{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: kcmDeploymentName, - ContainerName: kcmContainerName, - }, - &sharedrules.Rule242422{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242423{ - Client: runtimeClient, - Namespace: ns, - StatefulSetETCDMain: etcdMain, - StatefulSetETCDEvents: etcdEvents, - }, - rule.NewSkipRule( - sharedrules.ID242424, - "Kubernetes Kubelet must enable tlsPrivateKeyFile for client authentication to secure service.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242425, - "Kubernetes Kubelet must enable tlsCertFile for client authentication to secure service.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule242426{ - Client: runtimeClient, - Namespace: ns, - StatefulSetETCDMain: etcdMain, - StatefulSetETCDEvents: etcdEvents, - }, - &sharedrules.Rule242427{ - Client: runtimeClient, - Namespace: ns, - StatefulSetETCDMain: etcdMain, - StatefulSetETCDEvents: etcdEvents, - }, - &sharedrules.Rule242428{ - Client: runtimeClient, - Namespace: ns, - StatefulSetETCDMain: etcdMain, - StatefulSetETCDEvents: etcdEvents, - }, - &sharedrules.Rule242429{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242430{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242431{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242432{ - Client: runtimeClient, - Namespace: ns, - StatefulSetETCDMain: etcdMain, - StatefulSetETCDEvents: etcdEvents, - }, - &sharedrules.Rule242433{ - Client: runtimeClient, - Namespace: ns, - StatefulSetETCDMain: etcdMain, - StatefulSetETCDEvents: etcdEvents, - }, - rule.NewSkipRule( - sharedrules.ID242434, - "Kubernetes Kubelet must enable kernel protection.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - &sharedrules.Rule242436{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - rule.NewSkipRule( - sharedrules.ID242437, - "Kubernetes must have a pod security policy set.", - "PSPs are removed in K8s version 1.25.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - &sharedrules.Rule242438{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &rules.Rule242442{ - Client: runtimeClient, - Namespace: ns, - Options: opts242442, - }, - rule.NewSkipRule( - sharedrules.ID242443, - "Kubernetes must contain the latest updates as authorized by IAVMs, CTOs, DTMs, and STIGs.", - "Scanning/patching security vulnerabilities should be enforced organizationally. Security vulnerability scanning should be automated and maintainers should be informed automatically.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242444, - "Kubernetes component manifests must be owned by root.", - "Rule is duplicate of 242405. Gardener does not deploy any control plane component as systemd processes or static pod.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242445)), - retry.WithBaseRule(&sharedrules.Rule242445{ - Logger: r.Logger().With("rule_id", sharedrules.ID242445), - InstanceID: r.instanceID, - Client: runtimeClient, - Namespace: ns, - PodContext: runtimePodContext, - ETCDMainSelector: labels.SelectorFromSet(labels.Set{"app.kubernetes.io/part-of": etcdMain}), - ETCDEventsSelector: labels.SelectorFromSet(labels.Set{"app.kubernetes.io/part-of": etcdEvents}), - Options: opts242445, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242446)), - retry.WithBaseRule(&sharedrules.Rule242446{ - Logger: r.Logger().With("rule_id", sharedrules.ID242446), - InstanceID: r.instanceID, - Client: runtimeClient, - Namespace: ns, - PodContext: runtimePodContext, - DeploymentNames: []string{apiserverDeploymentName, kcmDeploymentName}, - Options: opts242446, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - rule.NewSkipRule( - sharedrules.ID242447, - "The Kubernetes Kube Proxy kubeconfig must have file permissions set to 644 or more restrictive.", - noPodsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242448, - "The Kubernetes Kube Proxy kubeconfig must be owned by root.", - noPodsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242449, - "The Kubernetes Kubelet certificate authority file must have file permissions set to 644 or more restrictive.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242450, - "The Kubernetes Kubelet certificate authority must be owned by root.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242451)), - retry.WithBaseRule(&rules.Rule242451{ - Logger: r.Logger().With("rule_id", sharedrules.ID242451), - InstanceID: r.instanceID, - Client: runtimeClient, - Namespace: ns, - PodContext: runtimePodContext, - Options: opts242451, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - rule.NewSkipRule( - sharedrules.ID242452, - "The Kubernetes kubelet KubeConfig must have file permissions set to 644 or more restrictive.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242453, - "The Kubernetes kubelet KubeConfig file must be owned by root.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242454, - "The Kubernetes kubeadm.conf must be owned by root.", - `Gardener does not use kubeadm and also does not store any "main config" anywhere (flow/component logic built-in/in-code).`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242455, - "The Kubernetes kubeadm.conf must have file permissions set to 644 or more restrictive.", - `Gardener does not use kubeadm and also does not store any "main config" anywhere (flow/component logic built-in/in-code).`, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242456, - "The Kubernetes kubelet config must have file permissions set to 644 or more restrictive.", - "Duplicate of 242452. "+noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - rule.NewSkipRule( - sharedrules.ID242457, - "The Kubernetes kubelet config must be owned by root.", - "Duplicate of 242453. "+noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242459)), - retry.WithBaseRule(&sharedrules.Rule242459{ - Logger: r.Logger().With("rule_id", sharedrules.ID242459), - InstanceID: r.instanceID, - Client: runtimeClient, - Namespace: ns, - PodContext: runtimePodContext, - ETCDMainSelector: labels.SelectorFromSet(labels.Set{"app.kubernetes.io/part-of": etcdMain}), - ETCDEventsSelector: labels.SelectorFromSet(labels.Set{"app.kubernetes.io/part-of": etcdEvents}), - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242460)), - retry.WithBaseRule(&sharedrules.Rule242460{ - Logger: r.Logger().With("rule_id", sharedrules.ID242460), - InstanceID: r.instanceID, - Client: runtimeClient, - Namespace: ns, - PodContext: runtimePodContext, - DeploymentNames: []string{apiserverDeploymentName, kcmDeploymentName}, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - &sharedrules.Rule242461{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242462{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242463{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule242464{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - rule.NewSkipRule( - sharedrules.ID242465, - "The Kubernetes API Server audit log path must be set.", - "Rule is duplicate of 242402.", - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242466)), - retry.WithBaseRule(&rules.Rule242466{ - Logger: r.Logger().With("rule_id", sharedrules.ID242466), - InstanceID: r.instanceID, - Client: runtimeClient, - Namespace: ns, - PodContext: runtimePodContext, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - retry.New( - retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242467)), - retry.WithBaseRule(&rules.Rule242467{ - Logger: r.Logger().With("rule_id", sharedrules.ID242467), - InstanceID: r.instanceID, - Client: runtimeClient, - Namespace: ns, - PodContext: runtimePodContext, - }), - retry.WithRetryCondition(rcFileChecks), - retry.WithMaxRetries(*r.args.MaxRetries), - ), - rule.NewSkipRule( - sharedrules.ID245541, - "Kubernetes Kubelet must not disable timeouts.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityMedium), - ), - &sharedrules.Rule245542{ - Client: runtimeClient, - Namespace: ns, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - &sharedrules.Rule245543{ - Client: runtimeClient, - Namespace: ns, - Options: opts245543, - DeploymentName: apiserverDeploymentName, - ContainerName: apiserverContainerName, - }, - rule.NewSkipRule( - sharedrules.ID245544, - "Kubernetes endpoints must use approved organizational certificate and key pair to protect information in transit.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID254800, - "Kubernetes must have a Pod Security Admission control file configured.", - noPodsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - rule.NewSkipRule( - sharedrules.ID254801, - "Kubernetes must enable PodSecurity admission controller on static pods and Kubelets.", - noKubeletsMsg, - rule.Skipped, - rule.SkipRuleWithSeverity(rule.SeverityHigh), - ), - } - - for i, r := range rules { - var severityLevel rule.SeverityLevel - if severity, ok := r.(rule.Severity); !ok { - return fmt.Errorf("rule %s does not implement rule.Severity", r.ID()) - } else { - severityLevel = severity.Severity() - } - - opt, found := ruleOptions[r.ID()] - if found && opt.Skip != nil && opt.Skip.Enabled { - rules[i] = rule.NewSkipRule(r.ID(), r.Name(), opt.Skip.Justification, rule.Accepted, rule.SkipRuleWithSeverity(severityLevel)) - } - } - - // check that the registered rules equal - // the number of rules in that ruleset version - if len(rules) != 91 { - return fmt.Errorf("revision expects 91 registered rules, but got: %d", len(rules)) - } - - return r.AddRules(rules...) -} - -func parseV2R3Options[O rules.RuleOption](options any) (*O, error) { - optionsByte, err := json.Marshal(options) - if err != nil { - return nil, err - } - - var parsedOptions O - if err := json.Unmarshal(optionsByte, &parsedOptions); err != nil { - return nil, err - } - - return &parsedOptions, nil -} - -func getV2R3OptionOrNil[O rules.RuleOption](options any) (*O, error) { - if options == nil { - return nil, nil - } - return parseV2R3Options[O](options) -} From 228400485f2e0774ac8cbc28de15931cc4fa6eff Mon Sep 17 00:00:00 2001 From: georgibaltiev Date: Tue, 3 Feb 2026 17:19:14 +0200 Subject: [PATCH 3/4] add: support for version v2r5 of the DISA STIG ruleset --- .../gardener/ruleset/disak8sstig/ruleset.go | 7 + .../ruleset/disak8sstig/v2r5_ruleset.go | 741 ++++++++++++++ .../managedk8s/ruleset/disak8sstig/ruleset.go | 7 + .../ruleset/disak8sstig/v2r5_ruleset.go | 948 ++++++++++++++++++ .../ruleset/disak8sstig/ruleset.go | 7 + .../ruleset/disak8sstig/v2r5_ruleset.go | 812 +++++++++++++++ 6 files changed, 2522 insertions(+) create mode 100644 pkg/provider/gardener/ruleset/disak8sstig/v2r5_ruleset.go create mode 100644 pkg/provider/managedk8s/ruleset/disak8sstig/v2r5_ruleset.go create mode 100644 pkg/provider/virtualgarden/ruleset/disak8sstig/v2r5_ruleset.go diff --git a/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go b/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go index 178265e5e..61da331a0 100644 --- a/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/gardener/ruleset/disak8sstig/ruleset.go @@ -135,6 +135,13 @@ func FromGenericConfig(rulesetConfig config.RulesetConfig, additionalOpsPodLabel if err := ruleset.registerV2R4Rules(ruleOptions); err != nil { return nil, err } + case "v2r5": + if err := ruleset.validateV2R5RuleOptions(indexedRuleOptions, fldPath.Child("ruleOptions")); err != nil { + return nil, err + } + if err := ruleset.registerV2R5Rules(ruleOptions); err != nil { + return nil, err + } default: return nil, fmt.Errorf("unknown ruleset %s version: %s - use 'diki show provider gardener' to see the provider's supported rulesets", rulesetConfig.ID, rulesetConfig.Version) } diff --git a/pkg/provider/gardener/ruleset/disak8sstig/v2r5_ruleset.go b/pkg/provider/gardener/ruleset/disak8sstig/v2r5_ruleset.go new file mode 100644 index 000000000..14bb67354 --- /dev/null +++ b/pkg/provider/gardener/ruleset/disak8sstig/v2r5_ruleset.go @@ -0,0 +1,741 @@ +// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package disak8sstig + +import ( + "encoding/json" + "fmt" + + resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" + kubernetesgardener "github.com/gardener/gardener/pkg/client/kubernetes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + + "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/provider/gardener/ruleset/disak8sstig/rules" + "github.com/gardener/diki/pkg/rule" + "github.com/gardener/diki/pkg/rule/retry" + "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" + sharedrules "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/rules" +) + +func validateV2R5Options[O rules.RuleOption](options any, fldPath *field.Path) field.ErrorList { + parsedOptions, err := getV2R5OptionOrNil[O](options) + if err != nil { + return field.ErrorList{ + field.InternalError(fldPath, err), + } + } + + if parsedOptions == nil { + return nil + } + + if val, ok := any(parsedOptions).(option.Option); ok { + return val.Validate(fldPath) + } + + return nil +} + +func parseV2R5Options[O rules.RuleOption](options any) (*O, error) { + optionsByte, err := json.Marshal(options) + if err != nil { + return nil, err + } + + var parsedOptions O + if err := json.Unmarshal(optionsByte, &parsedOptions); err != nil { + return nil, err + } + + return &parsedOptions, nil +} + +func getV2R5OptionOrNil[O rules.RuleOption](options any) (*O, error) { + if options == nil { + return nil, nil + } + return parseV2R5Options[O](options) +} + +func (r *Ruleset) validateV2R5RuleOptions(ruleOptions map[string]internalconfig.IndexedRuleOptionsConfig, fldPath *field.Path) error { + allErrs := field.ErrorList{} + + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242390](ruleOptions[sharedrules.ID242390].Args, fldPath.Index(ruleOptions[sharedrules.ID242390].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[rules.Options242400](ruleOptions[sharedrules.ID242400].Args, fldPath.Index(ruleOptions[sharedrules.ID242400].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.Options242414](ruleOptions[sharedrules.ID242414].Args, fldPath.Index(ruleOptions[sharedrules.ID242414].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.Options242415](ruleOptions[sharedrules.ID242415].Args, fldPath.Index(ruleOptions[sharedrules.ID242415].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.Options242442](ruleOptions[sharedrules.ID242442].Args, fldPath.Index(ruleOptions[sharedrules.ID242442].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242445].Args, fldPath.Index(ruleOptions[sharedrules.ID242445].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242446].Args, fldPath.Index(ruleOptions[sharedrules.ID242446].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[rules.Options242451](ruleOptions[sharedrules.ID242451].Args, fldPath.Index(ruleOptions[sharedrules.ID242451].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[rules.Options242466](ruleOptions[sharedrules.ID242466].Args, fldPath.Index(ruleOptions[sharedrules.ID242466].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[rules.Options242467](ruleOptions[sharedrules.ID242467].Args, fldPath.Index(ruleOptions[sharedrules.ID242467].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options245543](ruleOptions[sharedrules.ID245543].Args, fldPath.Index(ruleOptions[sharedrules.ID245543].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options254800](ruleOptions[sharedrules.ID254800].Args, fldPath.Index(ruleOptions[sharedrules.ID254800].Index).Child("args"))...) + + return allErrs.ToAggregate() +} + +func (r *Ruleset) registerV2R5Rules(ruleOptions map[string]config.RuleOptionsConfig) error { // TODO: add to FromGenericConfig + shootClient, err := client.New(r.ShootConfig, client.Options{Scheme: kubernetesgardener.ShootScheme}) + if err != nil { + return err + } + + seedClient, err := client.New(r.SeedConfig, client.Options{Scheme: kubernetesgardener.SeedScheme}) + if err != nil { + return err + } + + shootPodContext, err := pod.NewSimplePodContext(shootClient, r.ShootConfig, r.AdditionalOpsPodLabels) + if err != nil { + return err + } + + seedPodContext, err := pod.NewSimplePodContext(seedClient, r.SeedConfig, r.AdditionalOpsPodLabels) + if err != nil { + return err + } + + shootClientSet, err := kubernetes.NewForConfig(r.ShootConfig) + if err != nil { + return err + } + + opts242390, err := getV2R5OptionOrNil[sharedrules.Options242390](ruleOptions[sharedrules.ID242390].Args) + if err != nil { + return fmt.Errorf("rule option 242390 error: %s", err.Error()) + } + opts242400, err := getV2R5OptionOrNil[rules.Options242400](ruleOptions[sharedrules.ID242400].Args) + if err != nil { + return fmt.Errorf("rule option 242400 error: %s", err.Error()) + } + opts242414, err := getV2R5OptionOrNil[disaoption.Options242414](ruleOptions[sharedrules.ID242414].Args) + if err != nil { + return fmt.Errorf("rule option 242414 error: %s", err.Error()) + } + opts242415, err := getV2R5OptionOrNil[disaoption.Options242415](ruleOptions[sharedrules.ID242415].Args) + if err != nil { + return fmt.Errorf("rule option 242415 error: %s", err.Error()) + } + opts242442, err := getV2R5OptionOrNil[disaoption.Options242442](ruleOptions[sharedrules.ID242442].Args) + if err != nil { + return fmt.Errorf("rule option 242442 error: %s", err.Error()) + } + opts242445, err := getV2R5OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242445].Args) + if err != nil { + return fmt.Errorf("rule option 242445 error: %s", err.Error()) + } + opts242446, err := getV2R5OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242446].Args) + if err != nil { + return fmt.Errorf("rule option 242446 error: %s", err.Error()) + } + opts242451, err := getV2R5OptionOrNil[rules.Options242451](ruleOptions[sharedrules.ID242451].Args) + if err != nil { + return fmt.Errorf("rule option 242451 error: %s", err.Error()) + } + opts242466, err := getV2R5OptionOrNil[rules.Options242466](ruleOptions[sharedrules.ID242466].Args) + if err != nil { + return fmt.Errorf("rule option 242466 error: %s", err.Error()) + } + opts242467, err := getV2R5OptionOrNil[rules.Options242467](ruleOptions[sharedrules.ID242467].Args) + if err != nil { + return fmt.Errorf("rule option 242467 error: %s", err.Error()) + } + opts245543, err := getV2R5OptionOrNil[sharedrules.Options245543](ruleOptions[sharedrules.ID245543].Args) + if err != nil { + return fmt.Errorf("rule option 245543 error: %s", err.Error()) + } + opts254800, err := getV2R5OptionOrNil[sharedrules.Options254800](ruleOptions[sharedrules.ID254800].Args) + if err != nil { + return fmt.Errorf("rule option 254800 error: %s", err.Error()) + } + + rcOpsPod := retry.RetryConditionFromRegex( + *retryerrors.OpsPodNotFoundRegexp, + ) + rcFileChecks := retry.RetryConditionFromRegex( + *retryerrors.ContainerNotFoundOnNodeRegexp, + *retryerrors.ContainerFileNotFoundOnNodeRegexp, + *retryerrors.ContainerNotReadyRegexp, + *retryerrors.OpsPodNotFoundRegexp, + *retryerrors.ObjectNotFoundRegexp, + ) + + // Gardener images use distroless nonroot user with ID 65532 + // https://github.com/GoogleContainerTools/distroless/blob/main/base/base.bzl#L8 + gardenerFileOwnerOptions := &disaoption.FileOwnerOptions{ + ExpectedFileOwner: disaoption.ExpectedOwner{ + Users: []string{"0", "65532"}, + Groups: []string{"0", "65532"}, + }, + } + workerPoolGroupByLabels := []string{"worker.gardener.cloud/pool"} + + rules := []rule.Rule{ + &sharedrules.Rule242376{Client: seedClient, Namespace: r.shootNamespace}, + &rules.Rule242377{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242378{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242379{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242380{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242381{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242382{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242383{Client: shootClient}, + rule.NewSkipRule( + sharedrules.ID242384, + "The Kubernetes Scheduler must have secure binding.", + "The Kubernetes Scheduler runs in a container which already has limited access to network interfaces. In addition ingress traffic to the Kubernetes Scheduler is restricted via network policies, making an unintended exposure less likely.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242385, + "The Kubernetes Controller Manager must have secure binding.", + "The Kubernetes Controller Manager runs in a container which already has limited access to network interfaces. In addition ingress traffic to the Kubernetes Controller Manager is restricted via network policies, making an unintended exposure less likely.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242387{ + Client: shootClient, + V1RESTClient: shootClientSet.CoreV1().RESTClient(), + }, + &sharedrules.Rule242389{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242390{Client: seedClient, Namespace: r.shootNamespace, Options: opts242390}, + &sharedrules.Rule242391{ + Client: shootClient, + V1RESTClient: shootClientSet.CoreV1().RESTClient(), + }, + &sharedrules.Rule242392{ + Client: shootClient, + V1RESTClient: shootClientSet.CoreV1().RESTClient(), + }, + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242393)), + retry.WithBaseRule(&sharedrules.Rule242393{ + Logger: r.Logger().With("rule_id", sharedrules.ID242393), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242393{ + NodeGroupByLabels: workerPoolGroupByLabels, + }, + }), + retry.WithRetryCondition(rcOpsPod), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242394)), + retry.WithBaseRule(&sharedrules.Rule242394{ + Logger: r.Logger().With("rule_id", sharedrules.ID242394), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242394{ + NodeGroupByLabels: workerPoolGroupByLabels, + }, + }), + retry.WithRetryCondition(rcOpsPod), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + &sharedrules.Rule242395{Client: shootClient}, + rule.NewSkipRule( + sharedrules.ID242396, + "Kubernetes Kubectl cp command must give expected access and results.", + `"kubectl" is not installed into control plane pods or worker nodes and Gardener does not offer Kubernetes v1.12 or older.`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242397{ + Client: shootClient, + V1RESTClient: shootClientSet.CoreV1().RESTClient(), + }, + rule.NewSkipRule( + sharedrules.ID242398, + "Kubernetes DynamicAuditing must not be enabled.", + // feature-gates.DynamicAuditing removed in v1.19. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ + "Option feature-gates.DynamicAuditing removed in Kubernetes v1.19.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242399, + "Kubernetes DynamicKubeletConfig must not be enabled.", + // feature-gates.DynamicKubeletConfig removed in v1.26. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ + "Option feature-gates.DynamicKubeletConfig removed in Kubernetes v1.26.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242400)), + retry.WithBaseRule(&rules.Rule242400{ + Logger: r.Logger().With("rule_id", sharedrules.ID242400), + InstanceID: r.instanceID, + ControlPlaneClient: seedClient, + ClusterClient: shootClient, + ClusterPodContext: shootPodContext, + ClusterV1RESTClient: shootClientSet.CoreV1().RESTClient(), + ControlPlaneNamespace: r.shootNamespace, + Options: opts242400, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + &sharedrules.Rule242402{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242403{Client: seedClient, Namespace: r.shootNamespace}, + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242404)), + retry.WithBaseRule(&sharedrules.Rule242404{ + Logger: r.Logger().With("rule_id", sharedrules.ID242404), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242404{ + NodeGroupByLabels: workerPoolGroupByLabels, + }, + }), + retry.WithRetryCondition(rcOpsPod), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + rule.NewSkipRule( + sharedrules.ID242405, + "Kubernetes manifests must be owned by root.", + "Gardener does not deploy any control plane component as systemd processes or static pod.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242406)), + retry.WithBaseRule(&sharedrules.Rule242406{ + Logger: r.Logger().With("rule_id", sharedrules.ID242406), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242406{ + NodeGroupByLabels: workerPoolGroupByLabels, + FileOwnerOptions: gardenerFileOwnerOptions, + }, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242407)), + retry.WithBaseRule(&sharedrules.Rule242407{ + Logger: r.Logger().With("rule_id", sharedrules.ID242407), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242407{ + NodeGroupByLabels: workerPoolGroupByLabels, + }, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + rule.NewSkipRule( + sharedrules.ID242408, + "The Kubernetes manifest files must have least privileges.", + `Gardener does not deploy any control plane component as systemd processes or static pod.`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242409{Client: seedClient, Namespace: r.shootNamespace}, + rule.NewSkipRule( + sharedrules.ID242410, + "The Kubernetes API Server must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242411, + "The Kubernetes Scheduler must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242412, + "The Kubernetes Controllers must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242413, + "The Kubernetes etcd must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &rules.Rule242414{ + ClusterClient: shootClient, + ControlPlaneClient: seedClient, + ControlPlaneNamespace: r.shootNamespace, + Options: opts242414, + }, + &rules.Rule242415{ + ClusterClient: shootClient, + ControlPlaneClient: seedClient, + ControlPlaneNamespace: r.shootNamespace, + Options: opts242415, + }, + &sharedrules.Rule242417{ + Client: shootClient, + Options: &sharedrules.Options242417{ + AcceptedPods: []sharedrules.AcceptedPods242417{ + { + AcceptedNamespacedObject: option.AcceptedNamespacedObject{ + NamespacedObjectSelector: option.NamespacedObjectSelector{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{resourcesv1alpha1.ManagedBy: "gardener"}, + }, + NamespaceLabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"kubernetes.io/metadata.name": "kube-system"}, + }, + }, + Justification: "Gardener managed pods are not user pods", + }, + Status: "Passed", + }, + }, + }, + }, + &sharedrules.Rule242418{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242419{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242420{ + Client: shootClient, + V1RESTClient: shootClientSet.CoreV1().RESTClient(), + }, + &sharedrules.Rule242421{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242422{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242423{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242424{ + Client: shootClient, + V1RESTClient: shootClientSet.CoreV1().RESTClient(), + }, + &sharedrules.Rule242425{ + Client: shootClient, + V1RESTClient: shootClientSet.CoreV1().RESTClient(), + }, + &sharedrules.Rule242426{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242427{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242428{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242429{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242430{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242431{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242432{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242433{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242434{ + Client: shootClient, + V1RESTClient: shootClientSet.CoreV1().RESTClient(), + }, + &sharedrules.Rule242436{Client: seedClient, Namespace: r.shootNamespace}, + rule.NewSkipRule( + sharedrules.ID242437, + "Kubernetes must have a pod security policy set.", + "PSPs are removed in K8s version 1.25.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + &sharedrules.Rule242438{Client: seedClient, Namespace: r.shootNamespace}, + &rules.Rule242442{ClusterClient: shootClient, ControlPlaneClient: seedClient, ControlPlaneNamespace: r.shootNamespace, Options: opts242442}, + rule.NewSkipRule( + sharedrules.ID242443, + "Kubernetes must contain the latest updates as authorized by IAVMs, CTOs, DTMs, and STIGs.", + "Scanning/patching security vulnerabilities should be enforced organizationally. Security vulnerability scanning should be automated and maintainers should be informed automatically.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242444, + "Kubernetes component manifests must be owned by root.", + `Rule is duplicate of "242405"`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242445)), + retry.WithBaseRule(&sharedrules.Rule242445{ + Logger: r.Logger().With("rule_id", sharedrules.ID242445), + InstanceID: r.instanceID, + Client: seedClient, + PodContext: seedPodContext, + Namespace: r.shootNamespace, + Options: opts242445, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242446)), + retry.WithBaseRule(&sharedrules.Rule242446{ + Logger: r.Logger().With("rule_id", sharedrules.ID242446), + InstanceID: r.instanceID, + Client: seedClient, + PodContext: seedPodContext, + Namespace: r.shootNamespace, + Options: opts242446, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242447)), + retry.WithBaseRule(&sharedrules.Rule242447{ + Logger: r.Logger().With("rule_id", sharedrules.ID242447), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242448)), + retry.WithBaseRule(&sharedrules.Rule242448{ + Logger: r.Logger().With("rule_id", sharedrules.ID242448), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242448{ + FileOwnerOptions: gardenerFileOwnerOptions, + }, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242449)), + retry.WithBaseRule(&sharedrules.Rule242449{ + Logger: r.Logger().With("rule_id", sharedrules.ID242449), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242449{ + NodeGroupByLabels: workerPoolGroupByLabels, + }, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242450)), + retry.WithBaseRule(&sharedrules.Rule242450{ + Logger: r.Logger().With("rule_id", sharedrules.ID242450), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242450{ + NodeGroupByLabels: workerPoolGroupByLabels, + FileOwnerOptions: gardenerFileOwnerOptions, + }, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242451)), + retry.WithBaseRule(&rules.Rule242451{ + Logger: r.Logger().With("rule_id", sharedrules.ID242451), + InstanceID: r.instanceID, + ControlPlaneClient: seedClient, + ClusterClient: shootClient, + ControlPlanePodContext: seedPodContext, + ClusterPodContext: shootPodContext, + ControlPlaneNamespace: r.shootNamespace, + Options: opts242451, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242452)), + retry.WithBaseRule(&sharedrules.Rule242452{ + Logger: r.Logger().With("rule_id", sharedrules.ID242452), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242452{ + NodeGroupByLabels: workerPoolGroupByLabels, + }, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242453)), + retry.WithBaseRule(&sharedrules.Rule242453{ + Logger: r.Logger().With("rule_id", sharedrules.ID242453), + InstanceID: r.instanceID, + Client: shootClient, + PodContext: shootPodContext, + Options: &sharedrules.Options242453{ + NodeGroupByLabels: workerPoolGroupByLabels, + FileOwnerOptions: gardenerFileOwnerOptions, + }, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + rule.NewSkipRule( + sharedrules.ID242454, + "Kubernetes kubeadm.conf must be owned by root.", + `Gardener does not use "kubeadm" and also does not store any "main config" anywhere in seed or shoot (flow/component logic built-in/in-code).`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242455, + "Kubernetes kubeadm.conf must have file permissions set to 644 or more restrictive.", + `Gardener does not use "kubeadm" and also does not store any "main config" anywhere in seed or shoot (flow/component logic built-in/in-code).`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242456, + "Kubernetes kubelet config must have file permissions set to 644 or more restrictive.", + `Rule is duplicate of "242452".`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242457, + "Kubernetes kubelet config must be owned by root.", + `Rule is duplicate of "242453".`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242459)), + retry.WithBaseRule(&sharedrules.Rule242459{ + Logger: r.Logger().With("rule_id", sharedrules.ID242459), + InstanceID: r.instanceID, + Client: seedClient, + PodContext: seedPodContext, + Namespace: r.shootNamespace, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242460)), + retry.WithBaseRule(&sharedrules.Rule242460{ + Logger: r.Logger().With("rule_id", sharedrules.ID242460), + InstanceID: r.instanceID, + Client: seedClient, + PodContext: seedPodContext, + Namespace: r.shootNamespace, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + &sharedrules.Rule242461{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242462{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242463{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule242464{Client: seedClient, Namespace: r.shootNamespace}, + rule.NewSkipRule( + sharedrules.ID242465, + "Kubernetes API Server audit log path must be set.", + `Rule is duplicate of "242402"`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242466)), + retry.WithBaseRule(&rules.Rule242466{ + Logger: r.Logger().With("rule_id", sharedrules.ID242466), + InstanceID: r.instanceID, + ControlPlaneClient: seedClient, + ClusterClient: shootClient, + ControlPlanePodContext: seedPodContext, + ClusterPodContext: shootPodContext, + ControlPlaneNamespace: r.shootNamespace, + Options: opts242466, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242467)), + retry.WithBaseRule(&rules.Rule242467{ + Logger: r.Logger().With("rule_id", sharedrules.ID242467), + InstanceID: r.instanceID, + ControlPlaneClient: seedClient, + ClusterClient: shootClient, + ControlPlanePodContext: seedPodContext, + ClusterPodContext: shootPodContext, + ControlPlaneNamespace: r.shootNamespace, + Options: opts242467, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + &sharedrules.Rule245541{ + Client: shootClient, + V1RESTClient: shootClientSet.CoreV1().RESTClient(), + }, + &sharedrules.Rule245542{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule245543{Client: seedClient, Namespace: r.shootNamespace, Options: opts245543}, + &sharedrules.Rule245544{Client: seedClient, Namespace: r.shootNamespace}, + &sharedrules.Rule254800{Client: seedClient, Namespace: r.shootNamespace, Options: opts254800}, + rule.NewSkipRule( + // featureGates.PodSecurity made GA in v1.25 and removed in v1.28. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ + sharedrules.ID254801, + "Kubernetes must enable PodSecurity admission controller on static pods and Kubelets.", + "Option featureGates.PodSecurity was made GA in v1.25 and removed in v1.28.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + &sharedrules.Rule274882{ + Client: seedClient, + Namespace: r.shootNamespace, + }, + rule.NewSkipRule( + sharedrules.ID274883, + "Sensitive information must be stored using Kubernetes Secrets or an external Secret store provider.", + "Cannot be tested with confidence and should be enforced organizationally.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID274884, + "Kubernetes must limit Secret access on a need-to-know basis.", + "Cannot be tested with confidence and should be enforced organizationally.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + } + + for i, r := range rules { + var severityLevel rule.SeverityLevel + if severity, ok := r.(rule.Severity); !ok { + return fmt.Errorf("rule %s does not implement rule.Severity", r.ID()) + } else { + severityLevel = severity.Severity() + } + + opt, found := ruleOptions[r.ID()] + if found && opt.Skip != nil && opt.Skip.Enabled { + rules[i] = rule.NewSkipRule(r.ID(), r.Name(), opt.Skip.Justification, rule.Accepted, rule.SkipRuleWithSeverity(severityLevel)) + } + } + + // check that the registered rules equal + // the number of rules in that ruleset version + if len(rules) != 94 { + return fmt.Errorf("revision expects 94 registered rules, but got: %d", len(rules)) + } + + return r.AddRules(rules...) +} diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go b/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go index 6d62e7c7b..6d8d71c2d 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/ruleset.go @@ -130,6 +130,13 @@ func FromGenericConfig(rulesetConfig config.RulesetConfig, additionalOpsPodLabel if err := ruleset.registerV2R4Rules(ruleOptions); err != nil { return nil, err } + case "v2r5": + if err := ruleset.validateV2R5RuleOptions(indexedRuleOptions, fldPath.Child("ruleOptions")); err != nil { + return nil, err + } + if err := ruleset.registerV2R5Rules(ruleOptions); err != nil { + return nil, err + } default: return nil, fmt.Errorf("unknown ruleset %s version: %s - use 'diki show provider managedk8s' to see the provider's supported rulesets", rulesetConfig.ID, rulesetConfig.Version) } diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/v2r5_ruleset.go b/pkg/provider/managedk8s/ruleset/disak8sstig/v2r5_ruleset.go new file mode 100644 index 000000000..57f004a76 --- /dev/null +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/v2r5_ruleset.go @@ -0,0 +1,948 @@ +// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package disak8sstig + +import ( + "crypto/tls" + "crypto/x509" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "path/filepath" + + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + + "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/provider/managedk8s/ruleset/disak8sstig/rules" + "github.com/gardener/diki/pkg/rule" + "github.com/gardener/diki/pkg/rule/retry" + "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" + sharedrules "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/rules" +) + +func validateV2R5Options[O rules.RuleOption](options any, fldPath *field.Path) field.ErrorList { + parsedOptions, err := getV2R5OptionOrNil[O](options) + if err != nil { + return field.ErrorList{ + field.InternalError(fldPath, err), + } + } + + if parsedOptions == nil { + return nil + } + + if val, ok := any(parsedOptions).(option.Option); ok { + return val.Validate(fldPath) + } + + return nil +} + +func (r *Ruleset) validateV2R5RuleOptions(ruleOptions map[string]internalconfig.IndexedRuleOptionsConfig, fldPath *field.Path) error { + allErrs := field.ErrorList{} + + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242383](ruleOptions[sharedrules.ID242383].Args, fldPath.Index(ruleOptions[sharedrules.ID242383].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242393](ruleOptions[sharedrules.ID242393].Args, fldPath.Index(ruleOptions[sharedrules.ID242393].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242394](ruleOptions[sharedrules.ID242394].Args, fldPath.Index(ruleOptions[sharedrules.ID242394].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242396](ruleOptions[sharedrules.ID242396].Args, fldPath.Index(ruleOptions[sharedrules.ID242396].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[rules.Options242400](ruleOptions[sharedrules.ID242400].Args, fldPath.Index(ruleOptions[sharedrules.ID242400].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242404](ruleOptions[sharedrules.ID242404].Args, fldPath.Index(ruleOptions[sharedrules.ID242404].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242406](ruleOptions[sharedrules.ID242406].Args, fldPath.Index(ruleOptions[sharedrules.ID242406].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242407](ruleOptions[sharedrules.ID242407].Args, fldPath.Index(ruleOptions[sharedrules.ID242407].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.Options242414](ruleOptions[sharedrules.ID242414].Args, fldPath.Index(ruleOptions[sharedrules.ID242414].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.Options242415](ruleOptions[sharedrules.ID242415].Args, fldPath.Index(ruleOptions[sharedrules.ID242415].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242417](ruleOptions[sharedrules.ID242417].Args, fldPath.Index(ruleOptions[sharedrules.ID242417].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[rules.Options242442](ruleOptions[sharedrules.ID242442].Args, fldPath.Index(ruleOptions[sharedrules.ID242442].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[option.ClusterObjectSelector](ruleOptions[sharedrules.ID242447].Args, fldPath.Index(ruleOptions[sharedrules.ID242447].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242448](ruleOptions[sharedrules.ID242448].Args, fldPath.Index(ruleOptions[sharedrules.ID242448].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242449](ruleOptions[sharedrules.ID242449].Args, fldPath.Index(ruleOptions[sharedrules.ID242449].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242450](ruleOptions[sharedrules.ID242450].Args, fldPath.Index(ruleOptions[sharedrules.ID242450].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[rules.Options242451](ruleOptions[sharedrules.ID242451].Args, fldPath.Index(ruleOptions[sharedrules.ID242451].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242452](ruleOptions[sharedrules.ID242452].Args, fldPath.Index(ruleOptions[sharedrules.ID242452].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242453](ruleOptions[sharedrules.ID242453].Args, fldPath.Index(ruleOptions[sharedrules.ID242453].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[rules.Options242466](ruleOptions[sharedrules.ID242466].Args, fldPath.Index(ruleOptions[sharedrules.ID242466].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[rules.Options242467](ruleOptions[sharedrules.ID242467].Args, fldPath.Index(ruleOptions[sharedrules.ID242467].Index).Child("args"))...) + + return allErrs.ToAggregate() +} + +func (r *Ruleset) registerV2R5Rules(ruleOptions map[string]config.RuleOptionsConfig) error { // TODO: add to FromGenericConfig + client, err := client.New(r.Config, client.Options{}) + if err != nil { + return err + } + + podContext, err := pod.NewSimplePodContext(client, r.Config, r.AdditionalOpsPodLabels) + if err != nil { + return err + } + + clientSet, err := kubernetes.NewForConfig(r.Config) + if err != nil { + return err + } + + authorityCertPool := x509.NewCertPool() + + switch { + case len(r.Config.CAData) > 0: + ok := authorityCertPool.AppendCertsFromPEM(r.Config.CAData) + if !ok { + return errors.New("failed to parse kube-apiserver CA data from config") + } + case len(r.Config.CAFile) > 0: + caFileData, err := os.ReadFile(filepath.Clean(r.Config.CAFile)) + if err != nil { + return fmt.Errorf("failed to read CA file: %w", err) + } + ok := authorityCertPool.AppendCertsFromPEM(caFileData) + if !ok { + return errors.New("failed to parse kube-apiserver CA data from config") + } + default: + authorityCertPool = nil + } + + opts242383, err := getV2R5OptionOrNil[sharedrules.Options242383](ruleOptions[sharedrules.ID242383].Args) + if err != nil { + return fmt.Errorf("rule option 242383 error: %s", err.Error()) + } + opts242393, err := getV2R5OptionOrNil[sharedrules.Options242393](ruleOptions[sharedrules.ID242393].Args) + if err != nil { + return fmt.Errorf("rule option 242393 error: %s", err.Error()) + } + opts242394, err := getV2R5OptionOrNil[sharedrules.Options242394](ruleOptions[sharedrules.ID242394].Args) + if err != nil { + return fmt.Errorf("rule option 242394 error: %s", err.Error()) + } + opts242396, err := getV2R5OptionOrNil[sharedrules.Options242396](ruleOptions[sharedrules.ID242396].Args) + if err != nil { + return fmt.Errorf("rule option 242396 error: %s", err.Error()) + } + opts242400, err := getV2R5OptionOrNil[rules.Options242400](ruleOptions[sharedrules.ID242400].Args) + if err != nil { + return fmt.Errorf("rule option 242400 error: %s", err.Error()) + } + opts242404, err := getV2R5OptionOrNil[sharedrules.Options242404](ruleOptions[sharedrules.ID242404].Args) + if err != nil { + return fmt.Errorf("rule option 242404 error: %s", err.Error()) + } + opts242406, err := getV2R5OptionOrNil[sharedrules.Options242406](ruleOptions[sharedrules.ID242406].Args) + if err != nil { + return fmt.Errorf("rule option 242406 error: %s", err.Error()) + } + opts242407, err := getV2R5OptionOrNil[sharedrules.Options242407](ruleOptions[sharedrules.ID242407].Args) + if err != nil { + return fmt.Errorf("rule option 242407 error: %s", err.Error()) + } + opts242414, err := getV2R5OptionOrNil[disaoption.Options242414](ruleOptions[sharedrules.ID242414].Args) + if err != nil { + return fmt.Errorf("rule option 242414 error: %s", err.Error()) + } + opts242415, err := getV2R5OptionOrNil[disaoption.Options242415](ruleOptions[sharedrules.ID242415].Args) + if err != nil { + return fmt.Errorf("rule option 242415 error: %s", err.Error()) + } + opts242417, err := getV2R5OptionOrNil[sharedrules.Options242417](ruleOptions[sharedrules.ID242417].Args) + if err != nil { + return fmt.Errorf("rule option 242417 error: %s", err.Error()) + } + opts242442, err := getV2R5OptionOrNil[rules.Options242442](ruleOptions[sharedrules.ID242442].Args) + if err != nil { + return fmt.Errorf("rule option 242442 error: %s", err.Error()) + } + opts242447, err := getV2R5OptionOrNil[option.ClusterObjectSelector](ruleOptions[sharedrules.ID242447].Args) + if err != nil { + return fmt.Errorf("rule option 242447 error: %s", err.Error()) + } + opts242448, err := getV2R5OptionOrNil[sharedrules.Options242448](ruleOptions[sharedrules.ID242448].Args) + if err != nil { + return fmt.Errorf("rule option 242448 error: %s", err.Error()) + } + opts242449, err := getV2R5OptionOrNil[sharedrules.Options242449](ruleOptions[sharedrules.ID242449].Args) + if err != nil { + return fmt.Errorf("rule option 242449 error: %s", err.Error()) + } + opts242450, err := getV2R5OptionOrNil[sharedrules.Options242450](ruleOptions[sharedrules.ID242450].Args) + if err != nil { + return fmt.Errorf("rule option 242450 error: %s", err.Error()) + } + opts242451, err := getV2R5OptionOrNil[rules.Options242451](ruleOptions[sharedrules.ID242451].Args) + if err != nil { + return fmt.Errorf("rule option 242451 error: %s", err.Error()) + } + opts242452, err := getV2R5OptionOrNil[sharedrules.Options242452](ruleOptions[sharedrules.ID242452].Args) + if err != nil { + return fmt.Errorf("rule option 242452 error: %s", err.Error()) + } + opts242453, err := getV2R5OptionOrNil[sharedrules.Options242453](ruleOptions[sharedrules.ID242453].Args) + if err != nil { + return fmt.Errorf("rule option 242453 error: %s", err.Error()) + } + opts242466, err := getV2R5OptionOrNil[rules.Options242466](ruleOptions[sharedrules.ID242466].Args) + if err != nil { + return fmt.Errorf("rule option 242466 error: %s", err.Error()) + } + opts242467, err := getV2R5OptionOrNil[rules.Options242467](ruleOptions[sharedrules.ID242467].Args) + if err != nil { + return fmt.Errorf("rule option 242467 error: %s", err.Error()) + } + + rcOpsPod := retry.RetryConditionFromRegex( + *retryerrors.OpsPodNotFoundRegexp, + ) + rcFileChecks := retry.RetryConditionFromRegex( + *retryerrors.ContainerNotFoundOnNodeRegexp, + *retryerrors.ContainerFileNotFoundOnNodeRegexp, + *retryerrors.ContainerNotReadyRegexp, + *retryerrors.OpsPodNotFoundRegexp, + *retryerrors.ObjectNotFoundRegexp, + ) + + const ( + noControlPlaneMsg = "The Managed Kubernetes cluster does not have access to control plane components." + ) + rules := []rule.Rule{ + rule.NewSkipRule( + sharedrules.ID242376, + "The Kubernetes Controller Manager must use TLS 1.2, at a minimum, to protect the confidentiality of sensitive data during electronic dissemination.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242377, + "The Kubernetes Scheduler must use TLS 1.2, at a minimum, to protect the confidentiality of sensitive data during electronic dissemination.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242378, + "The Kubernetes API Server must use TLS 1.2, at a minimum, to protect the confidentiality of sensitive data during electronic dissemination.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242379, + "The Kubernetes etcd must use TLS to protect the confidentiality of sensitive data during electronic dissemination.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242380, + "The Kubernetes etcd must use TLS to protect the confidentiality of sensitive data during electronic dissemination.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242381, + "The Kubernetes Controller Manager must create unique service accounts for each work payload.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID242382, + "The Kubernetes API Server must enable Node,RBAC as the authorization mode.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242383{ + Client: client, + Options: opts242383, + }, + rule.NewSkipRule( + sharedrules.ID242384, + "The Kubernetes Scheduler must have secure binding.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242385, + "The Kubernetes Controller Manager must have secure binding.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242387{ + Client: client, + V1RESTClient: clientSet.CoreV1().RESTClient(), + }, + rule.NewSkipRule( + sharedrules.ID242389, + "The Kubernetes API server must have the secure port set.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &rules.Rule242390{ + KAPIExternalURL: r.Config.Host, + Client: &http.Client{ + Transport: &http.Transport{ + // the TLS MinVersion warnings are ignored in order to avoid version conflicts + TLSClientConfig: &tls.Config{ // #nosec: G402 + RootCAs: authorityCertPool, + InsecureSkipVerify: r.Config.Insecure, // #nosec: G402 + }, + }, + }, + }, + &sharedrules.Rule242391{ + Client: client, + V1RESTClient: clientSet.CoreV1().RESTClient(), + }, + &sharedrules.Rule242392{ + Client: client, + V1RESTClient: clientSet.CoreV1().RESTClient(), + }, + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242393)), + retry.WithBaseRule(&sharedrules.Rule242393{ + Logger: r.Logger().With("rule_id", sharedrules.ID242393), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242393, + }), + retry.WithRetryCondition(rcOpsPod), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242394)), + retry.WithBaseRule(&sharedrules.Rule242394{ + Logger: r.Logger().With("rule_id", sharedrules.ID242394), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242394, + }), + retry.WithRetryCondition(rcOpsPod), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + &sharedrules.Rule242395{Client: client}, + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242396)), + retry.WithBaseRule(&sharedrules.Rule242396{ + Logger: r.Logger().With("rule_id", sharedrules.ID242396), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242396, + }), + retry.WithRetryCondition(rcOpsPod), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + &sharedrules.Rule242397{ + Client: client, + V1RESTClient: clientSet.CoreV1().RESTClient(), + }, + rule.NewSkipRule( + // feature-gates.DynamicAuditing removed in v1.19. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ + sharedrules.ID242398, + "Kubernetes DynamicAuditing must not be enabled.", + "Option feature-gates.DynamicAuditing was removed in Kubernetes v1.19.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242399, + "Kubernetes DynamicKubeletConfig must not be enabled.", + // feature-gates.DynamicKubeletConfig removed in v1.26. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ + "Option feature-gates.DynamicKubeletConfig removed in Kubernetes v1.26.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242400)), + retry.WithBaseRule(&rules.Rule242400{ + Logger: r.Logger().With("rule_id", sharedrules.ID242400), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + V1RESTClient: clientSet.CoreV1().RESTClient(), + Options: opts242400, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + + rule.NewSkipRule( + sharedrules.ID242402, + "The Kubernetes API Server must have an audit log path set.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242403, + "Kubernetes API Server must generate audit records that identify what type of event has occurred, identify the source of the event, contain the event results, identify any users, and identify any containers associated with the event.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242404)), + retry.WithBaseRule(&sharedrules.Rule242404{ + Logger: r.Logger().With("rule_id", sharedrules.ID242404), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242404, + }), + retry.WithRetryCondition(rcOpsPod), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + rule.NewSkipRule( + sharedrules.ID242405, + "Kubernetes manifests must be owned by root.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242406)), + retry.WithBaseRule(&sharedrules.Rule242406{ + Logger: r.Logger().With("rule_id", sharedrules.ID242406), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242406, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242407)), + retry.WithBaseRule(&sharedrules.Rule242407{ + Logger: r.Logger().With("rule_id", sharedrules.ID242407), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242407, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + rule.NewSkipRule( + sharedrules.ID242408, + "The Kubernetes manifest files must have least privileges.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242409, + "Kubernetes Controller Manager must disable profiling.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242410, + "The Kubernetes API Server must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242411, + "The Kubernetes Scheduler must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242412, + "The Kubernetes Controllers must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242413, + "The Kubernetes etcd must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &rules.Rule242414{ + Client: client, + Options: opts242414, + }, + &rules.Rule242415{ + Client: client, + Options: opts242415, + }, + &sharedrules.Rule242417{ + Client: client, + Options: opts242417, + }, + rule.NewSkipRule( + sharedrules.ID242418, + "The Kubernetes API server must use approved cipher suites.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242419, + "Kubernetes API Server must have the SSL Certificate Authority set.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242420{ + Client: client, + V1RESTClient: clientSet.CoreV1().RESTClient(), + }, + rule.NewSkipRule( + sharedrules.ID242421, + "Kubernetes Controller Manager must have the SSL Certificate Authority set.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242422, + "Kubernetes API Server must have a certificate for communication.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242423, + "Kubernetes etcd must enable client authentication to secure service.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242424{ + Client: client, + V1RESTClient: clientSet.CoreV1().RESTClient(), + }, + &sharedrules.Rule242425{ + Client: client, + V1RESTClient: clientSet.CoreV1().RESTClient(), + }, + rule.NewSkipRule( + sharedrules.ID242426, + "Kubernetes etcd must enable client authentication to secure service.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242427, + "Kubernetes etcd must have a key file for secure communication.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242428, + "Kubernetes etcd must have a certificate for communication.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242429, + "Kubernetes etcd must have the SSL Certificate Authority set.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242430, + "Kubernetes etcd must have a certificate for communication.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242431, + "Kubernetes etcd must have a key file for secure communication.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242432, + "Kubernetes etcd must have peer-cert-file set for secure communication.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242433, + "Kubernetes etcd must have a peer-key-file set for secure communication.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242434{ + Client: client, + V1RESTClient: clientSet.CoreV1().RESTClient(), + }, + rule.NewSkipRule( + sharedrules.ID242436, + "The Kubernetes API server must have the ValidatingAdmissionWebhook enabled.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID242437, + "Kubernetes must have a pod security policy set.", + "PSPs are removed in K8s version 1.25.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID242438, + "Kubernetes API Server must configure timeouts to limit attack surface.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &rules.Rule242442{ + // We check only system (kube-proxy) pods in this rule, since there can be a user case to run different versions of images. + Client: client, + Options: opts242442, + }, + rule.NewSkipRule( + sharedrules.ID242443, + "Kubernetes must contain the latest updates as authorized by IAVMs, CTOs, DTMs, and STIGs.", + "Scanning/patching security vulnerabilities should be enforced organizationally. Security vulnerability scanning should be automated and maintainers should be informed automatically.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242444, + "Kubernetes component manifests must be owned by root.", + "Rule is duplicate of 242405. "+noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242445, + "The Kubernetes component etcd must be owned by etcd.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242446, + "The Kubernetes conf files must be owned by root.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242447)), + retry.WithBaseRule(&sharedrules.Rule242447{ + Logger: r.Logger().With("rule_id", sharedrules.ID242447), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242447, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242448)), + retry.WithBaseRule(&sharedrules.Rule242448{ + Logger: r.Logger().With("rule_id", sharedrules.ID242448), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242448, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242449)), + retry.WithBaseRule(&sharedrules.Rule242449{ + Logger: r.Logger().With("rule_id", sharedrules.ID242449), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242449, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242450)), + retry.WithBaseRule(&sharedrules.Rule242450{ + Logger: r.Logger().With("rule_id", sharedrules.ID242450), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242450, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242451)), + retry.WithBaseRule(&rules.Rule242451{ + Logger: r.Logger().With("rule_id", sharedrules.ID242451), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242451, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242452)), + retry.WithBaseRule(&sharedrules.Rule242452{ + Logger: r.Logger().With("rule_id", sharedrules.ID242452), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242452, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242453)), + retry.WithBaseRule(&sharedrules.Rule242453{ + Logger: r.Logger().With("rule_id", sharedrules.ID242453), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242453, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + rule.NewSkipRule( + sharedrules.ID242454, + "The Kubernetes kubeadm.conf must be owned by root.", + `Gardener does not use kubeadm and also does not store any "main config" anywhere (flow/component logic built-in/in-code).`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242455, + "The Kubernetes kubeadm.conf must have file permissions set to 644 or more restrictive.", + `Gardener does not use kubeadm and also does not store any "main config" anywhere (flow/component logic built-in/in-code).`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242456, + "The Kubernetes kubelet config must have file permissions set to 644 or more restrictive.", + "Duplicate of 242452.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242457, + "The Kubernetes kubelet config must be owned by root.", + "Duplicate of 242453.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242459, + "The Kubernetes etcd must have file permissions set to 644 or more restrictive.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242460, + "The Kubernetes admin kubeconfig must have file permissions set to 644 or more restrictive.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242461, + "Kubernetes API Server audit logs must be enabled.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242462, + "The Kubernetes API Server must be set to audit log max size.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242463, + "The Kubernetes API Server must be set to audit log maximum backup.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242464, + "The Kubernetes API Server audit log retention must be set.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242465, + "The Kubernetes API Server audit log path must be set.", + "Duplicate of 242402. "+noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242466)), + retry.WithBaseRule(&rules.Rule242466{ + Logger: r.Logger().With("rule_id", sharedrules.ID242466), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242466, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242467)), + retry.WithBaseRule(&rules.Rule242467{ + Logger: r.Logger().With("rule_id", sharedrules.ID242467), + InstanceID: r.instanceID, + Client: client, + PodContext: podContext, + Options: opts242467, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + &sharedrules.Rule245541{ + Client: client, + V1RESTClient: clientSet.CoreV1().RESTClient(), + }, + rule.NewSkipRule( + sharedrules.ID245542, + "Kubernetes API Server must disable basic authentication to protect information in transit.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID245543, + "Kubernetes API Server must disable token authentication to protect information in transit.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID245544, + "Kubernetes endpoints must use approved organizational certificate and key pair to protect information in transit.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID254800, + "Kubernetes must have a Pod Security Admission control file configured.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + // featureGates.PodSecurity made GA in v1.25 and removed in v1.28. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ + sharedrules.ID254801, + "Kubernetes must enable PodSecurity admission controller on static pods and Kubelets.", + "Option featureGates.PodSecurity was made GA in v1.25 and removed in v1.28.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID274882, + "Kubernetes Secrets must be encrypted at rest.", + noControlPlaneMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID274883, + "Sensitive information must be stored using Kubernetes Secrets or an external Secret store provider.", + "Cannot be tested with confidence and should be enforced organizationally.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID274884, + "Kubernetes must limit Secret access on a need-to-know basis.", + "Cannot be tested with confidence and should be enforced organizationally.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + } + + for i, r := range rules { + var severityLevel rule.SeverityLevel + if severity, ok := r.(rule.Severity); !ok { + return fmt.Errorf("rule %s does not implement rule.Severity", r.ID()) + } else { + severityLevel = severity.Severity() + } + + opt, found := ruleOptions[r.ID()] + if found && opt.Skip != nil && opt.Skip.Enabled { + rules[i] = rule.NewSkipRule(r.ID(), r.Name(), opt.Skip.Justification, rule.Accepted, rule.SkipRuleWithSeverity(severityLevel)) + } + } + + // check that the registered rules equal + // the number of rules in that ruleset version + if len(rules) != 94 { + return fmt.Errorf("revision expects 94 registered rules, but got: %d", len(rules)) + } + + return r.AddRules(rules...) +} + +func parseV2R5Options[O rules.RuleOption](options any) (*O, error) { + optionsByte, err := json.Marshal(options) + if err != nil { + return nil, err + } + + var parsedOptions O + if err := json.Unmarshal(optionsByte, &parsedOptions); err != nil { + return nil, err + } + + return &parsedOptions, nil +} + +func getV2R5OptionOrNil[O rules.RuleOption](options any) (*O, error) { + if options == nil { + return nil, nil + } + return parseV2R5Options[O](options) +} diff --git a/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go b/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go index 3832acd33..49a9d641f 100644 --- a/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go +++ b/pkg/provider/virtualgarden/ruleset/disak8sstig/ruleset.go @@ -130,6 +130,13 @@ func FromGenericConfig(rulesetConfig config.RulesetConfig, additionalOpsPodLabel if err := ruleset.registerV2R4Rules(ruleOptions); err != nil { return nil, err } + case "v2r5": + if err := ruleset.validateV2R5RuleOptions(indexedRuleOptions, fldPath.Child("ruleOptions")); err != nil { + return nil, err + } + if err := ruleset.registerV2R5Rules(ruleOptions); err != nil { + return nil, err + } default: return nil, fmt.Errorf("unknown ruleset %s version: %s - use 'diki show provider virtualgarden' to see the provider's supported rulesets", rulesetConfig.ID, rulesetConfig.Version) } diff --git a/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r5_ruleset.go b/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r5_ruleset.go new file mode 100644 index 000000000..ca8f89edb --- /dev/null +++ b/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r5_ruleset.go @@ -0,0 +1,812 @@ +// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package disak8sstig + +import ( + "encoding/json" + "fmt" + + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/validation/field" + "sigs.k8s.io/controller-runtime/pkg/client" + + "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/provider/virtualgarden/ruleset/disak8sstig/rules" + "github.com/gardener/diki/pkg/rule" + "github.com/gardener/diki/pkg/rule/retry" + "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" + sharedrules "github.com/gardener/diki/pkg/shared/ruleset/disak8sstig/rules" +) + +func validateV2R5Options[O rules.RuleOption](options any, fldPath *field.Path) field.ErrorList { + parsedOptions, err := getV2R5OptionOrNil[O](options) + if err != nil { + return field.ErrorList{ + field.InternalError(fldPath, err), + } + } + + if parsedOptions == nil { + return nil + } + + if val, ok := any(parsedOptions).(option.Option); ok { + return val.Validate(fldPath) + } + + return nil +} + +func (r *Ruleset) validateV2R5RuleOptions(ruleOptions map[string]internalconfig.IndexedRuleOptionsConfig, fldPath *field.Path) error { + allErrs := field.ErrorList{} + + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options242390](ruleOptions[sharedrules.ID242390].Args, fldPath.Index(ruleOptions[sharedrules.ID242390].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.Options242442](ruleOptions[sharedrules.ID242442].Args, fldPath.Index(ruleOptions[sharedrules.ID242442].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242445].Args, fldPath.Index(ruleOptions[sharedrules.ID242445].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242446].Args, fldPath.Index(ruleOptions[sharedrules.ID242446].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242451].Args, fldPath.Index(ruleOptions[sharedrules.ID242451].Index).Child("args"))...) + allErrs = append(allErrs, validateV2R5Options[sharedrules.Options245543](ruleOptions[sharedrules.ID245543].Args, fldPath.Index(ruleOptions[sharedrules.ID245543].Index).Child("args"))...) + + return allErrs.ToAggregate() +} + +func (r *Ruleset) registerV2R5Rules(ruleOptions map[string]config.RuleOptionsConfig) error { // TODO: add to FromGenericConfig + runtimeClient, err := client.New(r.RuntimeConfig, client.Options{}) + if err != nil { + return err + } + + runtimePodContext, err := pod.NewSimplePodContext(runtimeClient, r.RuntimeConfig, r.AdditionalOpsPodLabels) + if err != nil { + return err + } + opts242390, err := getV2R5OptionOrNil[sharedrules.Options242390](ruleOptions[sharedrules.ID242390].Args) + if err != nil { + return fmt.Errorf("rule option 242390 error: %s", err.Error()) + } + opts242442, err := getV2R5OptionOrNil[disaoption.Options242442](ruleOptions[sharedrules.ID242442].Args) + if err != nil { + return fmt.Errorf("rule option 242442 error: %s", err.Error()) + } + opts242445, err := getV2R5OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242445].Args) + if err != nil { + return fmt.Errorf("rule option 242445 error: %s", err.Error()) + } + opts242446, err := getV2R5OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242446].Args) + if err != nil { + return fmt.Errorf("rule option 242446 error: %s", err.Error()) + } + opts242451, err := getV2R5OptionOrNil[disaoption.FileOwnerOptions](ruleOptions[sharedrules.ID242451].Args) + if err != nil { + return fmt.Errorf("rule option 242451 error: %s", err.Error()) + } + opts245543, err := getV2R5OptionOrNil[sharedrules.Options245543](ruleOptions[sharedrules.ID245543].Args) + if err != nil { + return fmt.Errorf("rule option 245543 error: %s", err.Error()) + } + + rcFileChecks := retry.RetryConditionFromRegex( + *retryerrors.ContainerNotFoundOnNodeRegexp, + *retryerrors.ContainerFileNotFoundOnNodeRegexp, + *retryerrors.ContainerNotReadyRegexp, + *retryerrors.OpsPodNotFoundRegexp, + *retryerrors.ObjectNotFoundRegexp, + ) + + const ( + ns = "garden" + etcdMain = "virtual-garden-etcd-main" + etcdEvents = "virtual-garden-etcd-events" + kcmDeploymentName = "virtual-garden-kube-controller-manager" + kcmContainerName = "kube-controller-manager" + apiserverDeploymentName = "virtual-garden-kube-apiserver" + apiserverContainerName = "kube-apiserver" + noKubeletsMsg = "The Virtual Garden cluster does not have any nodes therefore there are no kubelets to check." + noPodsMsg = "The Virtual Garden cluster does not have any nodes therefore there cluster does not have any pods." + ) + rules := []rule.Rule{ + &sharedrules.Rule242376{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: kcmDeploymentName, + ContainerName: kcmContainerName, + }, + rule.NewSkipRule( + sharedrules.ID242377, + "The Kubernetes Scheduler must use TLS 1.2, at a minimum, to protect the confidentiality of sensitive data during electronic dissemination.", + "The Virtual Garden cluster does not make use of a Kubernetes Scheduler.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242378{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242379{ + Client: runtimeClient, + Namespace: ns, + StatefulSetETCDMain: etcdMain, + StatefulSetETCDEvents: etcdEvents, + }, + &sharedrules.Rule242380{ + Client: runtimeClient, + Namespace: ns, + StatefulSetETCDMain: etcdMain, + StatefulSetETCDEvents: etcdEvents, + }, + &sharedrules.Rule242381{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: kcmDeploymentName, + ContainerName: kcmContainerName, + }, + &sharedrules.Rule242382{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + ExpectedStartModes: []string{"RBAC", "Webhook"}, + }, + rule.NewSkipRule( + sharedrules.ID242383, + "User-managed resources must be created in dedicated namespaces.", + "By design the Garden cluster provides separate namespaces for user projects and users do not have access to system namespaces.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID242384, + "The Kubernetes Scheduler must have secure binding.", + "The Virtual Garden cluster does not make use of a Kubernetes Scheduler.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242385, + "The Kubernetes Controller Manager must have secure binding.", + "The Kubernetes Controller Manager runs in a container which already has limited access to network interfaces. In addition ingress traffic to the Kubernetes Controller Manager is restricted via network policies, making an unintended exposure less likely.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242387, + "The Kubernetes Kubelet must have the read-only port flag disabled.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + &sharedrules.Rule242389{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242390{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + Options: opts242390, + }, + rule.NewSkipRule( + sharedrules.ID242391, + "The Kubernetes Kubelet must have anonymous authentication disabled.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID242392, + "The Kubernetes kubelet must enable explicit authorization.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID242393, + "Kubernetes Worker Nodes must not have sshd service running.", + "The Virtual Garden cluster does not have any nodes.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242394, + "Kubernetes Worker Nodes must not have the sshd service enabled.", + "The Virtual Garden cluster does not have any nodes.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242395, + "Kubernetes dashboard must not be enabled.", + "The Virtual Garden cluster does not have any nodes therefore it does not deploy a Kubernetes dashboard.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242396, + "Kubernetes Kubectl cp command must give expected access and results.", + "The Virtual Garden cluster does not have any nodes therefore it does not install kubectl.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242397, + "Kubernetes kubelet static PodPath must not enable static pods.", + "The Virtual Garden cluster does not have any nodes therefore there are no kubelets to check.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + // feature-gates.DynamicAuditing removed in v1.19. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ + sharedrules.ID242398, + "Kubernetes DynamicAuditing must not be enabled.", + "Option feature-gates.DynamicAuditing was removed in Kubernetes v1.19.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242399, + "Kubernetes DynamicKubeletConfig must not be enabled.", + // feature-gates.DynamicKubeletConfig removed in v1.26. ref https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates-removed/ + "Option feature-gates.DynamicKubeletConfig removed in Kubernetes v1.26.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &rules.Rule242400{ + Client: runtimeClient, + Namespace: ns, + }, + &sharedrules.Rule242402{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242403{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + }, + rule.NewSkipRule( + sharedrules.ID242404, + "Kubernetes Kubelet must deny hostname override.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242405, + "Kubernetes manifests must be owned by root.", + "Gardener does not deploy any control plane component as systemd processes or static pod.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242406, + "Kubernetes kubelet configuration file must be owned by root.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242407, + "The Kubernetes KubeletConfiguration files must have file permissions set to 644 or more restrictive.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242408, + "The Kubernetes manifest files must have least privileges.", + "Gardener does not deploy any control plane component as systemd processes or static pod.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242409{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: kcmDeploymentName, + ContainerName: kcmContainerName, + }, + rule.NewSkipRule( + sharedrules.ID242410, + "The Kubernetes API Server must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242411, + "The Kubernetes Scheduler must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + "The Virtual Garden cluster does not make use of a Kubernetes Scheduler.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242412, + "The Kubernetes Controllers must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242413, + "The Kubernetes etcd must enforce ports, protocols, and services (PPS) that adhere to the Ports, Protocols, and Services Management Category Assurance List (PPSM CAL).", + "Cannot be tested and should be enforced organizationally. Gardener uses a minimum of known and automatically opened/used/created ports/protocols/services (PPSM stands for Ports, Protocols, Service Management).", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242414, + "The Kubernetes cluster must use non-privileged host ports for user pods.", + noPodsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242415, + "Secrets in Kubernetes must not be stored as environment variables.", + noPodsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID242417, + "Kubernetes must separate user functionality.", + noPodsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242418{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242419{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + rule.NewSkipRule( + sharedrules.ID242420, + "Kubernetes Kubelet must have the SSL Certificate Authority set.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242421{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: kcmDeploymentName, + ContainerName: kcmContainerName, + }, + &sharedrules.Rule242422{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242423{ + Client: runtimeClient, + Namespace: ns, + StatefulSetETCDMain: etcdMain, + StatefulSetETCDEvents: etcdEvents, + }, + rule.NewSkipRule( + sharedrules.ID242424, + "Kubernetes Kubelet must enable tlsPrivateKeyFile for client authentication to secure service.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242425, + "Kubernetes Kubelet must enable tlsCertFile for client authentication to secure service.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule242426{ + Client: runtimeClient, + Namespace: ns, + StatefulSetETCDMain: etcdMain, + StatefulSetETCDEvents: etcdEvents, + }, + &sharedrules.Rule242427{ + Client: runtimeClient, + Namespace: ns, + StatefulSetETCDMain: etcdMain, + StatefulSetETCDEvents: etcdEvents, + }, + &sharedrules.Rule242428{ + Client: runtimeClient, + Namespace: ns, + StatefulSetETCDMain: etcdMain, + StatefulSetETCDEvents: etcdEvents, + }, + &sharedrules.Rule242429{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242430{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242431{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242432{ + Client: runtimeClient, + Namespace: ns, + StatefulSetETCDMain: etcdMain, + StatefulSetETCDEvents: etcdEvents, + }, + &sharedrules.Rule242433{ + Client: runtimeClient, + Namespace: ns, + StatefulSetETCDMain: etcdMain, + StatefulSetETCDEvents: etcdEvents, + }, + rule.NewSkipRule( + sharedrules.ID242434, + "Kubernetes Kubelet must enable kernel protection.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + &sharedrules.Rule242436{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + rule.NewSkipRule( + sharedrules.ID242437, + "Kubernetes must have a pod security policy set.", + "PSPs are removed in K8s version 1.25.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + &sharedrules.Rule242438{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &rules.Rule242442{ + Client: runtimeClient, + Namespace: ns, + Options: opts242442, + }, + rule.NewSkipRule( + sharedrules.ID242443, + "Kubernetes must contain the latest updates as authorized by IAVMs, CTOs, DTMs, and STIGs.", + "Scanning/patching security vulnerabilities should be enforced organizationally. Security vulnerability scanning should be automated and maintainers should be informed automatically.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242444, + "Kubernetes component manifests must be owned by root.", + "Rule is duplicate of 242405. Gardener does not deploy any control plane component as systemd processes or static pod.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242445)), + retry.WithBaseRule(&sharedrules.Rule242445{ + Logger: r.Logger().With("rule_id", sharedrules.ID242445), + InstanceID: r.instanceID, + Client: runtimeClient, + Namespace: ns, + PodContext: runtimePodContext, + ETCDMainSelector: labels.SelectorFromSet(labels.Set{"app.kubernetes.io/part-of": etcdMain}), + ETCDEventsSelector: labels.SelectorFromSet(labels.Set{"app.kubernetes.io/part-of": etcdEvents}), + Options: opts242445, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242446)), + retry.WithBaseRule(&sharedrules.Rule242446{ + Logger: r.Logger().With("rule_id", sharedrules.ID242446), + InstanceID: r.instanceID, + Client: runtimeClient, + Namespace: ns, + PodContext: runtimePodContext, + DeploymentNames: []string{apiserverDeploymentName, kcmDeploymentName}, + Options: opts242446, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + rule.NewSkipRule( + sharedrules.ID242447, + "The Kubernetes Kube Proxy kubeconfig must have file permissions set to 644 or more restrictive.", + noPodsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242448, + "The Kubernetes Kube Proxy kubeconfig must be owned by root.", + noPodsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242449, + "The Kubernetes Kubelet certificate authority file must have file permissions set to 644 or more restrictive.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242450, + "The Kubernetes Kubelet certificate authority must be owned by root.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242451)), + retry.WithBaseRule(&rules.Rule242451{ + Logger: r.Logger().With("rule_id", sharedrules.ID242451), + InstanceID: r.instanceID, + Client: runtimeClient, + Namespace: ns, + PodContext: runtimePodContext, + Options: opts242451, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + rule.NewSkipRule( + sharedrules.ID242452, + "The Kubernetes kubelet KubeConfig must have file permissions set to 644 or more restrictive.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242453, + "The Kubernetes kubelet KubeConfig file must be owned by root.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242454, + "The Kubernetes kubeadm.conf must be owned by root.", + `Gardener does not use kubeadm and also does not store any "main config" anywhere (flow/component logic built-in/in-code).`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242455, + "The Kubernetes kubeadm.conf must have file permissions set to 644 or more restrictive.", + `Gardener does not use kubeadm and also does not store any "main config" anywhere (flow/component logic built-in/in-code).`, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242456, + "The Kubernetes kubelet config must have file permissions set to 644 or more restrictive.", + "Duplicate of 242452. "+noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + rule.NewSkipRule( + sharedrules.ID242457, + "The Kubernetes kubelet config must be owned by root.", + "Duplicate of 242453. "+noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242459)), + retry.WithBaseRule(&sharedrules.Rule242459{ + Logger: r.Logger().With("rule_id", sharedrules.ID242459), + InstanceID: r.instanceID, + Client: runtimeClient, + Namespace: ns, + PodContext: runtimePodContext, + ETCDMainSelector: labels.SelectorFromSet(labels.Set{"app.kubernetes.io/part-of": etcdMain}), + ETCDEventsSelector: labels.SelectorFromSet(labels.Set{"app.kubernetes.io/part-of": etcdEvents}), + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242460)), + retry.WithBaseRule(&sharedrules.Rule242460{ + Logger: r.Logger().With("rule_id", sharedrules.ID242460), + InstanceID: r.instanceID, + Client: runtimeClient, + Namespace: ns, + PodContext: runtimePodContext, + DeploymentNames: []string{apiserverDeploymentName, kcmDeploymentName}, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + &sharedrules.Rule242461{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242462{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242463{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule242464{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + rule.NewSkipRule( + sharedrules.ID242465, + "The Kubernetes API Server audit log path must be set.", + "Rule is duplicate of 242402.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242466)), + retry.WithBaseRule(&rules.Rule242466{ + Logger: r.Logger().With("rule_id", sharedrules.ID242466), + InstanceID: r.instanceID, + Client: runtimeClient, + Namespace: ns, + PodContext: runtimePodContext, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + retry.New( + retry.WithLogger(r.Logger().With("rule_id", sharedrules.ID242467)), + retry.WithBaseRule(&rules.Rule242467{ + Logger: r.Logger().With("rule_id", sharedrules.ID242467), + InstanceID: r.instanceID, + Client: runtimeClient, + Namespace: ns, + PodContext: runtimePodContext, + }), + retry.WithRetryCondition(rcFileChecks), + retry.WithMaxRetries(*r.args.MaxRetries), + ), + rule.NewSkipRule( + sharedrules.ID245541, + "Kubernetes Kubelet must not disable timeouts.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + &sharedrules.Rule245542{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + &sharedrules.Rule245543{ + Client: runtimeClient, + Namespace: ns, + Options: opts245543, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + rule.NewSkipRule( + sharedrules.ID245544, + "Kubernetes endpoints must use approved organizational certificate and key pair to protect information in transit.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID254800, + "Kubernetes must have a Pod Security Admission control file configured.", + noPodsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID254801, + "Kubernetes must enable PodSecurity admission controller on static pods and Kubelets.", + noKubeletsMsg, + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + &sharedrules.Rule274882{ + Client: runtimeClient, + Namespace: ns, + DeploymentName: apiserverDeploymentName, + ContainerName: apiserverContainerName, + }, + rule.NewSkipRule( + sharedrules.ID274883, + "Sensitive information must be stored using Kubernetes Secrets or an external Secret store provider.", + "Cannot be tested with confidence and should be enforced organizationally.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityHigh), + ), + rule.NewSkipRule( + sharedrules.ID274884, + "Kubernetes must limit Secret access on a need-to-know basis.", + "Cannot be tested with confidence and should be enforced organizationally.", + rule.Skipped, + rule.SkipRuleWithSeverity(rule.SeverityMedium), + ), + } + + for i, r := range rules { + var severityLevel rule.SeverityLevel + if severity, ok := r.(rule.Severity); !ok { + return fmt.Errorf("rule %s does not implement rule.Severity", r.ID()) + } else { + severityLevel = severity.Severity() + } + + opt, found := ruleOptions[r.ID()] + if found && opt.Skip != nil && opt.Skip.Enabled { + rules[i] = rule.NewSkipRule(r.ID(), r.Name(), opt.Skip.Justification, rule.Accepted, rule.SkipRuleWithSeverity(severityLevel)) + } + } + + // check that the registered rules equal + // the number of rules in that ruleset version + if len(rules) != 94 { + return fmt.Errorf("revision expects 94 registered rules, but got: %d", len(rules)) + } + + return r.AddRules(rules...) +} + +func parseV2R5Options[O rules.RuleOption](options any) (*O, error) { + optionsByte, err := json.Marshal(options) + if err != nil { + return nil, err + } + + var parsedOptions O + if err := json.Unmarshal(optionsByte, &parsedOptions); err != nil { + return nil, err + } + + return &parsedOptions, nil +} + +func getV2R5OptionOrNil[O rules.RuleOption](options any) (*O, error) { + if options == nil { + return nil, nil + } + return parseV2R5Options[O](options) +} From 61f39e7b1299dff5803dedae03c80136076a0319 Mon Sep 17 00:00:00 2001 From: georgibaltiev Date: Tue, 3 Feb 2026 17:20:46 +0200 Subject: [PATCH 4/4] fix: dates --- pkg/provider/gardener/ruleset/disak8sstig/v2r5_ruleset.go | 2 +- pkg/provider/managedk8s/ruleset/disak8sstig/v2r5_ruleset.go | 2 +- pkg/provider/virtualgarden/ruleset/disak8sstig/v2r5_ruleset.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/provider/gardener/ruleset/disak8sstig/v2r5_ruleset.go b/pkg/provider/gardener/ruleset/disak8sstig/v2r5_ruleset.go index 14bb67354..ee0636ea0 100644 --- a/pkg/provider/gardener/ruleset/disak8sstig/v2r5_ruleset.go +++ b/pkg/provider/gardener/ruleset/disak8sstig/v2r5_ruleset.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 diff --git a/pkg/provider/managedk8s/ruleset/disak8sstig/v2r5_ruleset.go b/pkg/provider/managedk8s/ruleset/disak8sstig/v2r5_ruleset.go index 57f004a76..3ea7afee1 100644 --- a/pkg/provider/managedk8s/ruleset/disak8sstig/v2r5_ruleset.go +++ b/pkg/provider/managedk8s/ruleset/disak8sstig/v2r5_ruleset.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 diff --git a/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r5_ruleset.go b/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r5_ruleset.go index ca8f89edb..7d5b9b632 100644 --- a/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r5_ruleset.go +++ b/pkg/provider/virtualgarden/ruleset/disak8sstig/v2r5_ruleset.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0