Um das Kubernetes Cluster abzusichern sind einige Einträge in diversen Ressourcen beschränkt. Die Beschränkungen werden im Cluster mit Gatekeeper überprüft und wenn nötig werden Ressourcen abgelehnt. Wenn eine Ressource aufgrund dieser Beschränkungen abgelehnt wird, sollte die API den Grund dafür nennen, beispielsweise beim Anlegen einer Ressource mit kubectl.

Im Folgenden werden die Beschränkungen für die verschiedenen Ressourcen beschrieben.

VirtualService

Der Eintrag .spec.gateways muss immer wenigstens ein Gateway enthalten.

Diese Gateways dürfen kein / enthalten.

Außerdem ist das Gateway mesh nur in Kombination mit .spec.exportTo: ["."] erlaubt.

Wird in einem VirtualService die Annotation wwu.io/nic_node genutzt, um einen Host ins NIC zu exportieren, so sind die Einträge in .spec.hosts auf die mit den Administratoren abgesprochenen DNS Einträgen limitiert.

Certificate

Wenn .spec.issuerRef.kind auf ClusterIssuer gesetzt ist, müssen alle Einträge in .spec.commonName and .spec.dnsNames mit den Administratoren abgesprochen werden.

Ingress

Alle Hosts in den Ingress Ressourcen, sowohl unter .spec.rules[*].host als auch unter .spec.tls[*].hosts müssen mit den Administratoren abgesprochen und einzeln freigeschaltet werden.

Gateway

Die Selektoren .spec.selectors sind auf die expliziten Einträge {"istio": "ingressgateway"} und {"istio": "egressgateway"} beschränkt.

Ist im Selektor das Ingressgateway gewählt, müssen alle Hosts .spec.servers[*].hosts entweder mit ./ oder mit <namespace>/ beginnen.

Außerdem sind in diesem Fall als Hosts nur die DNS Einträge erlaubt, die vorher mit den Administratoren abgesprochen wurden.

Alle .spec.servers[*].tls.credentialName müssen als Präfix den Namen des Namespace haben, im Detail <namespace>--<secret-name>.

DNSEndpoint

Alle DNS Namen in .spec.endpoints[*].dnsName müssen mit den Administratoren abgesprochen und einzeln freigeschaltet werden.

Service

Services vom Type Loadbalancer sind per se nicht erlaubt und müssen mit den Administratoren abgesprochen werden.

Bei erlaubten Loadbalancern muss entwerde die Annotations loadbalancer.openstack.org/keep-floatingip=true oder service.beta.kubernetes.io/openstack-internal-load-balancer: "true" gesetzt sein und jede IP in .spec.loadBalancerIP muss mit den Administratoren abgesprochen und einzeln freigeschaltet werden.

In spec.ExternalIPs sind keine Einträge erlaubt.

Alle Ports die mit der Annotation networking.istio.io/exportTo zu Istio exportiert werden, müssen entweder in jedem .spec.ports appProtocol gesetzt haben oder der name muss mit dem Protokoll beginnen. Siehe https://istio.io/latest/docs/ops/configuration/traffic-management/protocol-selection/#explicit-protocol-selection für Details.

Pod

Wenn istio als Sidecars verwendet werden, dürfen alle anderen Container nicht als Nutzer 1337 laufen. Sprich .spec.securityContext.runAsUser und .spec.containers[name!=istio].securityContext.runAsUser dürfen nicht 1337 sein.

Auch die erlaubten .spec.tolerations auf den Pods sind eingeschränkt und müssen mit den Administratoren abgesprochen werden. So werden beispielsweise bestimmte Tolerations benötigt, um Pods auf den GPU Knoten oder den Worker Knoten laufen zu lassen.

Wenn GPUs benutzt werden, muss die Summe der .spec.containers[*].resource.limits.nvidia.com/gpu in der Summe 1 ergeben. Sprich, einer der Container muss dort eine 1 und bei den anderen darf dieser Wert entweder nicht gesetzt sein oder muss explizit auf 0 gesetzt werden. Dies wird vermutlich am besten in diesem Beipiel deutlich.

Außerdem haben wir für Pods komplexe Security Profile, die die Pods weiter einschränken. Diese werden im folgenden im Detail erklärt.

Security Profil

Es existieren 2 verschiedene Profile. Das default Profil, das den Zugang zu privilegierten Features des Clusters verbieten und das hardened Profile, das wesentlich stärker einschränkt und auch einige Best Practice erzwingt.

Default im Detail

Dies ist das Profil für eigentlich alle Kunden. Es schützt den Cluster vor unautorisierten Zugriffen.

NameCRDRestriktionen

Default

default-psp-capabilitiesK8sPSPCapabilitiesEs ist nicht erlaubt via securityContext.capabilities.add capabilities hinzuzufügenNone
default-psp-flexvolume-driversK8sPSPCapabilitiesEs ist nicht erlaubt flex volumes zu nutzenNone
default-psp-forbidden-systclsK8sPSPForbiddenSysctlsAlle sysctl Calls sind verbotenNone
default-psp-host-filesystemK8sPSPHostFilesystemKeine hostPaths  sind erlaubtNone
default-psp-host-namespaceK8sPSPHostNamespaceEs ist nicht erlaubt sich den Process Namespace mit dem Host zu teilenNone
default-psp-host-network-portsK8sPSPHostNetworkingPortsEs ist nicht erlaubt das Netzwerk des Hosts zu benutzenNone
default-psp-privileged-containerK8sPSPPrivilegedContainerprivilegierte Pods sind verbotenNone
default-psp-proc-mountK8sPSPProcMountNur der default procMountType ist erlaubt

