Pourquoi et comment utiliser les journaux d’audit pour sécuriser Kubernetes
De façon générale, Kubernetes ne propose pas de fonctions de surveillance de la sécurité ou de détection des menaces. C’est à vous, en tant qu’administrateur du cluster, de surveiller et de gérer les problèmes de sécurité.
Néanmoins, Kubernetes fournit un outil très utile pour aider à détecter les potentiels événements de sécurité, sous la forme de journaux d’audit. En enregistrant automatiquement les détails relatifs aux demandes d’accès émises vers l’API Kubernetes, les journaux d’audit représentent une ressource centralisée que vous pouvez exploiter pour détecter toute activité suspecte sur l’ensemble de votre cluster.
Cet article définit les journaux d’audit Kubernetes, explique comment les utiliser et fournit un exemple d’utilisation de ces journaux pour suivre les événements de sécurité.
Qu’est-ce qu’un journal d’audit Kubernetes ?
En résumé, un journal d’audit Kubernetes est un journal qui enregistre des informations provenant du service d’audit Kubernetes.
L’objectif des journaux d’audit est d’aider les administrateurs de clusters à suivre les requêtes qui ont été effectuées, de savoir qui en est à l’origine, quelles sont les ressources qui ont été impactées et de connaître le résultat de chaque requête.
Ainsi, en enregistrant et en analysant les données d’audit, vous pouvez bénéficier d’une visibilité précoce des éventuels problèmes de sécurité qui peuvent se produire au sein de votre cluster, comme les demandes d’accès non autorisé aux ressources ou les demandes initiées par des utilisateurs ou des services inconnus. Les journaux d’audit peuvent également être très utiles lorsque vous recherchez une faille de sécurité existante (même si, avec un peu de chance, vous détecterez les problèmes avant !).
Les journaux d’audit n’existent que si vous les créez dans Kubernetes
Même si l’on utilise fréquemment la formulation « journaux d’audit Kubernetes », cette nomenclature est un peu trompeuse, car Kubernetes ne crée pas de journal d’audit spécifique à proprement parler. En d’autres termes, ce n’est pas comme si Kubernetes enregistrait automatiquement tous les événements d’audit dans un fichier spécifique, qu’il suffirait alors d’ouvrir ou d’analyser pour effectuer un suivi des événements de sécurité.
À la place, Kubernetes apportent des solutions que les administrateurs peuvent éventuellement utiliser pour enregistrer des événements de sécurité et les transmettre à un backend de leur choix. Vous pouvez ainsi créer différents types de journaux d’audit dans Kubernetes, mais leur nature exacte dépendra de la configuration que vous avez choisie. De plus, il n’y a pas de journal d’audit par défaut, sauf si vous le configurez au préalable.
Événements et étapes de l’audit Kubernetes
Kubernetes enregistre les données d’audit en s’appuyant sur deux concepts clés : les étapes et les événements d’audit.
Un événement d’audit correspond à toute demande adressée au serveur d’API. Quant aux étapes, elles coïncident aux différents stades que le serveur parcourt lorsqu’il traite chaque demande.
Il y a quatre « étapes » possibles pour chaque événement :
- RequestReceived : à cette étape, le serveur API a reçu la demande, mais n’a pas encore commencé à la traiter.
- ResponseStarted : le serveur a commencé à traiter la demande, mais n’a pas encore envoyé de réponse.
- ResponseComplete : le serveur a terminé le traitement de la demande et a envoyé une réponse.
- Panic : cette étape se produit lorsque le serveur API « panique » en réponse à une demande.
Vous pouvez demander à Kubernetes d’enregistrer des informations dans un journal d’audit pour chaque étape de chaque événement d’audit qui se produit au sein de votre cluster. Vous pouvez ainsi suivre non seulement le moment où des demandes relatives à la sécurité sont émises, mais aussi la façon dont elles sont traitées.
Cette granularité est très utile, car elle vous aide à évaluer la gravité des incidents de sécurité. Exemple : une demande potentiellement malveillante bloquée à l’étape ResponseStarted est moins préoccupante que celle pour laquelle le serveur API renvoie une réponse acceptant la demande. Il est probable que vous vouliez être au courant des deux types d’événements, mais vous donnerez la priorité au second. Avec l’audit de Kubernetes, vous pouvez facilement faire la différence entre les deux.
Format des journaux d’audit
Par défaut, Kubernetes génère des données sur chaque événement d’audit au format JSON. Voici un exemple d’événement que vous pouvez stocker dans un fichier journal :
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1beta1",
"metadata": {
"creationTimestamp": "2018-10-08T08:26:55Z"
},
"level": "Request",
"timestamp": "2018-10-08T08:26:55Z",
"auditID": "288ace59-97ba-4121-b06e-f648f72c3122",
"stage": "ResponseComplete",
"requestURI": "/api/v1/pods?limit=500",
"verb": "list",
"user": {
"username": "admin",
"groups": ["system:authenticated"]
},
"sourceIPs": ["10.0.138.91"],
"objectRef": {
"resource": "pods",
"apiVersion": "v1"
},
"responseStatus": {
"metadata": {},
"code": 200
},
"requestReceivedTimestamp": "2018-10-08T08:26:55.466934Z",
"stageTimestamp": "2018-10-08T08:26:55.471137Z",
"annotations": {
"authorization.k8s.io/decision": "allow",
"authorization.k8s.io/reason": "RBAC: allowed by
ClusterRoleBinding "admin-cluster-binding" of ClusterRole "cluster-
admin" to User "admin""
}
}
Activation de l’audit dans le serveur API Kubernetes
Pour rappel, si Kubernetes apporte des solutions pour enregistrer les événements d’audit, il ne les enregistre pas pour vous par défaut. Vous devez activer et configurer cette fonction si vous voulez générer des journaux d’audit.
Pour ce faire, vous devez spécifier l’emplacement de deux fichiers dans la configuration de votre serveur API :
--audit-policy-file=/etc/kubernetes/audit-policy.yaml \
--audit-log-path=/var/log/audit.log
Ici, audit-policy.yaml est le fichier qui définit les événements d’audit à enregistrer et la façon de les enregistrer, tandis que audit.log est l’emplacement (sur votre nœud master) où les données d’enregistrement sont vraiment stockées.
Définition du fichier de politique d’audit
Il est probable que vous n’ayez pas envie d’enregistrer chaque demande d’API qui se produit au sein de votre cluster. Si vous les enregistriez toutes, vous auriez tellement de données que le bruit généré ne vous permettrait plus de vous y retrouver.
C’est pourquoi vous créez un fichier de politique d’audit. Un fichier de politique d’audit est un fichier au format YAML qui spécifie les événements à enregistrer et la quantité de données à enregistrer pour chacun de ces événements.
Par exemple :
apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy
# Don't generate audit events for all requests in RequestReceived stage.
omitStages:
- "RequestReceived"
rules:
# Log pod changes at RequestResponse level
- level: RequestResponse
resources:
- group: ""
# Resource "pods" doesn't match requests to any subresource of pods,
# which is consistent with the RBAC policy.
resources: ["pods"]
# Only check access to resource "pods"
- level: Metadata
resources:
- group: ""
resources: ["pods/log", "pods/status"]
# Don't log watch requests by the "system:kube-proxy" on endpoints or services
- level: None
users: ["system:kube-proxy"]
verbs: ["watch"]
resources:
- group: "" # core API group
resources: ["endpoints", "services"]
# Don't log authenticated requests to certain non-resource URL paths.
- level: None
userGroups: ["system:authenticated"]
nonResourceURLs:
- "/api*" # Wildcard matching.
- "/version"
# Log the request body of configmap changes in kube-system.
- level: Request
resources:
- group: "" # core API group
resources: ["configmaps"]
# This rule only applies to resources in the "kube-system" namespace.
# The empty string "" can be used to select non-namespaced resources.
namespaces: ["kube-system"]
# Log configmap and secret changes in all other namespaces at the Metadata level.
- level: Metadata
resources:
- group: "" # core API group
resources: ["secrets", "configmaps"]
# A catch-all rule to log all other requests at the Metadata level.
- level: Metadata
# Long-running requests like watches that fall under this rule will not
# generate an audit event in RequestReceived.
omitStages:
- "RequestReceived"
Comme vous pouvez le voir dans les commentaires ci-dessus, ce fichier de politique restreint les types d’événements d’audit qui sont enregistrés. Il ignore les événements de l’étape RequestReceived, par exemple. De plus, il n’effectue le suivi que pour l’accès aux pods.
Détection des événements de sécurité avec les journaux d’audit
Voici un exemple simple qui permet d’illustrer l’audit Kubernetes en action. Imaginez que nous avons créé un fichier de politique d’audit qui ressemble à ce qui suit:
apiVersion: audit.k8s.io/v1beta1
kind: Policy
omitStages:
- "RequestReceived"
Rules:
- level: Request
users: ["admin"]
Resources:
- group: ""
resources: ["*"]
- level: Request
user: ["system:anonymous"]
resources:
- group: ""
resources: ["*"]
Cette configuration d’audit nous permet de détecter lorsqu’un nouvel utilisateur non associé à un rôle ou à un ClusterRole existant émet une demande.
Par exemple, imaginez que l’utilisateur essaie de répertorier les pods avec :
kubectl get pods
Comme l'utilisateur n'a pas l'autorisation de répertorier les pods, le serveur API refusera la demande (voici quelle sera la réponse de kubectl : « Error from server (Forbidden): pods is forbidden: User”).
Dans le même temps, le serveur API enregistrera un événement d’audit qui ressemblera à ceci :
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1beta1",
"metadata": {
"creationTimestamp": "2018-10-08T10:00:20Z"
},
"level": "Request",
"timestamp": "2018-10-08T10:00:20Z",
"auditID": "5fc5eab3-82f5-480f-93d2-79bfb47789f1",
"stage": "ResponseComplete",
"requestURI": "/api/v1/namespaces/default/pods?limit=500",
"verb": "list",
"user": {
"username": "system:anonymous",
"groups": ["system:unauthenticated"]
},
"sourceIPs": ["10.0.141.137"],
"objectRef": {
"resource": "pods",
"namespace": "default",
"apiVersion": "v1"
},
"responseStatus": {
"metadata": {},
"status": "Failure",
"reason": "Forbidden",
"code": 403
},
"requestReceivedTimestamp": "2018-10-08T10:00:20.605009Z",
"stageTimestamp": "2018-10-08T10:00:20.605191Z",
"annotations": {
"authorization.k8s.io/decision": "forbid",
"authorization.k8s.io/reason": ""
}
}
En effectuant le suivi du journal d’audit, les administrateurs peuvent donc détecter la demande, ce qui les alertera sur l’existence d’un compte utilisateur qui ne devrait probablement pas exister.
Tirer le meilleur parti des journaux d’audit Kubernetes
Dans un cluster à grande échelle où le serveur API traite des centaines ou des milliers de requêtes par heure, analyser le journal d’audit manuellement afin de détecter les risques potentiels n’est pas pratique.
Au lieu de cela, vous pouvez transmettre les données du journal des événements à un outil de détection qui peut surveiller automatiquement les événements d’audit et générer des alertes lorsque quelque chose paraît suspect.
Deux façons permettent d’y parvenir :
- Surveillez directement le fichier journal d’audit : vous pouvez configurer votre outil de détection d’intrusion pour surveiller le fichier journal d’audit directement sur votre nœud master. En général, pour que cela fonctionne, vous devez exécutez l’outil sur le nœud master, ce qui n’est pas forcément souhaitable, car cela augmente la charge imposée au master. Bien sûr, vous pourriez essayer de récupérer le fichier journal à partir du nœud master et de l’envoyer à un outil externe à l’aide d’un agent de journalisation qui s’exécute en local. Cependant, cela ne résout pas vraiment le problème, car vous devez toujours exécuter un logiciel supplémentaire, l’agent de journalisation, sur le nœud master.
- Envoyez des événements via HTTP : vous pouvez utiliser des webhooks pour envoyer des données d’événement à un outil de sécurité externe via HTTP. Votre outil de sécurité peut ainsi fonctionner de manière totalement indépendante de votre cluster.
Par exemple, pour transmettre en continu des données d’événements d’audit à Falco, l’outil de sécurité d’exécution open source, vous devez configurer Falco en tant que webhook backend dans votre fichier kube-apiserver :
apiVersion: v1
kind: Config
clusters:
- name: falco
cluster:
server: http://$FALCO_SERVICE_CLUSTERIP:8765/k8s_audit
contexts:
- context:
cluster: falco
user: ""
name: default-context
current-context: default-context
preferences: {}
users: []
Cette configuration vous permet d’utiliser Falco (qui est hébergé sur un serveur de votre choix) pour vous avertir des événements de sécurité au moment où ils se produisent. Vous n’avez pas à vous soucier de surveiller directement le fichier d’audit Kubernetes ou d’exécuter un logiciel de sécurité directement dans votre cluster.
L’audit est l’un des aspects essentiels de toute stratégie de sécurité Kubernetes. Bien que les journaux d’audit (comme beaucoup d’autres choses dans Kubernetes) soient un peu complexes à configurer et à gérer, leur configuration peut également être très poussée. Kubernetes vous permet de contrôler exactement les types de données d’audit que vous enregistrez, l’emplacement où elles sont stockées et la manière dont vous travaillez avec.
Adoptez une approche stratégique pour déterminer les types d’événements d’audit à enregistrer (évitez le bruit !), et intégrez les données d’audit à des outils de détection des intrusions (comme Falco) qui peuvent vous avertir en temps réel des risques potentiels. Vous maximisez ainsi votre capacité à trouver et à corriger les menaces de sécurité relatives à Kubernetes, avant qu’elles ne deviennent des problèmes majeurs.