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.
Name | CRD | Restriktionen | Default |
---|---|---|---|
default-psp-capabilities | K8sPSPCapabilities | Es ist nicht erlaubt via securityContext.capabilities.add capabilities hinzuzufügen | None |
default-psp-flexvolume-drivers | K8sPSPCapabilities | Es ist nicht erlaubt flex volumes zu nutzen | None |
default-psp-forbidden-systcls | K8sPSPForbiddenSysctls | Alle sysctl Calls sind verboten | None |
default-psp-host-filesystem | K8sPSPHostFilesystem | Keine hostPaths sind erlaubt | None |
default-psp-host-namespace | K8sPSPHostNamespace | Es ist nicht erlaubt sich den Process Namespace mit dem Host zu teilen | None |
default-psp-host-network-ports | K8sPSPHostNetworkingPorts | Es ist nicht erlaubt das Netzwerk des Hosts zu benutzen | None |
default-psp-privileged-container | K8sPSPPrivilegedContainer | privilegierte Pods sind verboten | None |
default-psp-proc-mount | K8sPSPProcMount | Nur der default procMountType ist erlaubt | None |
default-psp-volume-types | K8sPSPVolumeTypes | Folgende Volume Typen sind erlaubt
| None |
default-container-must-have-limits | K8sContainerLimits | Jeder 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.
Name | CRD | Restriktionen | Default |
---|---|---|---|
hardened-psp-pods-allowed-user-ranges | K8sPSPAllowedUsers | Der Pods
| None |
hardened-psp-allow-privilege-escalation-container | K8sPSPAllowPrivilegeEscalationContainer | Privilege Escalation ist verboten | Ist das Feld nicht gesetzt, wird automatisch securityContext.allowPrivilegeEscalation: false hinzugefügt |
hardened-psp-capabilities-container | K8sPSPCapabilities | Der Pod muss alle Capabilities droppen | Ist das Feld nicht gesetzt, wird automatisch securityContext.capabilities.drop: ["ALL"] hinzugefügt |
hardened-psp-flexvolume-drivers | K8sPSPFlexVolumes | Es ist nicht erlaubt flex volumes zu nutzen | None |
hardened-psp-forbidden-systcls | K8sPSPForbiddenSysctls | Alle sysctl Calls sind verboten | None |
hardened-psp-host-filesystem | K8sPSPHostFilesystem | Keine hostPaths sind erlaubt | None |
hardened-psp-host-namespace | K8sPSPHostNamespace | Es ist nicht erlaubt sich den Process Namespace mit dem Host zu teilen | None |
hardened-psp-host-network-ports | K8sPSPHostNetworkingPorts | Es ist nicht erlaubt das Netzwerk des Hosts zu benutzen | None |
hardened-psp-privileged-container | K8sPSPPrivilegedContainer | privilegierte Pods sind verboten | None |
hardened-psp-proc-mount | K8sPSPProcMount | Nur der default procMountType ist erlaubt | None |
hardened-psp-readonlyrootfilesystem-container | K8sPSPReadOnlyRootFilesystem | Das Root Filesystem des Pods muss readonly sein | Ist das Feld nicht gesetzt, wird automatisch securityContext.readOnlyRootFilesystem: true hinzugefügt |
hardened-psp-volume-types | K8sPSPVolumeTypes | Folgende Volume Typen sind erlaubt
| None |
hardened-container-must-have-limits | K8sContainerLimits | Jeder 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
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.