L'ABC de la sécurité dans Kubernetes: authentification, autorisation, audit



Tôt ou tard, le fonctionnement de tout système pose la question de la sécurité: assurer l'authentification, la séparation des droits, l'audit et autres tâches. De nombreuses solutions ont déjà été créées pour Kubernetes qui peuvent atteindre la conformité aux normes même dans des environnements très exigeants ... Le même matériel est consacré aux aspects de sécurité de base mis en œuvre dans le cadre des mécanismes intégrés de K8. Tout d'abord, il sera utile pour ceux qui commencent à se familiariser avec Kubernetes, comme point de départ pour étudier les problèmes liés à la sécurité.

Authentification


Kubernetes a deux types d'utilisateurs:

  • Comptes de service - comptes gérés par l'API Kubernetes;
  • Utilisateurs - utilisateurs «normaux» contrôlés par des services externes indépendants.

La principale différence entre ces types est que pour les comptes de service, il existe des objets spéciaux dans l'API Kubernetes (ils sont appelés ServiceAccounts ) qui sont liés à l'espace de noms et à l'ensemble de données d'autorisation stockées dans le cluster dans des objets de type Secrets. Ces utilisateurs (comptes de service) sont principalement destinés à gérer les droits d'accès aux processus de l'API Kubernetes exécutés dans un cluster Kubernetes.

Les utilisateurs ordinaires n'ont pas d'entrées dans l'API Kubernetes: elles doivent être gérées par des mécanismes externes. Ils sont destinés aux personnes ou processus vivant en dehors du cluster.

Chaque demande à l'API est liée soit au compte de service, soit à l'utilisateur, ou est considérée comme anonyme.

Les données d'authentification des utilisateurs incluent:

  • Nom d'utilisateur - nom d'utilisateur (sensible à la casse!);
  • L'UID est une chaîne d'identification d'utilisateur lisible par machine qui est «plus cohérente et unique que le nom d'utilisateur»;
  • Groupes - une liste des groupes auxquels l'utilisateur appartient;
  • Extra - champs supplémentaires pouvant être utilisés par le mécanisme d'autorisation.

Kubernetes peut utiliser un grand nombre de mécanismes d'authentification: certificats X509, jetons de support, procurations d'authentification, HTTP Basic Auth. En utilisant ces mécanismes, un grand nombre de schémas d'autorisation peuvent être mis en œuvre: d'un fichier statique avec des mots de passe à OpenID OAuth2.

De plus, plusieurs régimes d'autorisation sont autorisés en même temps. Par défaut, le cluster utilise:

  • jetons de compte de service - pour les comptes de service;
  • X509 - pour les utilisateurs.

La question sur la gestion des ServiceAccounts dépasse le cadre de cet article, mais je recommande de commencer à en savoir plus sur ce problème à partir de la page de documentation officielle . Nous examinerons plus en détail la question du travail des certificats X509.

Certificats pour les utilisateurs (X.509)