None

default-psp-volume-typesK8sPSPVolumeTypes

Folgende Volume Typen sind erlaubt

  • emptyDir
  • downwardAPI
  • configMap
  • secret
  • persistentVolumeClaim
  • projected
None
default-container-must-have-limitsK8sContainerLimitsJeder Container muss resources.limits.cpu (<= 8), resources.limits.memory (<= 32Gi) und resources.limits.ephemeral-storage (<= 32Gi) gesetzt haben.None

Hardened im Detail

Dieses Profil ist eher für Systemdienste oder spezielle weitere Dienste gedacht. Es erlaubt einer wesentlich striktere Überprüfung, lässt sich aber auch feiner einstellen.

NameCRDRestriktionenDefault
hardened-psp-pods-allowed-user-rangesK8sPSPAllowedUsers

Der Pods

  • darf nicht als Root laufen
  • darf nicht mit der Root Gruppe laufen
  • sollte nicht mit der Root supplementalGroups laufen
  • sollte nicht mit der Root fsGroup  laufen
None
hardened-psp-allow-privilege-escalation-containerK8sPSPAllowPrivilegeEscalationContainerPrivilege Escalation ist verbotenIst das Feld nicht gesetzt, wird automatisch securityContext.allowPrivilegeEscalation: false hinzugefügt
hardened-psp-capabilities-containerK8sPSPCapabilitiesDer Pod muss alle Capabilities droppenIst das Feld nicht gesetzt, wird automatisch securityContext.capabilities.drop: ["ALL"]  hinzugefügt
hardened-psp-flexvolume-driversK8sPSPFlexVolumesEs ist nicht erlaubt flex volumes zu nutzenNone
hardened-psp-forbidden-systclsK8sPSPForbiddenSysctlsAlle sysctl Calls sind verbotenNone
hardened-psp-host-filesystemK8sPSPHostFilesystemKeine hostPaths  sind erlaubtNone
hardened-psp-host-namespaceK8sPSPHostNamespaceEs ist nicht erlaubt sich den Process Namespace mit dem Host zu teilenNone
hardened-psp-host-network-portsK8sPSPHostNetworkingPortsEs ist nicht erlaubt das Netzwerk des Hosts zu benutzenNone
hardened-psp-privileged-containerK8sPSPPrivilegedContainerprivilegierte Pods sind verbotenNone
hardened-psp-proc-mountK8sPSPProcMountNur der default procMountType ist erlaubt

None

hardened-psp-readonlyrootfilesystem-containerK8sPSPReadOnlyRootFilesystemDas Root Filesystem des Pods muss readonly seinIst das Feld nicht gesetzt, wird automatisch securityContext.readOnlyRootFilesystem: true hinzugefügt
hardened-psp-volume-typesK8sPSPVolumeTypes

Folgende Volume Typen sind erlaubt

  • emptyDir
  • downwardAPI
  • configMap
  • secret
  • persistentVolumeClaim
  • projected
None
hardened-container-must-have-limitsK8sContainerLimitsJeder Container muss resources.limits.cpu (<= 8), resources.limits.memory (<= 32Gi) und resources.limits.ephemeral-storage (<= 32Gi) gesetzt haben.None

Der Workflow mit dem hardened  Profil würde man zuerst auf dem Namespace das hardened  Profil aktivieren und danach, am besten per Pod, Labels hinzufügen, um die Überprüfung einzelner Komponenten wieder zu deaktiveren. Laufen die Pods wieder, können für die ausgeschalten Komponenten speziellere Constraints geschrieben werden. Außerdem können natürlich auch weitere striktere Constraints hinzugefügt werden.

Aktivieren eines Security Profils

Ein Security Profil wird per Namespace aktiviert mittels des Labels constraints.gatekeeper.sh/securityprofile=<name>  aktiviert, z.B. constraints.gatekeeper.sh/securityprofile=<default>

Ausschalten einzelner Komponenten eines Profils

Die Erzwingen verschiedenen Policies eines Profils kann über Labels wieder ausgeschaltet werden. Diese haben die Form constraints.gatekeeper.sh/drop-<name>, z.B. constraints.gatekeeper.sh/drop-default-psp-capabilities=true Für das default  Profil können diese nur auf Namespaces gesetzt werden und schalten die Komponente für alle Pods in dem Namespace wieder aus. Für hardened  können einzelne Komponenten auf pro Pod per Pod Label ausgeschaltet werden.

Schreiben eigener Constraints

Will man restriktivere Sicherheitsregeln oder eine existierende ersetzen (alte Komponente ausschalten und ersetzen), kann man eigene, sogenannte Constraints schreiben. Die dazugehörigen CRDs sind weiter unten in der Liste mit aufgeführt und zum Teil parametrisierbar. So kann man z.B. mit der folgenden Cluster Ressource

prometheus-psp-host-network-ports
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPHostNetworkingPorts
metadata:
  name: prometheus-node-exporter-psp-host-network-ports
spec:
  match:
      kinds:
        - apiGroups:
            - ''
          kinds:
            - Pod
      namespaces:
        - prometheus
      labelSelector:
        matchLabels:
          app: prometheus-node-exporter
  parameters:
    hostNetwork: true
    hostPorts:
      min: 9100
      max: 9100

den Port 9100 im Host Network für alle Pods mit dem Label app=prometheus-node-exporter im Namespace prometheus  erlauben. Für weitere Informationen sei auf die Gatekeeper Dokumentation verwiesen. Außerdem kann man auch in direkt in die CRDs schauen.

  • Keine Stichwörter