Skip to content

Gardener Compliance Report

Overview

Gardener aims to comply with public security standards and guidelines, such as the Security Technical Implementation Guide (STIG) for Kubernetes from Defense Information Systems Agency (DISA). The DISA Kubernetes STIG is a set of rules that provide recommendations for secure deployment and operation of Kubernetes. It covers various aspects of Kubernetes security, including the configurations of the Kubernetes API server and other components, cluster management, certificate management, handling of updates and patches.

While Gardener aims to follow this guideline, we also recognize that not all of the rules may be directly applicable or optimal for Gardener specific environment. Therefore, some of the requirements are adjusted. Rules that are not applicable to Gardener are skipped given an appropriate justification.

For every release, we check that Gardener is able of creating security hardened shoot clusters, reconfirming that the configurations which are not secure by default (as per Gardener Kubernetes Cluster Hardening Procedure) are still possible and work as expected.

In order to automate and ease this process, Gardener uses a tool called diki.

Security Hardened Shoot Configurations

The following security hardened shoot configurations were used in order to generate the compliance report.

AWS

kind: Shoot
apiVersion: core.gardener.cloud/v1beta1
metadata:
  name: aws
spec:
  cloudProfileName: aws
  kubernetes:
    kubeAPIServer:
      admissionPlugins:
        - name: PodSecurity
          config:
            apiVersion: pod-security.admission.config.k8s.io/v1beta1
            kind: PodSecurityConfiguration
            defaults:
              enforce: baseline
              audit: baseline
              warn: baseline
          disabled: false
      auditConfig:
        auditPolicy:
          configMapRef:
            name: audit-policy
    version: "1.31"
    enableStaticTokenKubeconfig: false
  networking:
    type: calico
    pods: 100.64.0.0/12
    nodes: 10.180.0.0/16
    services: 100.104.0.0/13
    ipFamilies:
      - IPv4
  provider:
    type: aws
    controlPlaneConfig:
      apiVersion: aws.provider.extensions.gardener.cloud/v1alpha1
      kind: ControlPlaneConfig
    infrastructureConfig:
      apiVersion: aws.provider.extensions.gardener.cloud/v1alpha1
      kind: InfrastructureConfig
      networks:
        vpc:
          cidr: 10.180.0.0/16
        zones:
          - internal: 10.180.48.0/20
            name: eu-west-1c
            public: 10.180.32.0/20
            workers: 10.180.0.0/19
    workers:
      - cri:
          name: containerd
        name: worker-kkfk1
        machine:
          type: m5.large
          image:
            name: gardenlinux
          architecture: amd64
        maximum: 2
        minimum: 2
        maxSurge: 1
        maxUnavailable: 0
        volume:
          type: gp3
          size: 50Gi
        zones:
          - eu-west-1c
    workersSettings:
      sshAccess:
        enabled: false
  purpose: evaluation
  region: eu-west-1
  secretBindingName: secretBindingName
Azure

kind: Shoot
apiVersion: core.gardener.cloud/v1beta1
metadata:
  name: azure
spec:
  cloudProfileName: az
  kubernetes:
    kubeAPIServer:
      admissionPlugins:
        - name: PodSecurity
          config:
            apiVersion: pod-security.admission.config.k8s.io/v1beta1
            kind: PodSecurityConfiguration
            defaults:
              enforce: baseline
              audit: baseline
              warn: baseline
          disabled: false
      auditConfig:
        auditPolicy:
          configMapRef:
            name: audit-policy
    version: "1.31"
    enableStaticTokenKubeconfig: false
  networking:
    type: calico
    pods: 100.64.0.0/12
    nodes: 10.180.0.0/16
    services: 100.104.0.0/13
    ipFamilies:
      - IPv4
  provider:
    type: azure
    controlPlaneConfig:
      apiVersion: azure.provider.extensions.gardener.cloud/v1alpha1
      kind: ControlPlaneConfig
    infrastructureConfig:
      apiVersion: azure.provider.extensions.gardener.cloud/v1alpha1
      kind: InfrastructureConfig
      networks:
        vnet:
          cidr: 10.180.0.0/16
        workers: 10.180.0.0/16
      zoned: true
    workers:
      - cri:
          name: containerd
        name: worker-g7p4p
        machine:
          type: Standard_A4_v2
          image:
            name: gardenlinux
          architecture: amd64
        maximum: 2
        minimum: 2
        maxSurge: 1
        maxUnavailable: 0
        volume:
          type: StandardSSD_LRS
          size: 50Gi
        zones:
          - '3'
    workersSettings:
      sshAccess:
        enabled: false
  purpose: evaluation
  region: westeurope
  secretBindingName: secretBindingName