La manière classique de travailler avec des certificats implique:

  • génération de clés:

     mkdir -p ~/mynewuser/.certs/ openssl genrsa -out ~/.certs/mynewuser.key 2048 
  • génération de demande de certificat:

     openssl req -new -key ~/.certs/mynewuser.key -out ~/.certs/mynewuser.csr -subj "/CN=mynewuser/O=company" 
  • traitement de la demande de certificat à l'aide des clés d'autorité de certification du cluster Kubernetes, obtention d'un certificat utilisateur (pour obtenir un certificat, vous devez utiliser un compte qui a accès à la clé d'autorité de certification du cluster Kubernetes, qui se trouve dans /etc/kubernetes/pki/ca.key par défaut):

     openssl x509 -req -in ~/.certs/mynewuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out ~/.certs/mynewuser.crt -days 500 
  • création d'un fichier de configuration:
    • description du cluster (spécifiez l'adresse et l'emplacement du fichier de certificat CA de l'installation de cluster particulière):

       kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443 
    • ou - si ce n'est pas l' option recommandée - vous pouvez omettre le certificat racine (alors kubectl ne vérifiera pas l'exactitude du cluster api-server):

       kubectl config set-cluster kubernetes --insecure-skip-tls-verify=true --server=https://192.168.100.200:6443 
    • ajout d'un utilisateur au fichier de configuration:

       kubectl config set-credentials mynewuser --client-certificate=.certs/mynewuser.crt --client-key=.certs/mynewuser.key 
    • ajout de contexte:

       kubectl config set-context mynewuser-context --cluster=kubernetes --namespace=target-namespace --user=mynewuser 
    • affectation de contexte par défaut:

       kubectl config use-context mynewuser-context 

Après les manipulations ci-dessus, une configuration du formulaire sera créée dans le fichier .kube/config :

 apiVersion: v1 clusters: - cluster: certificate-authority: /etc/kubernetes/pki/ca.crt server: https://192.168.100.200:6443 name: kubernetes contexts: - context: cluster: kubernetes namespace: target-namespace user: mynewuser name: mynewuser-context current-context: mynewuser-context kind: Config preferences: {} users: - name: mynewuser user: client-certificate: /home/mynewuser/.certs/mynewuser.crt client-key: /home/mynewuser/.certs/mynewuser.key 

Pour faciliter le transfert de la configuration entre les comptes et les serveurs, il est utile de modifier les valeurs des clés suivantes:

  • certificate-authority
  • client-certificate
  • client-key

Pour ce faire, vous pouvez encoder les fichiers qui y sont indiqués en utilisant base64 et les enregistrer dans la configuration en ajoutant le suffixe -data au nom des clés, c'est-à-dire obtenir certificate-authority-data , etc.

Certificats avec kubeadm


Avec la sortie de Kubernetes 1.15, travailler avec des certificats est devenu beaucoup plus facile grâce à la version alpha de son support dans l'utilitaire kubeadm . Par exemple, voici à quoi pourrait ressembler la génération d'un fichier de configuration avec des clés utilisateur:

 kubeadm alpha kubeconfig user --client-name=mynewuser --apiserver-advertise-address 192.168.100.200 

NB : L' adresse de publicité requise peut être consultée dans la configuration d'api-server, qui se trouve par défaut dans /etc/kubernetes/manifests/kube-apiserver.yaml .

La configuration résultante sera sortie sur stdout. Il doit être enregistré dans ~/.kube/config compte utilisateur ou dans le fichier spécifié dans la KUBECONFIG environnement KUBECONFIG .

Creusez plus profondément


Pour ceux qui souhaitent bien comprendre les problèmes décrits:


Se connecter


Un compte authentifié n'est pas autorisé à agir dans un cluster par défaut. Kubernetes dispose d'un mécanisme d'autorisation pour accorder des autorisations.

Avant la version 1.6, Kubernetes utilisait un type d'authentification appelé ABAC (contrôle d'accès basé sur les attributs). Des détails à ce sujet peuvent être trouvés dans la documentation officielle . Cette approche est actuellement considérée comme héritée, mais vous pouvez toujours l'utiliser en même temps que d'autres types d'autorisation.

La façon actuelle (et plus flexible) de diviser les droits d'accès aux clusters est appelée RBAC ( contrôle d'accès basé sur les rôles ). Il a été déclaré stable depuis Kubernetes 1.8 . RBAC met en œuvre un modèle de droits qui interdit tout ce qui n'est pas explicitement autorisé.
Pour activer RBAC , vous devez exécuter Kubernetes api-server avec l' --authorization-mode=RBAC . Les paramètres sont définis dans le manifeste avec la configuration api-server, qui par défaut se trouve sur le chemin /etc/kubernetes/manifests/kube-apiserver.yaml , dans la section des command . Cependant, RBAC est déjà activé par défaut, vous ne devriez donc pas vous en soucier: vous pouvez le vérifier par la valeur du authorization-mode d' authorization-mode (dans le kube-apiserver.yaml déjà mentionné). Soit dit en passant, parmi ses valeurs, il peut y avoir d'autres types d'autorisation ( node , webhook , always allow ), mais nous les laisserons en dehors de la portée du matériel.

Soit dit en passant, nous avons déjà publié un article avec une histoire assez détaillée sur les principes et les caractéristiques de travailler avec RBAC, donc je me limiterai à une brève liste des bases et des exemples.

