Toutes les communication entre les différents composants,
tels que le etcd, le manager, le scheduler, le serveur API et les nodes, sont sécurisés à l’aide de TLS.
Pour assurer la sécurité, la première ligne de défense va être de controler l’accès aux machines elles-mêmes.
Tous les accès aux noeuds doivent être sécurisés, l’authentification root désactivée, l’authentification par mot de passe désactivée et seule l’authentification par clé SSH doit être disponible.
Et la communication entre les applications peut être restreinte à l’aide des NetworkPolicy.
La seconde ligne de défense, va être de limiter qui a accès au cluster k8s et ce qu’il a le droit de faire — par exemple qui a le droit de créer des pods. Le serveur kube-api est au centre de tout, et est contrôlé par les mécanismes d’authentification et d’autorisation respectivement.
On peut accéder à l’API sur le noeud master, au port 6443 par défaut.
Il existe différentes manières de s’authentifier auprès du serveur API:
Ni l’authentification Basic ou Bearer token ne sont recommendés puisque non securisés.
Cette approche est d’ailleurs dépréciée dans la version 1.19 de K8s et n’est plus disponible dans les versions ultérieures.
Pour accéder à l’API, il est nécessaire de s’identifier. On peut le faire en utilisant un certificat:
$ kubectl cluster-info
Kubernetes control plane is running at https://172.30.1.2:6443
CoreDNS is running at https://172.30.1.2:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
$ curl https://172.30.1.2:6443/version \
--key /etc/kubernetes/pki/apiserver-kubelet-client.key \
--cert /etc/kubernetes/pki/apiserver-kubelet-client.crt \
--cacert /etc/kubernetes/pki/ca.crt
{
"major": "1",
"minor": "29",
"gitVersion": "v1.29.0",
"gitCommit": "3f7a50f38688eb332e2a1b013678c6435d539ae6",
"gitTreeState": "clean",
"buildDate": "2023-12-13T08:45:03Z",
"goVersion": "go1.21.5",
"compiler": "gc",
"platform": "linux/amd64"
}
Quand on utilise kubectl, il n’est pas nécessaire de spécifier un certificat puisque
l’emplacement du certificat est sauvegardé dans un fichier de configuration (kubeconfig).
Une solution alternative pour utiliser le certificat de kubectl tout en utilisant curl est de démarrer un proxy kubectl: la commande kubectl proxy
lance un proxy localement, qui transmet les requêtes au serveur API en utilisant les informations d’identification et certificats du fichier kubeconfig.
$ kubectl proxy
Starting to serve on 127.0.0.1:8001
^Z
[1]+ Stopped kubectl proxy
controlplane $ bg
[1]+ kubectl proxy &
controlplane $ curl http://127.0.0.1:8001/version
{
"major": "1",
"minor": "29",
"gitVersion": "v1.29.0",
"gitCommit": "3f7a50f38688eb332e2a1b013678c6435d539ae6",
"gitTreeState": "clean",
"buildDate": "2023-12-13T08:45:03Z",
"goVersion": "go1.21.5",
"compiler": "gc",
"platform": "linux/amd64"
Pour utiliser l’authentification Basic:
Créer un fichier CSV avec 3 colonnes: password, username, userid.
On peut éventuellement avoir une 4ème colonne, qui spécifie le groupe de l’utilisateur
password123,user1,u0001
password123,user2,u0002
password123,user3,u0003
password123,user4,u0004
password123,user1,u0001,group1
password123,user2,u0002,group1
password123,user3,u0003,group2
password123,user4,u0004,group2
Modifier les configurations de kube-apiserver
et ajouter l’option --basic-auth-file=/tmp/users/user-details.csv
à la commande
$ vim /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --authorization-mode=Node,RBAC
<content-hidden>
- --basic-auth-file=/tmp/users/user-details.csv
Il faudra s’authentifier auprès du serveur en utilisant les credentials d’un utilisateur
curl -v -k https://localhost:6443/api/v1/pods -u "user1:password123"
Même principe qu’une authentification Basic, créer un fichier CSV avec 3 ou 4 colonnes.
À la place du mot de passe mettre un token
token1,user1,u0001,group1
token2,user2,u0002,group1
token3,user3,u0003,group2
token4,user4,u0004,group2
Modifier les configurations de kube-apiserver
et ajouter l’option --token-auth-file=/tmp/users/user-token-details.csv
à la commande
Passer les credentials dans les entêtes
curl -v -k https://localhost:6443/api/v1/pods --header "Authorization: Bearer token1"
K8s ne gère pas les comptes utilisateur, il s’appuie sur une source externe telle qu’un fichier contenant les détails des utilisateurs, un certificat ou un service d’identité tel que LDAP. En revanche il gère les comptes service: on peut créer un service account directement via k8s.
Une fois authentifié, ce que l’utilisateur a le droit de faire ou non
est définit par les mécanismes d’autorisation.
L’autorisation peut être mise en oeuvre à l’aide de
ABAC (attribute-based access control)
On attribue à un utilisateur ou à un groupe d’utilisateurs un ensemble de permissions via un fichier de configuration en JSON. Chaque fois qu’on veut apporter une modification, il faut éditer ce fichier manuellement et redémarrer le serveur KubeApi. Cette approche est peu pratique
RBAC (role-based access control)
On crée un rôle auquel on donne des permissions. Ensuite, on associe un utilisateur à un rôle.
Quand on veut apporter une modification, il suffit de modifier le rôle, ce qui se repércute immédiatement sur les utilisateurs. Cette approche est plus standard pour gérer les accès au sein du cluster Kubernetes
WEBHOOK
On externalise les mécanismes d’autorisation. Par exemple, Open Policy Agent est un outil tiers qui aide au contrôle d’admission et d’autorisation. On demande à k8s d’effectuer un appel API à l’agent avec les informations sur l’utilisateur et ses exigences d’accès. L’agent décide si l’utilisateur doit être autorisé ou non et c’est sur cette base que k8s accorde l’accès ou non
NODE
Toute requête provenant de l’utilisateur system-node et faisant partie du groupe system-nodes est autorisé et se voit accordé les privilèges requis pour kubelet
AlwaysAllow
Autorise toutes les demandes sans effectuer de contrôle d’autorisation
AlwaysDeny
Refuse toutes les requetes
Le mode d’autorisation utilisé est définit par l’option authorization-mode
du serveur KubeAPI.
On peut activer plusieurs types en les séparant par des virgules.
Si non définit, la valeur par défaut est AlwaysAllow
Si plusieurs modes sont activés: les modes sont testés dans l’ordre.
Lorsqu’un mode refuse l’autorisation, le prochain mode est testé.
Et dès qu’un mode approuve la requête, l’utilisateur reçoit l’autorisation.
Pour définir des permissions, la première chose à comprendre est la notion de groupes API (apiGroups).
Les dernières fonctionnalités k8s sont disponibles à travers des groupes nommés: par exemple NetworkPolicy fait partie du groupe networking.k8s.io. Les fonctionnalités historiques quant à elles ne sont pas dans un groupe nommé: on dit qu’elle font partie du groupe coeur.
Pour lister tous les groupes:
Pour retrouver les différentes ressources dans un groupe:
L’objet Role est la première étape pour le mode RBAC:
On crée un rôle et on lui donne des permissions d’effectuer des actions données sur des ressources données.
Un rôle peut contenir plusieurs règles et chaque règle contient 3 sections:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list", "get", "create", "update", "delete"]
- apiGroups: [""]
resources: ["ConfigMap"]
verbs: ["create"]
Les roles et (rolebindings) sont limité à un namespace. S’il n’est pas spécifié, alors il s’agit du namespace par défaut. On peut spécifier un namespace différent dans les metadata du fichier de définition
Si on veut que l’utilisateur ait accès à certaines ressources mais pas toutes,
on peut ajouter une section resourceNames à la règle
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "create", "update"]
resourceNames: ["blue", "orange"]
On peut également créer un rôle via une commande impérative:
$ kubectl create role developer --namespace=default --verb=list,create,delete --resource=pods
role.rbac.authorization.k8s.io/developer created
Les différents verbes sont:
Verb | HTTP | Description |
---|---|---|
create | POST | Créer une nouvelle ressource |
delete | DELETE | Supprimer une ressource |
get | GET | Récupérer une ressource |
list | LIST | Lister des ressources |
patch | PATCH | Modifier une ressource existante via une mise à jour partielle |
update | PUT | Modifier une ressource existante via un objet complet |
watch | GET | Streamer les mises à jour d’une ressource |
proxy | GET | Se connecter à une ressource via un streaming WebSocket proxy. |
$ k get roles --all-namespaces
NAMESPACE NAME CREATED AT
blue developer 2024-02-02T21:02:11Z
kube-public kubeadm:bootstrap-signer-clusterinfo 2024-02-02T20:53:37Z
$ k describe role kube-proxy -n kube-system
Name: kube-proxy
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
configmaps [] [kube-proxy] [get]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: devuser-developer-binding
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: User
name: dev-user
apiGroup: rbac.authorization.k8s.io
On peut également utiliser une commande:
$ k create rolebinding dev-user-binding --namespace=default --role=developer --user=dev-user
rolebinding.rbac.authorization.k8s.io/dev-user-binding created
$ k get rolebinding --all-namespaces
NAMESPACE NAME ROLE AGE
blue dev-user-binding Role/developer 4m16s
$ kubectl describe rolebinding kube-proxy -n kube-system
Name: kube-proxy
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: kube-proxy
Subjects:
Kind Name Namespace
---- ---- ---------
Group system:bootstrappers:kubeadm:default-node-token
Pour vérifier si on est autorisé à effectuer une action:
$ kubectl auth can-i create deployments
no
Un administrateur peut vérifier pour un autre utilisateur avec l’action --as:
# User
$ kubectl auth can-i create deployments --as dev-user
no
$ kubectl get pods --as dev-user
Error from server (Forbidden): pods is forbidden: User "dev-user" cannot list resource "pods" in API group "" in the namespace "default"
# Serviceaccount
$ kubectl auth can-i create deployments --as system:serviceaccount:ns1:pipeline
no
Les Role et RoleBinding sont limités à un namespace.
Pour autoriser les utilisateurs à accéder à des ressources sans namespace (comme les volumes), ou pour autoriser les utilisateurs à accéder aux ressources de tout namespace, on utilise des ClusterRole et ClusterRoleBinding
La déclaration est la même à la différence près du type utilisé:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-administrator
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list", "get", "create", "delete"]
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-admin-role-binding
subjects:
- kind: User
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-administrator
apiGroup: rbac.authorization.k8s.io
Même chose pour les commandes:
$ k create clusterrole access-nodes --resource=nodes --verb=list,get
clusterrole.rbac.authorization.k8s.io/access-nodes created
$ k create clusterrolebinding access-nodes --user=michelle --clusterrole=access-nodes
clusterrolebinding.rbac.authorization.k8s.io/access-nodes created
Pour voir la liste des ressources avec ou sans namespace:
$ kubectl api-resources --namespaced=true
$ kubectl api-resources --namespaced=false