GCP

kind: Shoot
apiVersion: core.gardener.cloud/v1beta1
metadata:
  name: gcp
spec:
  cloudProfileName: gcp
  kubernetes:
    kubeAPIServer:
      admissionPlugins:
        - name: PodSecurity
          config:
            apiVersion: pod-security.admission.config.k8s.io/v1beta1
            kind: PodSecurityConfiguration
            defaults:
              enforce: baseline
              audit: baseline
              warn: baseline
          disabled: false
      auditConfig:
        auditPolicy:
          configMapRef:
            name: audit-policy
    version: "1.31"
    enableStaticTokenKubeconfig: false
  networking:
    type: calico
    pods: 100.64.0.0/12
    nodes: 10.180.0.0/16
    services: 100.104.0.0/13
    ipFamilies:
      - IPv4
  provider:
    type: gcp
    controlPlaneConfig:
      apiVersion: gcp.provider.extensions.gardener.cloud/v1alpha1
      kind: ControlPlaneConfig
      zone: europe-west1-b
    infrastructureConfig:
      apiVersion: gcp.provider.extensions.gardener.cloud/v1alpha1
      kind: InfrastructureConfig
      networks:
        workers: 10.180.0.0/16
    workers:
      - cri:
          name: containerd
        name: worker-bex82
        machine:
          type: n1-standard-2
          image:
            name: gardenlinux
          architecture: amd64
        maximum: 2
        minimum: 2
        maxSurge: 1
        maxUnavailable: 0
        volume:
          type: pd-balanced
          size: 50Gi
        zones:
          - europe-west1-b
    workersSettings:
      sshAccess:
        enabled: false
  purpose: evaluation
  region: europe-west1
  secretBindingName: secretBindingName
OpenStack

kind: Shoot
apiVersion: core.gardener.cloud/v1beta1
metadata:
  name: openstack
spec:
  cloudProfileName: converged-cloud-cp
  kubernetes:
    kubeAPIServer:
      admissionPlugins:
        - name: PodSecurity
          config:
            apiVersion: pod-security.admission.config.k8s.io/v1beta1
            kind: PodSecurityConfiguration
            defaults:
              enforce: baseline
              audit: baseline
              warn: baseline
          disabled: false
      auditConfig:
        auditPolicy:
          configMapRef:
            name: audit-policy
    version: "1.31"
    enableStaticTokenKubeconfig: false
  networking:
    type: calico
    pods: 100.64.0.0/12
    nodes: 10.180.0.0/16
    services: 100.104.0.0/13
    ipFamilies:
      - IPv4
  provider:
    type: openstack
    controlPlaneConfig:
      apiVersion: openstack.provider.extensions.gardener.cloud/v1alpha1
      kind: ControlPlaneConfig
      loadBalancerProvider: f5
    infrastructureConfig:
      apiVersion: openstack.provider.extensions.gardener.cloud/v1alpha1
      kind: InfrastructureConfig
      networks:
        workers: 10.180.0.0/16
      floatingPoolName: FloatingIP-external-cp
    workers:
      - cri:
          name: containerd
        name: worker-dqty2
        machine:
          type: g_c2_m4
          image:
            name: gardenlinux
          architecture: amd64
        maximum: 2
        minimum: 2
        maxSurge: 1
        maxUnavailable: 0
        zones:
          - eu-de-1b
    workersSettings:
      sshAccess:
        enabled: false
  purpose: evaluation
  region: eu-de-1
  secretBindingName: secretBindingName

For every created Security Hardened Shoot, additional NetworkPolicies are configured for the default, kube-public and kube-node-lease namespaces, in order to comply with Rule 2000 of the Security Hardened Kubernetes ruleset. This configuration is done since Gardener does not take care of the NetworkPolicies in the aforementioned namespaces.

Diki Configuration

The following diki configuration was used in order to test each of the shoot clusters described above. Mind that the rules regarding audit logging are skipped because organizations have different requirements and Gardener can integrate with different audit logging solutions.