Les entités API suivantes sont utilisées pour contrôler l'accès à Kubernetes via RBAC:

  • Role et ClusterRole sont des rôles qui décrivent les privilèges:
  • Role vous permet de décrire les droits dans un espace de noms;
  • ClusterRole - au sein du cluster, y compris les objets spécifiques au cluster tels que les nœuds, les URL non liées aux ressources (c'est-à-dire non liés aux ressources Kubernetes - par exemple, /version , /logs , /api* );
  • RoleBinding et ClusterRoleBinding - sert à lier Role et ClusterRole à un utilisateur, un groupe d'utilisateurs ou un ServiceAccount.

Le rôle des entités et la liaison de rôles sont limités par l'espace de noms, c'est-à-dire doit être dans le même espace de noms. Cependant, RoleBinding peut faire référence à ClusterRole, qui vous permet de créer un ensemble d'autorisations standard et de contrôler l'accès à l'aide de celles-ci.

Les rôles décrivent les droits à l'aide d'ensembles de règles contenant:

  • Groupes d'API - voir la documentation officielle des apiGroups et la sortie des kubectl api-resources de kubectl api-resources ;
  • ressources ( ressources : pod , namespace , deployment , etc.);
  • verbes ( verbes : set , update , etc.).
  • noms de ressource ( resourceNames ) - pour le cas où vous devez fournir un accès à une ressource spécifique, et non à toutes les ressources de ce type.

Une discussion plus détaillée de l'autorisation dans Kubernetes peut être trouvée sur la page de documentation officielle. Au lieu de cela (ou plutôt, en plus de cela), je donnerai des exemples qui illustrent son travail.

Exemples d'entités RBAC


Un Role simple qui vous permet d'obtenir la liste et l'état des pods et de les surveiller dans l' target-namespace :

 apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: target-namespace name: pod-reader rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"] 

Un exemple de ClusterRole , qui vous permet d'obtenir une liste et l'état des pods et de les surveiller dans tout le cluster:

 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: #  "namespace" ,   ClusterRole    name: pod-reader rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"] 

Exemple RoleBinding , qui permet à l'utilisateur mynewuser "lire" des pods dans l' my-namespace :

 apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods namespace: target-namespace subjects: - kind: User name: mynewuser #     ! apiGroup: rbac.authorization.k8s.io roleRef: kind: Role #    “Role”  “ClusterRole” name: pod-reader #  Role,      namespace, #   ClusterRole,   #    apiGroup: rbac.authorization.k8s.io 

Audit d'événement


Schématiquement, l'architecture de Kubernetes peut être représentée comme suit:

image

Le composant clé de Kubernetes, qui est responsable du traitement des demandes, est api-server . Toutes les opérations sur le cluster le traversent. Pour en savoir plus sur ces mécanismes internes, consultez l'article « Que se passe-t-il dans Kubernetes lorsque l'exécution de kubectl démarre? ".

L'audit système est une fonctionnalité intéressante de Kubernetes, qui est désactivée par défaut. Il vous permet de consigner tous les appels vers l'API Kubernetes. Comme vous pouvez facilement le deviner, grâce à cette API, toutes les actions liées à la surveillance et à la modification de l'état du cluster sont effectuées. Une bonne description de ses fonctionnalités se trouve (comme d'habitude) dans la documentation officielle de K8s. Ensuite, je vais essayer de présenter le sujet dans un langage plus simple.

Donc, pour activer l'audit , nous devons passer trois paramètres requis au conteneur dans l'api-server, plus sur ce qui est décrit ci-dessous:

  • --audit-policy-file=/etc/kubernetes/policies/audit-policy.yaml
  • --audit-log-path=/var/log/kube-audit/audit.log
  • --audit-log-format=json

En plus de ces trois paramètres nécessaires, il existe de nombreux paramètres supplémentaires liés à l'audit: de la rotation des journaux aux descriptions des webhooks. Exemples de paramètres de rotation des journaux:

  • --audit-log-maxbackup=10
  • --audit-log-maxsize=100
  • --audit-log-maxage=7

Mais nous ne nous attarderons pas sur eux plus en détail - vous pouvez trouver tous les détails dans la documentation de kube-apiserver .

