Table des matières
Version - 2020.03
Dernière mise-à-jour : 2020/12/31 10:14
DOF305 - Sécurisation de Kubernetes
Contenu du Module
- DOF305 - Sécurisation de Kubernetes
- Contenu du Module
- LAB #1 - Role Based Acces Control et Certificats TLS
- 1.1 - Présentation
- 1.2 - Le Fichier /etc/kubernetes/manifests/kube-apiserver.yaml
- 1.3 - Création d'un serviceAccount
- 1.4 - Création d'un Utilisateur
- 1.5 - Certificats TLS
- LAB #2 - Implémentation de la Sécurité au niveau des Pods
- 2.1 - Présentation
- 2.2 - Kubernetes Security Context
- ReadOnlyRootFilesystem
- drop
- 2.3 - Kubernetes Pod Security Policy
- 2.4 - Kubernetes Network Policies
- 2.5 - Kubernetes Resource Allocation Management
- LAB #3 - Sécuriser les Composants de Kubernetes
- 3.1 - L'accès à l'API Kubelet
- 3.2 - L'accès de Kubelet à l'API Kubernetes
- 3.3 - Sécuriser etcd
LAB #1 - Role Based Acces Control et Certificats TLS
1.1 - Présentation
Un objet Kubernetes est soit lié à un Namespace soit non-lié à un Namespace.
Kubernetes utilise l'API rbac.authorization.k8s.io pour gérer les autorisations. Les acteurs jouant un rôle dans cette API sont :
- Namespaces,
- peuvent être considérées comme des clusters virtuels,
- permettent l'isolation et la segmentation logique,
- permettent le regroupement d'utilisateurs, de rôles et de ressources,
- sont utilisés avec des applications, des clients, des projets ou des équipes.
- Subjects,
- Regular Users - permettent la gestion des accès autorisés depuis l'extérieur du cluster que cela soit par un utilisateur physique ou sous une autre forme. La gestion des utilisateurs est la responsabilité de l'Administrateur du cluster,
- ServiceAccounts - permettent la mise en place de permissions au niveau des entités logiciels. Kubernetes crée un certain nombre de serviceAccounts automatiquement mais l'Administrateur peut en créer d'autres. Chaque pod a un serviceAccount qui gère les privilèges accordés au processus et aux conteneurs du pod,
- User Groups - Kubernetes regroupe des utilisateurs en utilisant des propriétés communes telles le préfixe d'un serviceAccount ou le champ de l'organisation dans un certificat. Il est ensuite possible d'accorder des privilèges de type RBAC aux groupes ainsi créés.
- Resources,
- ce sont des entités auxquelles auront accès les Subjects,
- une ressource est une entité telle un pod, un deployment ou des sous-ressources telles les journaux d'un pod,
- le Pod Security Policy (PSP) est aussi considéré comme une ressource.
- Roles et ClusterRoles,
- Roles - permettent de définir des règles représentant un jeu de permissions, telles GET WATCH LIST CREATE UPDATE PATCH et DELETE, qui peuvent être utilisées avec des ressources dans un Namespace,
- On ajoute des permissions, on ne les retire pas. Il n'y a pas donc des règles de type deny.
- ClusterRoles - n'est pas lié à un Namespace. Un ClusterRole est utilisé pour :
- définir des permissions pour des ressources à être utilisées dans un Namespace
- définir des permissions pour des ressources à être utilisées dans tous les Namespaces
- définir des permissions pour des ressources du cluster.
- Un exemple d'un Role pour accorder les permissions dans le Namespace default est :
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: default name: pod-reader rules: - apiGroups: [""] # "" indicates the core API group resources: ["pods"] verbs: ["get", "watch", "list"]
- Un example d'un ClusterRole pour accorder des permissions de lecture des secrets dans un Namespace spécifique ou dans tous les Namespaces est :
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: secret-reader rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "watch", "list"]
- RoleBindings et ClusterRoleBindings,
- permettent d'accorder des permissions définies dans des Roles ou ClusterRoles à des Subjects,
- RoleBindings sont spécifiques à un NameSpace,
- ClusterRoleBindings s'appliquent au niveau du Cluster.
1.2 - Le Fichier /etc/kubernetes/manifests/kube-apiserver.yaml
Connectez-vous au noeud kind-control-plane en utilisant la commande docker :
root@debian10:~# docker exec -it kind-control-plane /bin/bash root@kind-control-plane:/#
L'utilisation de RBAC est définie par la valeur de la directive –authorization-mode dans le fichier /etc/kubernetes/manifests/kube-apiserver.yaml :
root@kind-control-plane:/# cat /etc/kubernetes/manifests/kube-apiserver.yaml apiVersion: v1 kind: Pod metadata: annotations: kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 172.18.0.5:6443 creationTimestamp: null labels: component: kube-apiserver tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - --advertise-address=172.18.0.5 - --allow-privileged=true - --authorization-mode=Node,RBAC - --client-ca-file=/etc/kubernetes/pki/ca.crt - --enable-admission-plugins=NodeRestriction - --enable-bootstrap-token-auth=true - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key - --etcd-servers=https://127.0.0.1:2379 - --insecure-port=0 - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key - --requestheader-allowed-names=front-proxy-client - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt - --requestheader-extra-headers-prefix=X-Remote-Extra- - --requestheader-group-headers=X-Remote-Group - --requestheader-username-headers=X-Remote-User - --runtime-config= - --secure-port=6443 - --service-account-key-file=/etc/kubernetes/pki/sa.pub - --service-cluster-ip-range=10.96.0.0/16 - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key --More-- [q] <--------------------------------------------------------------Appuyez sur la touche **q** root@kind-control-plane:/# exit exit
1.3 - Création d'un serviceAccount
Il est préférable de créer un serviceAccount par service. Ceci permet une configuration plus fine de la sécurité concernant le service. Si un serviceAccount n'est pas spécifié lors de la création des pods, ces pods se verront attribués le serviceAccount par défaut du Namespace.
Imaginons que vous souhaitez que votre application interagisse avec l'API de Kubernetes afin d'obtenir des informations sur les pods dans un Namespace. le serviceAccount par défaut dasn le Namespace default ne peut pas accomplir cette tâche :
root@debian10:~# kubectl auth can-i list pods -n default --as=system:serviceaccount:default:default no
Important : le format de la valeur de l'option –as est system:serviceaccount:namespace:Nom_du_serviceaccount.
Créez maintenant le fichier flask.yaml :
root@debian10:~# vi flask.yaml root@debian10:~# cat flask.yaml apiVersion: v1 kind: Namespace metadata: name: flask --- apiVersion: v1 kind: ServiceAccount metadata: name: flask-backend namespace: flask --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: flask-backend-role namespace: flask rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: flask-backend-role-binding namespace: flask subjects: - kind: ServiceAccount name: flask-backend namespace: flask roleRef: kind: Role name: flask-backend-role apiGroup: rbac.authorization.k8s.io
Ce fichier crée :
- un Namespace appelé flask,
- un serviceAccount appelé flask-backend pour le Namespace flask,
- un Role appelé flask-backend-role qui accorde les permissions get, watch et list sur les pods dans le Namespace flask,
- un RoleBinding appelé flask-backend-role-binding qui accorde les permissions définies dans le Role flask-backend-role au Subject de type serviceAccount appelé flask-backend.
Important : apiGroups: [“”] - “” indique le groupe api core ou legacy. Ce groupe se trouve au chemin REST /api/v1. Ce groupe n'est jamais spécifié dans un champs apiVersion, d'où la raison pour laquelle on écrit apiVersion: v1 et non apiVersion api/v1.
Appliquez le fichier :
root@debian10:~# kubectl create -f flask.yaml namespace/flask created serviceaccount/flask-backend created role.rbac.authorization.k8s.io/flask-backend-role created rolebinding.rbac.authorization.k8s.io/flask-backend-role-binding created
Créez maintenant le fichier deployment_flask.yaml qui crée des pods qui utiliseront le serviceAccount appelé flask-backend :
root@debian10:~# vi deployment_flask.yaml root@debian10:~# cat deployment_flask.yaml --- apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deployment namespace: flask labels: app: myapp type: front-end spec: template: metadata: name: myapp-pod labels: app: myapp type: front-end spec: serviceAccount: flask-backend containers: - name: nginx-container image: nginx replicas: 3 selector: matchLabels: type: front-end
Exécutez kubectl :
root@debian10:~# kubectl create -f deployment_flask.yaml deployment.apps/myapp-deployment created
Vérifiez la présence du deployment :
root@debian10:~# kubectl get deployment -n flask NAME READY UP-TO-DATE AVAILABLE AGE myapp-deployment 3/3 3 3 25s
Vérifiez qu'un token d'accès à l'API a été créé :
root@debian10:~# kubectl get secrets -n flask NAME TYPE DATA AGE default-token-k2s9s kubernetes.io/service-account-token 3 3m8s flask-backend-token-b2n9r kubernetes.io/service-account-token 3 3m8s
Vérifiez maintenant que le serviceAccount flask-backend peut lister les pods dans le Namespace flask :
root@debian10:~# kubectl auth can-i list pods -n flask --as=system:serviceaccount:flask:flask-backend yes
Notez cependant que le serviceAccount flask-backend n'a pas la permission create dans le Namespace flask :
root@debian10:~# kubectl auth can-i create pods -n flask --as=system:serviceaccount:flask:flask-backend no
et que le serviceAccount flask-backend n'a pas la permission list dans le Namespace default :
root@debian10:~# kubectl auth can-i list pods -n default --as=system:serviceaccount:flask:flask-backend no
1.4 - Création d'un Utilisateur
Connectez-vous au noeud kind-control-plane en utilisant la commande docker :
root@debian10:~# docker exec -it kind-control-plane /bin/bash root@kind-control-plane:/#
Configurez l'accès au cluster :
root@kind-control-plane:/# mkdir -p $HOME/.kube root@kind-control-plane:/# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config root@kind-control-plane:/# chown $(id -u):$(id -g) $HOME/.kube/config
Les utilisateurs font partis du contexte de configuration qui définit le nom du cluster et le nom du Namespace :
root@kind-control-plane:/# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE * kubernetes-admin@kind kind kubernetes-admin
Important : Un contexte est un élément qui regroupe les paramètres d'accès sous un nom. Les paramètres d'accès sont au nombre de trois, à savoir le cluster, le namespace et l'utilisateur. La commande kubectl utilise les paramètres du contexte courant pour communiquer avec le cluster.
Consultez le contexte courant :
root@kind-control-plane:/# kubectl config view apiVersion: v1 clusters: - cluster: certificate-authority-data: DATA+OMITTED server: https://kind-control-plane:6443 name: kind contexts: - context: cluster: kind user: kubernetes-admin name: kubernetes-admin@kind current-context: kubernetes-admin@kind kind: Config preferences: {} users: - name: kubernetes-admin user: client-certificate-data: REDACTED client-key-data: REDACTED
Important : Le mot REDACTED indique que les valeurs sont cachées pour des raisons de sécurité.
Pour créer un nouveau utilisateur il faut commencer par créer une clef privée pour l'utilisateur :
root@kind-control-plane:/# openssl genrsa -out trainee.key 2048 Generating RSA private key, 2048 bit long modulus (2 primes) .........................................+++++ .......+++++ e is 65537 (0x010001)
Créez maintenant un CSR :
root@kind-control-plane:/# openssl req -new -key trainee.key -out trainee.csr -subj "/CN=trainee/O=examplegroup"
Important : Notez que Kubernetes utilisera la valeur de la clef de l'organisation pour le regroupement des utilisateurs.
Le CSR doit être signé par le CA racine de Kubernetes :
root@kind-control-plane:/# ls -l /etc/kubernetes/pki/ca.* -rw-r--r-- 1 root root 1066 Dec 13 12:34 /etc/kubernetes/pki/ca.crt -rw------- 1 root root 1675 Dec 13 12:34 /etc/kubernetes/pki/ca.key
Signez donc le CSR :
root@kind-control-plane:/# openssl x509 -req -in trainee.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out trainee.crt Signature ok subject=CN = trainee, O = examplegroup Getting CA Private Key
Visualisez le certificat de trainee :
root@kind-control-plane:/# openssl x509 -in trainee.crt -text Certificate: Data: Version: 1 (0x0) Serial Number: 65:7a:9f:e1:5d:bd:48:27:b3:50:c9:9f:60:c8:04:85:4e:85:7b:02 Signature Algorithm: sha256WithRSAEncryption Issuer: CN = kubernetes Validity Not Before: Dec 15 13:36:03 2020 GMT Not After : Jan 14 13:36:03 2021 GMT Subject: CN = trainee, O = examplegroup Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:b1:41:77:ae:dd:43:01:ee:73:57:73:46:f4:64: 84:52:49:7d:75:0c:26:e1:da:4e:46:e0:ba:af:84: 7d:05:51:db:da:51:99:26:37:b1:6b:a0:29:c7:fa: c8:0a:ca:ab:97:34:7d:79:a8:ea:00:75:9c:22:fd: 40:1e:dd:85:72:29:dd:18:c1:9e:9c:b6:e7:54:a1: 65:9a:08:e5:e5:22:1a:cc:71:33:6e:71:d2:2b:1d: 1b:ed:01:06:5e:42:33:db:97:a0:9b:c7:59:b2:ca: 0f:37:f2:a7:23:a9:6e:cd:63:fd:62:ac:b9:c6:aa: 47:e7:4c:7f:8b:b5:03:cd:8d:09:b6:98:c4:79:0e: 97:b5:7b:8c:69:0b:10:a3:05:1d:8f:a6:dd:42:dc: 60:76:08:1d:a2:9f:76:42:fe:77:6b:4e:d0:dc:a8: 54:ab:e2:07:9e:0d:d0:1c:18:f5:c0:ca:3f:d9:32: 5c:45:fd:e5:b3:94:bc:04:0e:49:fa:e6:2e:c1:e1: f2:b4:88:38:9b:d5:b8:27:d3:6f:8b:80:ac:bb:59: 17:20:9c:6a:71:72:5a:3e:c9:05:07:e2:6d:33:e2: 2e:78:58:85:46:89:ca:74:3e:d1:3a:a5:6c:1b:cb: f0:84:62:8b:44:9c:bb:1b:ab:d8:81:77:37:c0:c9: 27:15 Exponent: 65537 (0x10001) Signature Algorithm: sha256WithRSAEncryption 33:14:25:3c:51:ac:63:0b:a6:6f:1e:49:aa:3b:9c:e2:56:47: ea:23:d7:2f:e7:b0:4b:c0:6f:a7:51:19:2d:2e:ad:5e:ef:be: 07:3e:96:f5:db:6c:64:6d:83:0d:81:51:04:43:0c:ce:0b:09: de:ed:d3:c9:b5:85:b7:09:b9:20:67:31:4d:e1:09:7c:ec:e3: ef:b4:10:ff:7a:fe:8e:20:6c:0f:d5:b5:1f:73:f2:a9:d9:ec: 84:eb:c2:ab:1d:0e:27:ab:a8:b5:11:a6:d6:55:98:e6:90:2d: 25:e4:85:f1:70:04:e3:8e:9a:a8:bf:aa:a7:6a:ea:9f:5a:72: f1:20:7d:f5:a6:1d:0b:0e:bd:ba:d8:93:a6:a1:62:99:01:5d: bf:70:60:25:cd:cb:a8:62:39:e7:81:5c:79:da:b4:23:4e:54: a5:f4:cd:7a:58:13:a7:de:da:02:80:d9:9f:2b:6c:00:c1:43: a8:a9:f8:de:1f:fa:ff:a8:12:a0:ea:e8:1c:aa:2f:99:ee:9c: 9d:09:63:79:f8:7f:2d:12:67:aa:dd:71:70:c1:b6:19:3e:fe: 1e:74:4e:71:41:01:49:9e:51:38:d2:00:eb:2e:b8:cf:4f:0d: 19:9b:2b:0c:57:d7:dc:4d:23:d0:17:27:42:39:bf:ec:7c:e9: 22:ac:23:2e -----BEGIN CERTIFICATE----- MIICxTCCAa0CFGV6n+FdvUgns1DJn2DIBIVOhXsCMA0GCSqGSIb3DQEBCwUAMBUx EzARBgNVBAMTCmt1YmVybmV0ZXMwHhcNMjAxMjE1MTMzNjAzWhcNMjEwMTE0MTMz NjAzWjApMRAwDgYDVQQDDAd0cmFpbmVlMRUwEwYDVQQKDAxleGFtcGxlZ3JvdXAw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxQXeu3UMB7nNXc0b0ZIRS SX11DCbh2k5G4LqvhH0FUdvaUZkmN7FroCnH+sgKyquXNH15qOoAdZwi/UAe3YVy Kd0YwZ6ctudUoWWaCOXlIhrMcTNucdIrHRvtAQZeQjPbl6Cbx1myyg838qcjqW7N Y/1irLnGqkfnTH+LtQPNjQm2mMR5Dpe1e4xpCxCjBR2Ppt1C3GB2CB2in3ZC/ndr TtDcqFSr4geeDdAcGPXAyj/ZMlxF/eWzlLwEDkn65i7B4fK0iDib1bgn02+LgKy7 WRcgnGpxclo+yQUH4m0z4i54WIVGicp0PtE6pWwby/CEYotEnLsbq9iBdzfAyScV AgMBAAEwDQYJKoZIhvcNAQELBQADggEBADMUJTxRrGMLpm8eSao7nOJWR+oj1y/n sEvAb6dRGS0urV7vvgc+lvXbbGRtgw2BUQRDDM4LCd7t08m1hbcJuSBnMU3hCXzs 4++0EP96/o4gbA/VtR9z8qnZ7ITrwqsdDierqLURptZVmOaQLSXkhfFwBOOOmqi/ qqdq6p9acvEgffWmHQsOvbrYk6ahYpkBXb9wYCXNy6hiOeeBXHnatCNOVKX0zXpY E6fe2gKA2Z8rbADBQ6ip+N4f+v+oEqDq6ByqL5nunJ0JY3n4fy0SZ6rdcXDBthk+ /h50TnFBAUmeUTjSAOsuuM9PDRmbKwxX19xNI9AXJ0I5v+x86SKsIy4= -----END CERTIFICATE-----
Créez un deuxième utilisateur dans la même Organisation :
root@kind-control-plane:/# openssl genrsa -out stagiaire.key 2048 Generating RSA private key, 2048 bit long modulus (2 primes) ............+++++ ...........................................................................................................+++++ e is 65537 (0x010001) root@kind-control-plane:/# openssl req -new -key stagiaire.key -out stagiaire.csr -subj "/CN=stagiaire/O=examplegroup" root@kind-control-plane:/# openssl x509 -req -in stagiaire.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out stagiaire.crt Signature ok subject=CN = stagiaire, O = examplegroup Getting CA Private Key
Créez maintenant le contexte trainee :
root@kind-control-plane:/# kubectl config set-credentials trainee --client-certificate=trainee.crt --client-key=trainee.key User "trainee" set. root@kind-control-plane:/# kubectl config set-context trainee@kubernetes --cluster=kubernetes --user=trainee Context "trainee@kubernetes" created.
Vérifiez que le contexte soit présent :
root@kind-control-plane:/# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE * kubernetes-admin@kind kind kubernetes-admin trainee@kubernetes kubernetes trainee
Utilisez le contexte de trainee :
root@kind-control-plane:/# kubectl config use-context trainee@kubernetes Switched to context "trainee@kubernetes".
Vérifiez que vous utilisez le contexte de trainee :
root@kind-control-plane:/# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE kubernetes-admin@kind kind kubernetes-admin * trainee@kubernetes kubernetes trainee
Revenez au contexte de kubernetes-admin@kind :
root@kind-control-plane:/# kubectl config use-context kubernetes-admin@kind Switched to context "kubernetes-admin@kind". root@kind-control-plane:/# exit exit root@debian10:~#
1.5 - Certificats TLS
Par défaut la communication entre kubectl et l'API Kubernetes est cryptée. Les certificats se trouvent dans le répertoire /var/lib/kubelet/pki/ de chaque noeud :
root@kind-control-plane:/# ls -l /var/lib/kubelet/pki/ total 12 -rw------- 1 root root 2818 Dec 13 12:34 kubelet-client-2020-12-13-12-34-39.pem lrwxrwxrwx 1 root root 59 Dec 13 12:34 kubelet-client-current.pem -> /var/lib/kubelet/pki/kubelet-client-2020-12-13-12-34-39.pem -rw-r--r-- 1 root root 2319 Dec 13 12:34 kubelet.crt -rw------- 1 root root 1679 Dec 13 12:34 kubelet.key root@kind-control-plane:/# exit root@debian10:~# docker exec -it kind-worker2 /bin/bash root@kind-worker2:/# ls -l /var/lib/kubelet/pki/ total 12 -rw------- 1 root root 1118 Dec 13 12:35 kubelet-client-2020-12-13-12-35-43.pem lrwxrwxrwx 1 root root 59 Dec 13 12:35 kubelet-client-current.pem -> /var/lib/kubelet/pki/kubelet-client-2020-12-13-12-35-43.pem -rw-r--r-- 1 root root 2279 Dec 13 12:35 kubelet.crt -rw------- 1 root root 1679 Dec 13 12:35 kubelet.key root@kind-worker2:/# exit exit root@debian10:~# docker exec -it kind-worker3 /bin/bash root@kind-worker3:/# ls -l /var/lib/kubelet/pki/ total 12 -rw------- 1 root root 1118 Dec 13 12:35 kubelet-client-2020-12-13-12-35-45.pem lrwxrwxrwx 1 root root 59 Dec 13 12:35 kubelet-client-current.pem -> /var/lib/kubelet/pki/kubelet-client-2020-12-13-12-35-45.pem -rw-r--r-- 1 root root 2279 Dec 13 12:35 kubelet.crt -rw------- 1 root root 1675 Dec 13 12:35 kubelet.key root@kind-worker3:/# exit exit
Important : Par défaut les certificats de kubelet expirent au bout d'un an.
LAB #2 - Implémentation de la Sécurité au niveau des Pods
2.1 - Présentation
Un Admission Controller est un morceau de code qui intercepte les requêtes à destination de l'API de Kubernetes. L'utilisation des Admission Controllers est définie part la directive –admission-control du fichier /etc/kubernetes/manifests/kube-apiserver.yaml, par exemple :
--admission-control=Initializers, NamespaceLifecycle, LimitRanger, ServiceAccount, PersistentVolumeLabel, DefaultStorageClass, DefaultTolerationSeconds, NodeRestriction, ResourceQuota
Les Admission Controllers les plus importants en termes de sécurité sont :
- DenyEscalatingExec,
- interdit l'exécution des commandes avec un escalated container dans un pod priviligié. Les commandes concernées sont exec et attach. Un escalated container dans un pod priviligié n'est pas isolé et permet donc l'accès à l'hôte.
- NodeRestriction,
- limite les objets d'un nœud et d'un pod que kubectl est capable de modifier,
- PodSecurityPolicy,
- agit lors de la création ou de la modification d'un pod pour décider si celui-ci est admis au cluster en fonction du Contexte de Sécurité et les policies applicables,
- ValidatingAdmissionWebhooks,
- permet d'appeler un service externe qui implémente une politique de sécurité, tel que Grafeas.
2.2 - Kubernetes Security Context
La configuration du Contexte de Sécurité se fait du pod ou du conteneur. Voici quelques exemples.
ReadOnlyRootFilesystem
Créez le fichier readonly.yaml :
root@debian10:~# vi readonly.yaml root@debian10:~# cat readonly.yaml apiVersion: v1 kind: Pod metadata: name: flask-ro namespace: default spec: containers: - image: mateobur/flask name: flask-ro securityContext: readOnlyRootFilesystem: true
Exécutez kubectl :
root@kubemaster:~# kubectl create -f readonly.yaml pod/flask-ro created
Vérifiez que le pod est en état de READY :
root@debian10:~# kubectl create -f readonly.yaml pod/flask-ro created root@debian10:~# kubectl get pods NAME READY STATUS RESTARTS AGE flask-ro 0/1 ContainerCreating 0 11s postgres-deployment-746bc85b8-8lw6c 1/1 Running 1 4h23m redis-deployment-64cff75679-8zqr8 1/1 Running 1 4h23m result-app-deployment-7cdc94dfcd-nddsh 1/1 Running 1 4h23m result-app-deployment-7cdc94dfcd-ntbdj 1/1 Running 0 3h15m result-app-deployment-7cdc94dfcd-wsm2d 1/1 Running 0 3h15m voting-app-deployment-678c67fc7-59q7z 1/1 Running 0 3h15m voting-app-deployment-678c67fc7-sgczf 1/1 Running 0 3h15m voting-app-deployment-678c67fc7-zcs6c 1/1 Running 1 4h23m worker-app-deployment-767d5b67ff-sgj2x 1/1 Running 2 4h23m root@debian10:~# kubectl get pods NAME READY STATUS RESTARTS AGE flask-ro 1/1 Running 0 29s postgres-deployment-746bc85b8-8lw6c 1/1 Running 1 4h24m redis-deployment-64cff75679-8zqr8 1/1 Running 1 4h24m result-app-deployment-7cdc94dfcd-nddsh 1/1 Running 1 4h24m result-app-deployment-7cdc94dfcd-ntbdj 1/1 Running 0 3h15m result-app-deployment-7cdc94dfcd-wsm2d 1/1 Running 0 3h15m voting-app-deployment-678c67fc7-59q7z 1/1 Running 0 3h15m voting-app-deployment-678c67fc7-sgczf 1/1 Running 0 3h15m voting-app-deployment-678c67fc7-zcs6c 1/1 Running 1 4h23m worker-app-deployment-767d5b67ff-sgj2x 1/1 Running 2 4h23m
Connectez-vous au conteneur :
root@debian10:~# kubectl exec -it flask-ro -- bash root@flask-ro:/#
Notez que le système est en lecture seule :
root@flask-ro:/# mount | grep "/ " overlay on / type overlay (ro,relatime,lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/57/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/56/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/55/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/54/fs,upperdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/58/fs,workdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/58/work) root@flask-ro:/# touch test touch: cannot touch 'test': Read-only file system root@flask-ro:/# exit exit command terminated with exit code 1
drop
Créez le fichier drop.yaml :
root@debian10:~# vi drop.yaml root@debian10:~# cat drop.yaml apiVersion: v1 kind: Pod metadata: name: flask-cap namespace: default spec: containers: - image: mateobur/flask name: flask-cap securityContext: capabilities: drop: - NET_RAW - CHOWN
Exécutez kubectl :
root@debian10:~# kubectl create -f drop.yaml pod/flask-cap created
Vérifiez que le pod est en état de READY :
root@debian10:~# kubectl get pods NAME READY STATUS RESTARTS AGE flask-cap 1/1 Running 0 23s flask-ro 1/1 Running 0 5m42s postgres-deployment-746bc85b8-8lw6c 1/1 Running 1 4h29m redis-deployment-64cff75679-8zqr8 1/1 Running 1 4h29m result-app-deployment-7cdc94dfcd-nddsh 1/1 Running 1 4h29m result-app-deployment-7cdc94dfcd-ntbdj 1/1 Running 0 3h20m result-app-deployment-7cdc94dfcd-wsm2d 1/1 Running 0 3h20m voting-app-deployment-678c67fc7-59q7z 1/1 Running 0 3h21m voting-app-deployment-678c67fc7-sgczf 1/1 Running 0 3h21m voting-app-deployment-678c67fc7-zcs6c 1/1 Running 1 4h29m worker-app-deployment-767d5b67ff-sgj2x 1/1 Running 2 4h29m
Connectez-vous au conteneur :
root@debian10:~# kubectl exec -it flask-cap -- bash root@flask-cap:/#
Notez la mise en place des restrictions :
root@flask-cap:/# ping 8.8.8.8 ping: Lacking privilege for raw socket. root@flask-cap:/# chown daemon /tmp chown: changing ownership of '/tmp': Operation not permitted root@flask-cap:/# exit exit command terminated with exit code 1
HERE
2.3 - Kubernetes Pod Security Policy
Créez le fichier psp.yaml :
root@kubemaster:~# vi psp.yaml root@kubemaster:~# cat psp.yaml apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: example spec: privileged: true seLinux: rule: RunAsAny supplementalGroups: rule: RunAsAny runAsUser: rule: 'MustRunAs' ranges: - min: 1 max: 65535 fsGroup: rule: 'MustRunAs' ranges: - min: 1 max: 65535 volumes: - '*'
Exécutez kubectl :
root@kubemaster:~# kubectl create -f psp.yaml podsecuritypolicy.policy/example created
Consultez la présence de la Pod Security Policy :
root@kubemaster:~# kubectl get psp NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES example true RunAsAny MustRunAs MustRunAs RunAsAny false *
La Pod Security Policy créée empêche l'exécution d'un pod en utilisant l'utilisateur et le groupe root.
2.4 - Kubernetes Network Policies
Installez l'application exemple Guestbook de Kubernetes :
root@kubemaster:~# kubectl create -f https://raw.githubusercontent.com/fabric8io/kansible/master/vendor/k8s.io/kubernetes/examples/guestbook/all-in-one/guestbook-all-in-one.yaml service/redis-master created replicationcontroller/redis-master created service/redis-slave created replicationcontroller/redis-slave created service/frontend created replicationcontroller/frontend created
Cette application crée des pods de type backend et frontend :
root@kubemaster:~# kubectl describe pod redis-master-8rczl | grep tier tier=backend root@kubemaster:~# kubectl describe pod frontend-762mw | grep tier tier=frontend
Créez le fichier guestbook-network-policy.yaml qui empêchera la communication d'un pod backend vers un pod frontend :
root@kubemaster:~# vi guestbook-network-policy.yaml root@kubemaster:~# cat guestbook-network-policy.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-backend-egress namespace: default spec: podSelector: matchLabels: tier: backend policyTypes: - Egress egress: - to: - podSelector: matchLabels: tier: backend
Exécutez kubectl :
root@kubemaster:~# kubectl create -f guestbook-network-policy.yaml networkpolicy.networking.k8s.io/deny-backend-egress created
Attendez que tous les pods soient dans un état de READY :
root@kubemaster:~# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES flask-cap 1/1 Running 1 139m 192.168.205.242 kubenode1 <none> <none> flask-ro 1/1 Running 0 65m 192.168.35.141 kubenode2 <none> <none> frontend-762mw 1/1 Running 0 17m 192.168.205.250 kubenode1 <none> <none> frontend-lhw8b 1/1 Running 0 17m 192.168.35.143 kubenode2 <none> <none> frontend-n75vs 1/1 Running 0 17m 192.168.205.252 kubenode1 <none> <none> postgres-deployment-5b8bd66778-j99zz 1/1 Running 8 4d3h 192.168.35.138 kubenode2 <none> <none> redis-deployment-67d4c466c4-9wzfn 1/1 Running 8 4d3h 192.168.205.246 kubenode1 <none> <none> redis-master-8rczl 1/1 Running 0 17m 192.168.205.249 kubenode1 <none> <none> redis-slave-c8jzv 1/1 Running 0 17m 192.168.35.142 kubenode2 <none> <none> redis-slave-fjrjm 1/1 Running 0 17m 192.168.205.251 kubenode1 <none> <none> result-app-deployment-b8f9dc967-nzbgd 1/1 Running 8 4d3h 192.168.205.245 kubenode1 <none> <none> result-app-deployment-b8f9dc967-r84k6 1/1 Running 8 4d 192.168.35.135 kubenode2 <none> <none> result-app-deployment-b8f9dc967-zbsk2 1/1 Running 8 4d 192.168.35.137 kubenode2 <none> <none> voting-app-deployment-669dccccfb-jpn6h 1/1 Running 8 4d3h 192.168.35.136 kubenode2 <none> <none> voting-app-deployment-669dccccfb-ktd7d 1/1 Running 8 4d 192.168.35.140 kubenode2 <none> <none> voting-app-deployment-669dccccfb-x868p 1/1 Running 8 4d 192.168.205.243 kubenode1 <none> <none> worker-app-deployment-559f7749b6-jh86r 1/1 Running 21 4d3h 192.168.205.248 kubenode1 <none> <none>
Connectez-vous au pod redis-master :
root@kubemaster:~# kubectl exec -it redis-master-8rczl bash [ root@redis-master-8rczl:/data ]$
Essayez de contacter un pod du même tier :
[ root@redis-master-8rczl:/data ]$ ping -c 4 192.168.35.142 PING 192.168.35.142 (192.168.35.142) 56(84) bytes of data. 64 bytes from 192.168.35.142: icmp_seq=1 ttl=62 time=0.402 ms 64 bytes from 192.168.35.142: icmp_seq=2 ttl=62 time=0.301 ms 64 bytes from 192.168.35.142: icmp_seq=3 ttl=62 time=0.291 ms 64 bytes from 192.168.35.142: icmp_seq=4 ttl=62 time=0.395 ms --- 192.168.35.142 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3004ms rtt min/avg/max/mdev = 0.291/0.347/0.402/0.053 ms
Essayez maintenant de contacter un pod d'un tier différent :
[ root@redis-master-8rczl:/data ]$ ping -c 4 192.168.205.250 PING 192.168.205.250 (192.168.205.250) 56(84) bytes of data. --- 192.168.205.250 ping statistics --- 4 packets transmitted, 0 received, 100% packet loss, time 3017ms
Déconnectez-vous du pod redis-master et connectez-vous à un pod frontend :
root@kubemaster:~# kubectl exec -it frontend-762mw bash root@frontend-762mw:/var/www/html#
Essayez de contacter un pod du même tier :
root@frontend-762mw:/var/www/html# ping -c 4 192.168.35.143 PING 192.168.35.143 (192.168.35.143): 56 data bytes 64 bytes from 192.168.35.143: icmp_seq=0 ttl=62 time=0.476 ms 64 bytes from 192.168.35.143: icmp_seq=1 ttl=62 time=0.263 ms 64 bytes from 192.168.35.143: icmp_seq=2 ttl=62 time=0.231 ms 64 bytes from 192.168.35.143: icmp_seq=3 ttl=62 time=0.289 ms --- 192.168.35.143 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.231/0.315/0.476/0.095 ms
Essayez maintenant de contacter un pod d'un tier différent :
root@frontend-762mw:/var/www/html# ping -c 4 192.168.205.249 PING 192.168.205.249 (192.168.205.249): 56 data bytes 64 bytes from 192.168.205.249: icmp_seq=0 ttl=63 time=0.454 ms 64 bytes from 192.168.205.249: icmp_seq=1 ttl=63 time=0.052 ms 64 bytes from 192.168.205.249: icmp_seq=2 ttl=63 time=0.069 ms 64 bytes from 192.168.205.249: icmp_seq=3 ttl=63 time=0.050 ms --- 192.168.205.249 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.050/0.156/0.454/0.172 ms
2.5 - Kubernetes Resource Allocation Management
Les ressources qui peuvent être limitées au niveau d'un pod sont :
- CPU
- Mémoire
- Stockage local
Créez le fichier flask-resources.yaml :
root@kubemaster:~# vi flask-resources.yaml root@kubemaster:~# cat flask-resources.yaml apiVersion: v1 kind: Pod metadata: name: flask-resources namespace: default spec: containers: - image: mateobur/flask name: flask-resources resources: requests: memory: 512Mi limits: memory: 700Mi
Dans ce fichier on peut constater deux allocations de ressources :
- requests,
- la quantité de mémoire qui doit être libre au moment du scheduling du pod,
- limits,
- la limite de mémoire pour le pod concerné.
Exécutez kubectl :
root@kubemaster:~# kubectl create -f flask-resources.yaml pod/flask-resources created
Attendez que le statut du pod soit READY :
root@kubemaster:~# kubectl get pods NAME READY STATUS RESTARTS AGE flask-cap 1/1 Running 1 161m flask-resources 1/1 Running 0 4m47s flask-ro 1/1 Running 0 87m frontend-762mw 1/1 Running 0 39m frontend-lhw8b 1/1 Running 0 39m frontend-n75vs 1/1 Running 0 39m postgres-deployment-5b8bd66778-j99zz 1/1 Running 8 4d3h redis-deployment-67d4c466c4-9wzfn 1/1 Running 8 4d3h redis-master-8rczl 1/1 Running 0 39m redis-slave-c8jzv 1/1 Running 0 39m redis-slave-fjrjm 1/1 Running 0 39m result-app-deployment-b8f9dc967-nzbgd 1/1 Running 8 4d3h result-app-deployment-b8f9dc967-r84k6 1/1 Running 8 4d result-app-deployment-b8f9dc967-zbsk2 1/1 Running 8 4d voting-app-deployment-669dccccfb-jpn6h 1/1 Running 8 4d3h voting-app-deployment-669dccccfb-ktd7d 1/1 Running 8 4d voting-app-deployment-669dccccfb-x868p 1/1 Running 8 4d worker-app-deployment-559f7749b6-jh86r 1/1 Running 21 4d3h
Connectez-vous au pod :
root@kubemaster:~# kubectl exec -it flask-resources bash root@flask-resources:/#
Installez le paquet stress :
root@flask-resources:~# apt install stress Reading package lists... Done Building dependency tree Reading state information... Done The following NEW packages will be installed: stress 0 upgraded, 1 newly installed, 0 to remove and 3 not upgraded. Need to get 18.5 kB of archives. After this operation, 44.0 kB of additional disk space will be used. Get:1 http://deb.debian.org/debian/ jessie/main stress amd64 1.0.1-1+deb8u1 [18.5 kB] Fetched 18.5 kB in 5s (3605 B/s) debconf: delaying package configuration, since apt-utils is not installed Selecting previously unselected package stress. (Reading database ... 9595 files and directories currently installed.) Preparing to unpack .../stress_1.0.1-1+deb8u1_amd64.deb ... Unpacking stress (1.0.1-1+deb8u1) ... Setting up stress (1.0.1-1+deb8u1) ... root@flask-resources:~# which stress /usr/bin/stress
Testez la limite mise en place :
root@flask-resources:~# stress --cpu 1 --io 1 --vm 2 --vm-bytes 800M stress: info: [42] dispatching hogs: 1 cpu, 1 io, 2 vm, 0 hdd stress: FAIL: [42] (416) <-- worker 46 got signal 9 stress: WARN: [42] (418) now reaping child worker processes stress: FAIL: [42] (452) failed run completed in 1s
LAB #3 - Sécuriser les Composants de Kubernetes
3.1 - L'accès à l'API Kubelet
L'accès à l'API Kubelet est sécurisé par défaut. Par contre il convient de vérifier que la configuration ne comporte pas d'erreurs.
Le fichier /etc/systemd/system/kubelet.service.d/10-kubeadm.conf démontre l'emplacement du fichier de configuration de kubelet :
root@kubemaster:~# cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf # Note: This dropin only works with kubeadm and kubelet v1.11+ [Service] Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf" Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml" # This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env # This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use # the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file. EnvironmentFile=-/etc/default/kubelet ExecStart= ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS
La configuration par défaut de kubelet se trouve dans le fichier /var/lib/kubelet/config.yaml :
root@kubemaster:~# cat /var/lib/kubelet/config.yaml address: 0.0.0.0 apiVersion: kubelet.config.k8s.io/v1beta1 authentication: anonymous: enabled: false webhook: cacheTTL: 2m0s enabled: true x509: clientCAFile: /etc/kubernetes/pki/ca.crt authorization: mode: Webhook webhook: cacheAuthorizedTTL: 5m0s cacheUnauthorizedTTL: 30s cgroupDriver: cgroupfs cgroupsPerQOS: true clusterDNS: - 10.96.0.10 clusterDomain: cluster.local configMapAndSecretChangeDetectionStrategy: Watch containerLogMaxFiles: 5 containerLogMaxSize: 10Mi contentType: application/vnd.kubernetes.protobuf cpuCFSQuota: true cpuCFSQuotaPeriod: 100ms cpuManagerPolicy: none cpuManagerReconcilePeriod: 10s enableControllerAttachDetach: true enableDebuggingHandlers: true enforceNodeAllocatable: - pods eventBurst: 10 eventRecordQPS: 5 evictionHard: imagefs.available: 15% memory.available: 100Mi nodefs.available: 10% nodefs.inodesFree: 5% evictionPressureTransitionPeriod: 5m0s failSwapOn: true fileCheckFrequency: 20s hairpinMode: promiscuous-bridge healthzBindAddress: 127.0.0.1 healthzPort: 10248 httpCheckFrequency: 20s imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 80 imageMinimumGCAge: 2m0s iptablesDropBit: 15 iptablesMasqueradeBit: 14 kind: KubeletConfiguration kubeAPIBurst: 10 kubeAPIQPS: 5 makeIPTablesUtilChains: true maxOpenFiles: 1000000 maxPods: 110 nodeLeaseDurationSeconds: 40 nodeStatusReportFrequency: 1m0s nodeStatusUpdateFrequency: 10s oomScoreAdj: -999 podPidsLimit: -1 port: 10250 registryBurst: 10 registryPullQPS: 5 resolvConf: /etc/resolv.conf rotateCertificates: true runtimeRequestTimeout: 2m0s serializeImagePulls: true staticPodPath: /etc/kubernetes/manifests streamingConnectionIdleTimeout: 4h0m0s syncFrequency: 1m0s topologyManagerPolicy: none volumeStatsAggPeriod: 1m0s
En termes de sécurité il est important que la connexion anonyme soit désactivé :
... authentication: anonymous: enabled: false ...
que kubelet utilise un certificat :
... x509: clientCAFile: /etc/kubernetes/pki/ca.crt ...
que le mode d’autorisation soit webhook qui délègue les décisions d’autorisation à l'API Kubernetes
... authorization: mode: Webhook ...
3.2 - Sécuriser etcd
La configuration du cluster y compris la configuration du réseau, du stockage, les mots de passe et des données sensibles sont stockées dans etcd.
Il est possible de crypter les données sensibles, appelées des Secrets, mais cette fonctionnalité n'est pas activée par défaut.
Pour pouvoir procéder au cryptage il faut deux binaires etcd et etcdctl :
root@kubemaster:~# which etcd root@kubemaster:~# which etcdctl
Pour obtenir les deux binaires, téléchargez l'archive etcd :
root@kubemaster:~# export RELEASE="3.3.13" root@kubemaster:~# wget https://github.com/etcd-io/etcd/releases/download/v${RELEASE}/etcd-v${RELEASE}-linux-amd64.tar.gz
Désarchivez l'archive et placez-vous dans le répertoire etcd-v3.3.13-linux-amd64 :
root@kubemaster:~# tar xvf etcd-v${RELEASE}-linux-amd64.tar.gz root@kubemaster:~# cd etcd-v${RELEASE}-linux-amd64
Copiez les binaires etcd et etcdctl vers usr/local/bin :
root@kubemaster:~/etcd-v3.3.13-linux-amd64# cp etcd etcdctl /usr/local/bin root@kubemaster:~/etcd-v3.3.13-linux-amd64# etcd --version etcd Version: 3.3.13 Git SHA: 98d3084 Go Version: go1.10.8 Go OS/Arch: linux/amd64
Vérifiez le chemin de l'emplacement des certificats ainsi que le port d’écoute :
root@kubemaster:~# grep -- '--etcd' /etc/kubernetes/manifests/kube-apiserver.yaml - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key - --etcd-servers=https://127.0.0.1:2379
Vérifiez que le port 2379 soit à l'écoute :
root@kubemaster:~/etcd-v3.3.13-linux-amd64# ss -tunelp | grep 2379 tcp LISTEN 0 128 192.168.56.2:2379 *:* users:(("etcd",pid=5449,fd=7)) ino:22971 sk:8 <-> tcp LISTEN 0 128 127.0.0.1:2379 *:* users:(("etcd",pid=5449,fd=6)) ino:22970 sk:9 <->
Exportez les variables ETCDCTL_CACERT, ETCDCTL_CERT, ETCDCTL_KEY et ETCDCTL_API :
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt export ETCDCTL_CERT=/etc/kubernetes/pki/apiserver-etcd-client.crt export ETCDCTL_KEY=/etc/kubernetes/pki/apiserver-etcd-client.key export ETCDCTL_API=3
Créez le fichier /etc/kubernetes/pki/encryption-config.yaml afin d'utiliser l'algorithme de cryptage AES avec le mode d'opération Enchaînement des blocs :
root@kubemaster:~/etcd-v3.3.13-linux-amd64# vi /etc/kubernetes/pki/encryption-config.yaml root@kubemaster:~/etcd-v3.3.13-linux-amd64# cat /etc/kubernetes/pki/encryption-config.yaml kind: EncryptionConfig apiVersion: v1 resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: c2VjcmV0IGlzIHNlY3VyZQ== - name: key2 secret: dGhpcyBpcyBwYXNzd29yZA== - identity: {}
Activez experimental-encryption-provider-config dans le fichier /etc/kubernetes/manifests/kube-apiserver.yaml :
root@kubemaster:~/etcd-v3.3.13-linux-amd64# vi /etc/kubernetes/manifests/kube-apiserver.yaml root@kubemaster:~/etcd-v3.3.13-linux-amd64# cat /etc/kubernetes/manifests/kube-apiserver.yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: component: kube-apiserver tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - --advertise-address=192.168.56.2 - --allow-privileged=true - --authorization-mode=Node,RBAC - --client-ca-file=/etc/kubernetes/pki/ca.crt - --enable-admission-plugins=NodeRestriction - --enable-bootstrap-token-auth=true - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key - --etcd-servers=https://127.0.0.1:2379 - --insecure-port=0 - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key - --requestheader-allowed-names=front-proxy-client - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt - --requestheader-extra-headers-prefix=X-Remote-Extra- - --requestheader-group-headers=X-Remote-Group - --requestheader-username-headers=X-Remote-User - --secure-port=6443 - --service-account-key-file=/etc/kubernetes/pki/sa.pub - --service-cluster-ip-range=10.96.0.0/12 - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key - --experimental-encryption-provider-config=/etc/kubernetes/pki/encryption-config.yaml
Re-démarrez le server k8s_kube-apiserver pour la prise en compte de la modification du fichier /etc/kubernetes/manifests/kube-apiserver.yaml :
root@kubemaster:~/etcd-v3.3.13-linux-amd64# docker stop $(docker ps | grep k8s_kube-apiserver | gawk '{print $1}') dcec07c13291
Procédez au cryptage de tous les Secrets :
root@kubemaster:~/etcd-v3.3.13-linux-amd64# export KUBECONFIG=/etc/kubernetes/admin.conf root@kubemaster:~/etcd-v3.3.13-linux-amd64# kubectl get secrets --all-namespaces -o json | kubectl replace -f - secret/default-token-jcvl7 replaced secret/default-token-z8n7g replaced secret/default-token-9rdpr replaced secret/attachdetach-controller-token-7r6jg replaced secret/bootstrap-signer-token-rknhr replaced secret/calico-kube-controllers-token-n8crt replaced secret/calico-node-token-272kx replaced secret/certificate-controller-token-x26pf replaced secret/clusterrole-aggregation-controller-token-qxvjj replaced secret/coredns-token-w74l5 replaced secret/cronjob-controller-token-8s7fj replaced secret/daemon-set-controller-token-2mmmg replaced secret/default-token-8ctj2 replaced secret/deployment-controller-token-d4fl4 replaced secret/disruption-controller-token-wbpcd replaced secret/endpoint-controller-token-ldcpj replaced secret/expand-controller-token-wprmr replaced secret/generic-garbage-collector-token-lssw7 replaced secret/horizontal-pod-autoscaler-token-nxp94 replaced secret/job-controller-token-6lnjs replaced secret/kube-proxy-token-rjg4f replaced secret/namespace-controller-token-rfd89 replaced secret/node-controller-token-pb5pq replaced secret/persistent-volume-binder-token-99sft replaced secret/pod-garbage-collector-token-cxsjw replaced secret/pv-protection-controller-token-gkjgf replaced secret/pvc-protection-controller-token-j6jmq replaced secret/replicaset-controller-token-xz7mr replaced secret/replication-controller-token-vhq7f replaced secret/resourcequota-controller-token-bvgx5 replaced secret/service-account-controller-token-lkqwb replaced secret/service-controller-token-bcxft replaced secret/statefulset-controller-token-9r4j9 replaced secret/token-cleaner-token-glqqk replaced secret/ttl-controller-token-bsrvq replaced
<html> <DIV ALIGN=“CENTER”> Copyright © 2020 Hugh Norris </div> </html>