Configuration

metadata: ...
providers:
- id: gardener
  name: Gardener
  metadata: ...
  args: ...
  rulesets:
  - id: disa-kubernetes-stig
    name: DISA Kubernetes Security Technical Implementation Guide
    version: v2r3
    args:
      maxRetries: 5
    ruleOptions: 
    - ruleID: "242402"
      skip:
        enabled: true
        justification: Gardener can integrate with different audit logging solutions.
    - ruleID: "242403"
      skip:
        enabled: true
        justification: Gardener can integrate with different audit logging solutions.
    - ruleID: "242414"
      args:
        acceptedPods:
        - podMatchLabels:
            k8s-app: node-local-dns
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Node local dns requires port 53 in order to operate properly."
          ports:
          - 53
    - ruleID: "242445"
      args:
        expectedFileOwner:
          users: ["0", "65532"]
          groups: ["0", "65532"]
    - ruleID: "242446"
      args:
        expectedFileOwner:
          users: ["0", "65532"]
          groups: ["0", "65532"]
    - ruleID: "242451"
      args:
        expectedFileOwner:
          users: ["0", "65532"]
          groups: ["0", "65532"]
    - ruleID: "242462"
      skip:
        enabled: true
        justification: Gardener can integrate with different audit logging solutions.
    - ruleID: "242463"
      skip:
        enabled: true
        justification: Gardener can integrate with different audit logging solutions.
    - ruleID: "242464"
      skip:
        enabled: true
        justification: Gardener can integrate with different audit logging solutions.
    - ruleID: "245543"
      args:
        acceptedTokens:
        - user: "health-check"
          uid: "health-check"
    - ruleID: "254800"
      args:
        minPodSecurityStandardsProfile: "baseline"
- id: garden
  name: "Garden"
  metadata: ...
  args: ...
  rulesets:
  - id: security-hardened-shoot-cluster
    name: Security Hardened Shoot Cluster
    version: v0.2.1
    args: ...
    ruleOptions: 
    - ruleID: "1000"
      args:
        extensions:
        - type: shoot-lakom-service
    - ruleID: "2007"
      args:
        minPodSecurityStandardsProfile: baseline