Comme déjà mentionné, tous les paramètres sont définis dans le manifeste avec la configuration api-server (par défaut /etc/kubernetes/manifests/kube-apiserver.yaml ), dans la section des command . Revenons aux 3 paramètres requis et analysons-les:

  1. audit-policy-file - chemin d'accès au fichier YAML avec la description de la politique d'audit. Nous reviendrons sur son contenu, mais pour l'instant je note que le fichier doit être accessible à la lecture par le processus api-server. Par conséquent, il est nécessaire de le monter à l'intérieur du conteneur, pour lequel vous pouvez ajouter le code suivant aux sections appropriées de la configuration:

      volumeMounts: - mountPath: /etc/kubernetes/policies name: policies readOnly: true volumes: - hostPath: path: /etc/kubernetes/policies type: DirectoryOrCreate name: policies 
  2. audit-log-path - chemin d'accès au fichier journal. Le chemin devrait également être accessible au processus api-server, par conséquent, nous décrivons de la même manière son montage:

      volumeMounts: - mountPath: /var/log/kube-audit name: logs readOnly: false volumes: - hostPath: path: /var/log/kube-audit type: DirectoryOrCreate name: logs 
  3. audit-log-format - format du journal d'audit. Par défaut, il s'agit de json , mais le format de texte hérité est également disponible.

Politique d'audit


Maintenant sur le fichier mentionné avec la description de la politique de journalisation. Le premier concept de politique d'audit est le level , le niveau de journalisation . Ils sont les suivants:

  • None - ne vous connectez pas;
  • Metadata - enregistrer les métadonnées de demande: utilisateur, heure de la demande, ressource cible (pod, espace de noms, etc.), type d'action (verbe), etc.
  • Request - enregistrer les métadonnées et le corps de la demande;
  • RequestResponse - RequestResponse métadonnées, le corps de la demande et le corps de la réponse.

Les deux derniers niveaux ( Request et RequestResponse ) RequestResponse pas les requêtes qui n'ont pas accédé aux ressources (appels à des URL dites non-ressources).

De plus, toutes les demandes passent par plusieurs étapes :

  • RequestReceived - l'étape à laquelle la demande est reçue par le gestionnaire et n'a pas encore été transférée le long de la chaîne de gestionnaires;
  • ResponseStarted - en-têtes de réponse envoyés, mais avant d'envoyer le corps de la réponse. Généré pour les longues requêtes (par exemple, watch );
  • ResponseComplete - corps de réponse envoyé, plus d'informations ne seront pas envoyées;
  • Panic - des événements sont générés lorsqu'une urgence est détectée.

Vous pouvez utiliser omitStages pour ignorer toutes les étapes.

Dans le fichier de stratégie, nous pouvons décrire plusieurs sections avec différents niveaux de journalisation. La première règle de correspondance trouvée dans la description de la politique s'applique.

Le démon kubelet surveille la modification du manifeste avec la configuration du serveur api, et s'il en détecte, il redémarre le conteneur avec le serveur api. Mais il y a un détail important: ils ignoreront les modifications apportées au fichier de stratégie . Après avoir apporté des modifications au fichier de stratégie, vous devrez redémarrer manuellement api-server. Étant donné qu'api-server fonctionne comme un pod statique , la commande kubectl delete ne le redémarrera pas. Vous devrez manuellement docker stop Docker sur les maîtres de cube où la stratégie d'audit a été modifiée:

 docker stop $(docker ps | grep k8s_kube-apiserver | awk '{print $1}') 

Lorsque vous activez l'audit, il est important de se rappeler que kube-apiserver a une charge plus élevée . En particulier, la consommation de mémoire pour stocker le contexte des requêtes augmente. La journalisation démarre uniquement après l'envoi de l'en-tête de réponse. En outre, la charge dépend de la configuration de la stratégie d'audit.

Exemples de politiques


Analysons la structure des fichiers de stratégie à l'aide d'exemples.

Voici un fichier de policy simple pour tout enregistrer au niveau des Metadata :

 apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata 

