From 2a2c71f86c90af5945bc4790fb78055d2fde1c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20Gourv=C3=A9nec?= Date: Tue, 8 Apr 2025 16:22:00 +0200 Subject: [PATCH] bb --- wazuh.md | 389 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 354 insertions(+), 35 deletions(-) diff --git a/wazuh.md b/wazuh.md index f4cd9cb..5eb8470 100644 --- a/wazuh.md +++ b/wazuh.md @@ -6,7 +6,7 @@ Il y a 2 sources de logs qui nous intéressent : - les logs des conteneurs des pods ; - l'audit de l'apiserver. -Malheureusement (?) il n'est pas possible de récupérer les logs des pods à travers l'audit de l'apiserver (à part de façon bancale en interrogeant l'endpoint /logs mais ce n'est pas viable). +Il n'est pas possible de récupérer les logs des pods à travers l'audit de l'apiserver (à part peut-être de façon bancale en interrogeant l'endpoint /logs mais ce n'est pas viable). L'audit des logs de conteneurs de pods a plusieurs objectifs : - l'aide au débogage des applications tournant dans le cluster (ex: portail) @@ -18,30 +18,221 @@ L'audit de l'apiserveur a plusieurs objectifs : - l'aide au débogage en cas de grave disfonctionnement du cluster +## Audit de l'apiserver -Piste 1 : installer sur chaque noeud le client wazuh et créer un objet k8s de type audit policy. --> le client wazuh n'est pas fait pour. Après recherches et discussion sur le slack de Wazuh, il semblerait qu'aucun agent wazuh ne supporte un environnement k8s. +### Solutions explorées -Piste 2 : installer sur le serveur wazuh un listener et créer un objet k8s de type audit policy. +2 solutions ont été explorées : +1. Installer sur chaque noeud le client wazuh et créer un objet k8s de type audit policy ; +2. Installer sur le serveur wazuh un listener et créer un objet k8s de type audit policy. -Piste 3 : https://kubernetes.io/docs/concepts/cluster-administration/logging/ : daemonSet avec un agent Wazuh et /var/log/pods monté +La solution 1 n'est pas envisageable, les noeuds étant immutables. -## Lab local pour la piste 2 +Après recherches et discussion sur le slack de Wazuh, il semblerait qu'aucun agent wazuh ne supporte un environnement k8s et que la solution 2 soit la meilleure solution. -Pour un test local, suivre les docs suivantes : +### Lab local + +Pour un test local, la manière la plus simple est de suivre les docs suivantes : - https://documentation.wazuh.com/4.11/deployment-options/docker/wazuh-container.html#single-node-deployment - https://wazuh.com/blog/auditing-kubernetes-with-wazuh/ -Avec quelques modifications cependant pour ajouter un listener sur le port 1580 dans le conteneur de wazuh, ainsi qu'un script pour écouter sur ce port et envoyer les logs à la socket unix de wazuh. +avec quelques modifications cependant pour ajouter un listener sur le port 1580 dans le conteneur de wazuh afin d'envoyer les logs à la socket unix de wazuh. -### Installation du serveur wazuh +Attention : cette procédure est un POC largement améliorable. Cela reste avant tout une piste de démarrage pour travailler sur le sujet. -Dans le docker compose.yml, ajouter : +#### Installation du serveur wazuh + +Suivre le tuto https://documentation.wazuh.com/4.11/deployment-options/docker/wazuh-container.html#single-node-deployment pour la génération des certificats. + +Ajouter localement les fichiers suivants : +- custom-webhook.py + ``` +#!/var/ossec/framework/python/bin/python3 +import json +from socket import socket, AF_UNIX, SOCK_DGRAM +from flask import Flask, request +# CONFIG +PORT = 1580 +CERT = '/var/ossec/api/configuration/ssl/server.crt' +CERT_KEY = '/var/ossec/api/configuration/ssl/server.key' +# Analysisd socket address +socket_addr = '/var/ossec/queue/sockets/queue' +def send_event(msg): + string = '1:k8s:{0}'.format(json.dumps(msg)) + sock = socket(AF_UNIX, SOCK_DGRAM) + sock.connect(socket_addr) + sock.send(string.encode()) + sock.close() + return True +app = Flask(__name__) +context = (CERT, CERT_KEY) +@app.route('/', methods=['POST']) +def webhook(): + if request.method == 'POST': + if send_event(request.json): + print("Request sent to Wazuh") + else: + print("Failed to send request to Wazuh") + return "Webhook received!" +if __name__ == '__main__': + app.run(host='0.0.0.0', port=PORT, ssl_context=context) + ``` +- local_rules.xml +``` + + + k8s + audit.k8s.io/v1 + Kubernetes audit log. + + + 110002 + requestURI\":.+", \"verb\": \"create + Kubernetes request to create resource + + + 110002 + requestURI\":.+", \"verb\": \"delete + Kubernetes request to delete resource + + +``` + +Dans le docker-compose.yml, ajouter : - le mapping de port "1580:1580" au wazuh.manager - le mapping ./custom-webhook.py:/var/ossec/integrations/custom-webhook.py - le mapping ./local_rules.xml:/var/ossec/etc/rules/local_rules.xml -Se connecter au conteneur pour installer flask : + +Ce qui donne : +``` +# Wazuh App Copyright (C) 2017, Wazuh Inc. (License GPLv2) +version: '3.7' + +services: + wazuh.manager: + image: wazuh/wazuh-manager:4.11.1 + hostname: wazuh.manager + restart: always + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 655360 + hard: 655360 + ports: + - "1514:1514" + - "1515:1515" + - "1580:1580" + - "514:514/udp" + - "55000:55000" + environment: + - INDEXER_URL=https://wazuh.indexer:9200 + - INDEXER_USERNAME=admin + - INDEXER_PASSWORD=SecretPassword + - FILEBEAT_SSL_VERIFICATION_MODE=full + - SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem + - SSL_CERTIFICATE=/etc/ssl/filebeat.pem + - SSL_KEY=/etc/ssl/filebeat.key + - API_USERNAME=wazuh-wui + - API_PASSWORD=MyS3cr37P450r.*- + volumes: + - wazuh_api_configuration:/var/ossec/api/configuration + - wazuh_etc:/var/ossec/etc + - wazuh_logs:/var/ossec/logs + - wazuh_queue:/var/ossec/queue + - wazuh_var_multigroups:/var/ossec/var/multigroups + - wazuh_integrations:/var/ossec/integrations + - wazuh_active_response:/var/ossec/active-response/bin + - wazuh_agentless:/var/ossec/agentless + - wazuh_wodles:/var/ossec/wodles + - filebeat_etc:/etc/filebeat + - filebeat_var:/var/lib/filebeat + - ./config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem + - ./config/wazuh_indexer_ssl_certs/wazuh.manager.pem:/etc/ssl/filebeat.pem + - ./config/wazuh_indexer_ssl_certs/wazuh.manager-key.pem:/etc/ssl/filebeat.key + - ./config/wazuh_cluster/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf + - ./custom-webhook.py:/var/ossec/integrations/custom-webhook.py + - ./wazuh-webhook.service:/lib/systemd/system/wazuh-webhook.service + - ./local_rules.xml:/var/ossec/etc/rules/local_rules.xml + + wazuh.indexer: + image: wazuh/wazuh-indexer:4.11.1 + hostname: wazuh.indexer + restart: always + ports: + - "9200:9200" + environment: + - "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g" + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + volumes: + - wazuh-indexer-data:/var/lib/wazuh-indexer + - ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem + - ./config/wazuh_indexer_ssl_certs/wazuh.indexer-key.pem:/usr/share/wazuh-indexer/certs/wazuh.indexer.key + - ./config/wazuh_indexer_ssl_certs/wazuh.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh.indexer.pem + - ./config/wazuh_indexer_ssl_certs/admin.pem:/usr/share/wazuh-indexer/certs/admin.pem + - ./config/wazuh_indexer_ssl_certs/admin-key.pem:/usr/share/wazuh-indexer/certs/admin-key.pem + - ./config/wazuh_indexer/wazuh.indexer.yml:/usr/share/wazuh-indexer/opensearch.yml + - ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml + + wazuh.dashboard: + image: wazuh/wazuh-dashboard:4.11.1 + hostname: wazuh.dashboard + restart: always + ports: + - 443:5601 + environment: + - INDEXER_USERNAME=admin + - INDEXER_PASSWORD=SecretPassword + - WAZUH_API_URL=https://wazuh.manager + - DASHBOARD_USERNAME=kibanaserver + - DASHBOARD_PASSWORD=kibanaserver + - API_USERNAME=wazuh-wui + - API_PASSWORD=MyS3cr37P450r.*- + volumes: + - ./config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem + - ./config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem + - ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem + - ./config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml + - ./config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml + - wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config + - wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom + depends_on: + - wazuh.indexer + links: + - wazuh.indexer:wazuh.indexer + - wazuh.manager:wazuh.manager + +volumes: + wazuh_api_configuration: + wazuh_etc: + wazuh_logs: + wazuh_queue: + wazuh_var_multigroups: + wazuh_integrations: + wazuh_active_response: + wazuh_agentless: + wazuh_wodles: + filebeat_etc: + filebeat_var: + wazuh-indexer-data: + wazuh-dashboard-config: + wazuh-dashboard-custom: +``` + +Ensuite, lancer le serveur avec +``` +docker-compose up +``` + +Se connecter au conteneur pour installer flask (pour notre listener) : ``` docker exec single-node-wazuh.manager-1 /var/ossec/framework/python/bin/pip3 install flask ``` @@ -50,49 +241,177 @@ puis le serveur : docker exec single-node-wazuh.manager-1 /var/ossec/framework/python/bin/python3 /var/ossec/integrations/custom-webhook.py ``` -### Configuration du cluster kind +A ce moment là, le serveur est prêt à recevoir des logs d'audit k8s. -Pour lier le cluster et le serveur wazuh, on peut utiliser +Le serveur devrait être accessible sur https://127.0.0.1/app/login (admin/SecretPassword). + +Note : il reste un problème avec les dashboard qui ne s'affichent pas dans l'interface web. Ce n'est pas grave, les logs reste accessibles dans l'exploration de données. + +#### Lien entre le cluster et le serveur wazuh + +Pour lier le cluster et le serveur wazuh en réseau, sans s'embêter avec les configurations réseau, on peut faire un tunnel : ``` ssh -L 0.0.0.0:1581:172.21.0.3:1580 127.0.0.1 ``` -### Création du cluster k8s "kind" +#### Création du cluster k8s "kind" -On va créer un cluster kind nommé "wazuh" avec 1 CP et 1 Worker. On ajoute 2 fichiers de conf pour kubeapi-server : audit-policy.yaml et audit-webhook.yaml +On va créer un cluster kind nommé "wazuh" avec 1 CP et 1 Worker. On ajoute 2 fichiers de conf pour kubeapi-server : audit-policy.yaml et audit-webhook.yaml. -... +wazuh/audit-policy.yaml: +``` +apiVersion: audit.k8s.io/v1 +kind: Policy +rules: +# Don’t log requests to the following API endpoints +- level: None + nonResourceURLs: + - '/healthz*' + - '/metrics' + - '/swagger*' + - '/version' +# Limit requests containing tokens to Metadata level so the token is not included in the log +#- level: Metadata +# omitStages: +# - RequestReceived +# resources: +# - group: authentication.k8s.io +# resources: +# - tokenreviews +# Extended audit of auth delegation +#- level: RequestResponse +# omitStages: +# - RequestReceived +# resources: +# - group: authorization.k8s.io +# resources: +# - subjectaccessreviews +# Log changes to pods at RequestResponse level +- level: RequestResponse + omitStages: + - RequestReceived + resources: + # core API group; add third-party API services and your API services if needed + - group: '' + resources: ['pods'] + verbs: ['create', 'patch', 'update', 'delete'] +# Log everything else at Metadata level +- level: Metadata + omitStages: + - RequestReceived +- level: RequestResponse + nonResourceURLs: + - '/logs' +``` + +wazuh/audit-webhook.yaml (attention, changer MON_IP par 192.168.10.XX): +``` +apiVersion: v1 +kind: Config +preferences: {} +clusters: +- name: wazuh-webhook + cluster: + insecure-skip-tls-verify: true + server: https://MON_IP:1581 +# kubeconfig files require a context. Provide one for the API server. +current-context: webhook +contexts: +- context: + cluster: wazuh-webhook + user: kube-apiserver # Replace with name of API server if it’s different + name: webhook +``` + +wazuh.yaml: +``` +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +name: wazuh +nodes: +- role: control-plane + image: reg.cadoles.com/dh/kindest/node:v1.28.7 + kubeadmConfigPatches: + - | + kind: ClusterConfiguration + apiServer: + extraArgs: + audit-webhook-batch-max-size: "1" + audit-webhook-config-file: "/etc/kubernetes/audit-webhook.yaml" + audit-policy-file: "/etc/kubernetes/policies/audit-policy.yaml" + extraVolumes: + - name: audit-policies + hostPath: "/etc/kubernetes/policies" + mountPath: "/etc/kubernetes/policies" + readOnly: true + pathType: "DirectoryOrCreate" + - name: audit-webhook + hostPath: "/etc/kubernetes/audit-webhook.yaml" + mountPath: "/etc/kubernetes/audit-webhook.yaml" + readOnly: true + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraMounts: + - hostPath: "./wazuh/audit-policy.yaml" + containerPath: "/etc/kubernetes/policies/audit-policy.yaml" + readOnly: true + - hostPath: "./wazuh/audit-webhook.yaml" + containerPath: "/etc/kubernetes/audit-webhook.yaml" + readOnly: true + extraPortMappings: + - containerPort: 31000 + hostPort: 31001 + listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0" + - containerPort: 80 + hostPort: 8081 + listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0" + labels: + ingress-ready: true +- role: worker + image: reg.cadoles.com/dh/kindest/node:v1.28.7 + kubeadmConfigPatches: + - | + kind: JoinConfiguration + nodeRegistration: + kubeletExtraArgs: + system-reserved: memory=2Gi +``` ``` kind create cluster --config wazuh.yaml ``` -### Piste 3 : daemonSet + /var/log/pods +À ce moment là, on doit pouvoir voir des logs coté de custom-webhook.py qui reçoit des logs du cluster. Ensuite, dans l'interface web de wazuh, on doit pouvoir retrouver les logs du cluster dans l'exploration de données. Un filtre sur "data.apiVersion=audit.k8s.io/v1" devrait retourner uniquement les logs serveur. -Première étape : créer un pod qui fait ls "/var/log/" pour voir +### Analyse -## ?? +Comme indiqué en introduction, cette méthode permet de recevoir dans Wazuh toutes les activités passant par l'apiserver. Cependant : +- il faut prendre en compte une augmentation de la consommation mémoire sur les controle-plane ; +- il faut customizer audit-policy.yaml afin de filtrer les appels à l'apiserver réellement important et éviter de se retrouver avec trop de logs ; +- il faut trouver un moyen de filtrer par cluster (prod et pp) -### Filtres et dashboards Wazuh - -Filtrer tout ce qui vient de cluster k8s : data.apiVersion=audit.k8s.io/v1 -Filter par cluster : ??? étudier la possibilité d'utiliser agent.name=mon_cluster (default semble être "wazuh.manager") - - - -Attention: > The audit logging feature increases the memory consumption of the API server because some context required for auditing is stored for each request. Memory consumption depends on the audit logging configuration. -Coté serveur, docker compose fait le taf pour du test. -cd work/wazuh/wazuh-docker/single-node -sudo docker compose up -Ajout d'un agent : https://documentation.wazuh.com/4.11/installation-guide/wazuh-agent/index.html +## Journalisation au niveau cluster + +### Solutions explorées + +D'après la documentation [kubernetes](https://kubernetes.io/docs/concepts/cluster-administration/logging/#cluster-level-logging-architectures), la meilleure solution dans notre cas est d'installer un daemonset dans le cluster ayant accès au dossier /var/log/pods. + +Reste à déterminer quel logiciel faire tourner dans le pod. + + +### DaemonSet + +Première étape : créer un pod qui fait ls "/var/log/" pour voir, en local et sur les cluster de PP et prod +Piste 3 : https://kubernetes.io/docs/concepts/cluster-administration/logging/ : daemonSet avec un agent Wazuh et /var/log/pods monté + + -Package Linux ou source ? -Installation sur noeud semble compromis -CRD Audit inexistante -Coté kubernetes : tester de logger les URIs * Demander : - version du wazuh manager (la version de l'agent doit être inférieure ou égale)