- id: managedk8s
  name: "Managed Kubernetes"
  metadata: ...
  args: ...
  rulesets:
  - id: security-hardened-k8s
    name: Security Hardened Kubernetes Cluster
    version: v0.1.0
    ruleOptions: 
    - ruleID: "2001"
      args:
        acceptedPods:
        - matchLabels:
            k8s-app: calico-node
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to allow privilege escalation."
        - matchLabels:
            app: csi
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to allow privilege escalation."
        - matchLabels:
            gardener.cloud/role: network-problem-detector
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to allow privilege escalation."
        - matchLabels:
            app: node-problem-detector
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to allow privilege escalation."
        - matchLabels:
            role: proxy
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to allow privilege escalation."
        - matchLabels:
            app: vpn-shoot
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to allow privilege escalation."
    - ruleID: "2003"
      args:
        acceptedPods:
        - matchLabels:
            k8s-app: calico-node
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use a wider range of volume types."
          volumeNames:
          - lib-modules
          - var-run-calico
          - var-lib-calico
          - xtables-lock
          - cni-bin-dir
          - cni-net-dir
          - cni-log-dir
          - policysync
        - matchLabels:
            app: csi
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use a wider range of volume types."
          volumeNames:
          - registration-dir
          - plugin-dir
          - kubelet-dir
          - pods-mount-dir
          - host-dev
          - device-dir
          - sys-devices-dir
          - scsi-host-dir
        - matchLabels:
            k8s-app: egress-filter-applier
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use a wider range of volume types."
          volumeNames:
          - xtables-lock
        - matchLabels:
            role: proxy
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use a wider range of volume types."
          volumeNames:
          - ssl-certs-hosts
          - kernel-modules
          - kube-proxy-dir
          - kube-proxy-mode
          - xtables-lock
        - matchLabels:
            gardener.cloud/role: network-problem-detector
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use a wider range of volume types."
          volumeNames:
          - log
          - output
        - matchLabels:
            component: node-exporter
            gardener.cloud/role: monitoring
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use a wider range of volume types."
          volumeNames:
          - host
          - textfile
        - matchLabels:
            k8s-app: node-local-dns
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use a wider range of volume types."
          volumeNames:
          - xtables-lock
        - matchLabels:
            app: node-problem-detector
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use a wider range of volume types."
          volumeNames:
          - log
          - localtime
          - kmsg
        - matchLabels:
            app: vpn-shoot
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use a wider range of volume types."
          volumeNames:
          - dev-net-tun
    - ruleID: "2005"
      args:
        allowedImages:
        - prefix: "europe-docker.pkg.dev/sap-se-gcp-k8s-c-delivery/"
        - prefix: "europe-docker.pkg.dev/gardener-project/"
        - prefix: "quay.io/prometheus/" # required by node-exporter and blackbox-exporter
        - prefix: "registry.k8s.io/coredns/" # required by coredns
        - prefix: "registry.k8s.io/dns/" # required by node-local-dns
        - prefix: "registry.k8s.io/metrics-server/" # required by metric-server
        - prefix: "registry.k8s.io/sig-storage/" # required by csi-driver-node
        - prefix: "registry.k8s.io/cloud-provider-gcp/" # required by csi-driver-node
        - prefix: "registry.k8s.io/node-problem-detector/" # required by node-problem-detector
    - ruleID: "2006"
      args:
        acceptedClusterRoles:
        - matchLabels:
            kubernetes.io/bootstrapping: rbac-defaults
          justification: "Default RBAC Roles."
        - matchLabels:
            gardener.cloud/role: vpa
            resources.gardener.cloud/managed-by: gardener
          justification: "VPA RBAC Roles require */scale permissions to vertically scale resources."
    - ruleID: "2007"
      args:
        acceptedClusterRoles:
        - matchLabels:
            kubernetes.io/bootstrapping: rbac-defaults
          justification: "Default RBAC Roles."
        - matchLabels:
            gardener.cloud/role: vpa
            resources.gardener.cloud/managed-by: gardener
          justification: "VPA RBAC Roles require */scale permissions to vertically scale resources."
    - ruleID: "2008"
      args:
        acceptedPods:
        - matchLabels:
            k8s-app: calico-node
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use hostPath volumes."
          volumeNames:
          - lib-modules
          - var-run-calico
          - var-lib-calico
          - xtables-lock
          - cni-bin-dir
          - cni-net-dir
          - cni-log-dir
          - policysync
        - matchLabels:
            app: csi
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use hostPath volumes."
          volumeNames:
          - registration-dir
          - plugin-dir
          - kubelet-dir
          - pods-mount-dir
          - host-dev
          - device-dir
          - sys-devices-dir
          - scsi-host-dir
        - matchLabels:
            k8s-app: egress-filter-applier
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use hostPath volumes."
          volumeNames:
          - xtables-lock
        - matchLabels:
            role: proxy
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use hostPath volumes."
          volumeNames:
          - ssl-certs-hosts
          - kernel-modules
          - kube-proxy-dir
          - kube-proxy-mode
          - xtables-lock
        - matchLabels:
            gardener.cloud/role: network-problem-detector
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use hostPath volumes."
          volumeNames:
          - log
          - output
        - matchLabels:
            component: node-exporter
            gardener.cloud/role: monitoring
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use hostPath volumes."
          volumeNames:
          - host
          - textfile
        - matchLabels:
            k8s-app: node-local-dns
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use hostPath volumes."
          volumeNames:
          - xtables-lock
        - matchLabels:
            app: node-problem-detector
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use hostPath volumes."
          volumeNames:
          - log
          - localtime
          - kmsg
        - matchLabels:
            app: vpn-shoot
            resources.gardener.cloud/managed-by: gardener
          namespaceMatchLabels:
            kubernetes.io/metadata.name: kube-system
          justification: "Gardener managed resources are accepted to use hostPath volumes."
          volumeNames:
          - dev-net-tun
output:
  minStatus: Passed

Security Compliance Report for Hardened Shoot Clusters

The report can be directly viewed by clicking here or downloaded by clicking here

EU and German government funding logos

Funded by the European Union – NextGenerationEU.

The views and opinions expressed are solely those of the author(s) and do not necessarily reflect the views of the European Union or the European Commission. Neither the European Union nor the European Commission can be held responsible for them.