Vous pouvez spécifier une liste d'utilisateurs ( Users et ServiceAccounts ) et de groupes d'utilisateurs dans la stratégie. Par exemple, voici comment nous allons ignorer les utilisateurs du système, mais enregistrer tout le reste au niveau de la Request :

 apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: None userGroups: - "system:serviceaccounts" - "system:nodes" users: - "system:anonymous" - "system:apiserver" - "system:kube-controller-manager" - "system:kube-scheduler" - level: Request 

Il est également possible de décrire la cible:

  • namespaces
  • verbes ( verbes : get , update , delete et autres);
  • ressources ( ressources , à savoir: pod , configmaps , etc.) et groupes de ressources ( apiGroups ).

Faites attention! Les ressources et groupes de ressources (groupes API, c'est-à-dire apiGroups), ainsi que leurs versions installées dans le cluster, peuvent être obtenus à l'aide des commandes:

 kubectl api-resources kubectl api-versions 

La stratégie d'audit suivante est fournie à titre de démonstration des meilleures pratiques dans la documentation d'Alibaba Cloud :

 apiVersion: audit.k8s.io/v1beta1 kind: Policy #    RequestReceived omitStages: - "RequestReceived" rules: #   ,     : - level: None users: ["system:kube-proxy"] verbs: ["watch"] resources: - group: "" #  api group   ,    #   Kubernetes,  “core” resources: ["endpoints", "services"] - level: None users: ["system:unsecured"] namespaces: ["kube-system"] verbs: ["get"] resources: - group: "" # core resources: ["configmaps"] - level: None users: ["kubelet"] verbs: ["get"] resources: - group: "" # core resources: ["nodes"] - level: None userGroups: ["system:nodes"] verbs: ["get"] resources: - group: "" # core resources: ["nodes"] - level: None users: - system:kube-controller-manager - system:kube-scheduler - system:serviceaccount:kube-system:endpoint-controller verbs: ["get", "update"] namespaces: ["kube-system"] resources: - group: "" # core resources: ["endpoints"] - level: None users: ["system:apiserver"] verbs: ["get"] resources: - group: "" # core resources: ["namespaces"] #     read-only URLs: - level: None nonResourceURLs: - /healthz* - /version - /swagger* #   ,     “”: - level: None resources: - group: "" # core resources: ["events"] #   Secret, ConfigMap  TokenReview    , #         - level: Metadata resources: - group: "" # core resources: ["secrets", "configmaps"] - group: authentication.k8s.io resources: ["tokenreviews"] #   get, list  watch   ;    - level: Request verbs: ["get", "list", "watch"] resources: - group: "" # core - group: "admissionregistration.k8s.io" - group: "apps" - group: "authentication.k8s.io" - group: "authorization.k8s.io" - group: "autoscaling" - group: "batch" - group: "certificates.k8s.io" - group: "extensions" - group: "networking.k8s.io" - group: "policy" - group: "rbac.authorization.k8s.io" - group: "settings.k8s.io" - group: "storage.k8s.io" #        API - level: RequestResponse resources: - group: "" # core - group: "admissionregistration.k8s.io" - group: "apps" - group: "authentication.k8s.io" - group: "authorization.k8s.io" - group: "autoscaling" - group: "batch" - group: "certificates.k8s.io" - group: "extensions" - group: "networking.k8s.io" - group: "policy" - group: "rbac.authorization.k8s.io" - group: "settings.k8s.io" - group: "storage.k8s.io" #         - level: Metadata 


Un autre bon exemple de politique d'audit est le profil utilisé dans GCE .

Pour une réponse rapide aux événements d'audit, il est possible de décrire un webhook . Ce problème est divulgué dans la documentation officielle , je le laisserai en dehors du champ d'application de cet article.

Résumé


L'article fournit une vue d'ensemble des mécanismes de sécurité de base dans les clusters Kubernetes qui vous permettent de créer des comptes d'utilisateurs personnalisés, de partager leurs droits et d'enregistrer leurs actions. J'espère que cela sera utile à ceux qui sont confrontés à de telles questions en théorie ou déjà en pratique. Je vous recommande également de consulter la liste d'autres documents sur le thème de la sécurité dans Kubernetes, qui est répertoriée dans le "PS" - peut-être y trouverez-vous les détails nécessaires sur les questions qui vous concernent.

PS


Lisez aussi dans notre blog:

Source: https://habr.com/ru/post/fr468679/


All Articles