Partie 19 : Pentest du cluster K3s
Pourquoi un pentest ?
Après des mois passés à construire ce cluster K3s brique par brique — le site web, ArgoCD, le monitoring, la stack media, les logs — j'ai réalisé que je n'avais jamais pris le temps de regarder mon infra avec les yeux d'un attaquant. Est-ce que tout ce que j'ai construit est réellement sécurisé ? Est-ce qu'un voisin sur mon réseau WiFi pourrait accéder à mes fichiers ? Est-ce qu'un bot sur Internet pourrait trouver une faille dans mes services exposés ?
Il était temps de le vérifier.
La mise en place
Le choix de l'outil : Exegol
Plutôt que d'utiliser Kali Linux, j'ai opté pour Exegol : un environnement de pentest sous forme de container Docker, maintenu par la communauté française. L'avantage c'est qu'il embarque tous les outils nécessaires et se lance en une commande depuis mon Arch Linux.
L'installation a eu quelques accrocs — Python sur Arch refuse les installations système (externally-managed-environment), donc passage obligé par pipx. Et Docker n'était pas installé. Classique.
sudo pacman -S python-pipx docker
pipx install exegol
sudo systemctl start docker
Petite surprise : Exegol a changé de modèle depuis ma dernière utilisation, seule l'image free (43 Go) est gratuite. Les images spécialisées (web, AD, OSINT) sont maintenant payantes, j'ai déjà utilisé exegol par le passé et j'avais déjà des images téléchargée sur mon autre os, mais plus du tout maintenanu à jour et impossible de faire les mise à jour basiques. L'image free contient largement ce qu'il faut pour notre cas donc on va rester ce ça.
exegol start pentest-k3s free -l --log-method asciinema
Le flag -l active l'enregistrement de la session — pratique pour rédiger ce récap après coup.
L'approche
L'idée c'est de procéder comme un vrai attaquant, en partant de l'extérieur vers l'intérieur :
- Depuis Internet — qu'est-ce qu'un inconnu peut voir et attaquer ?
- Depuis le réseau local — qu'est-ce qu'un appareil compromis sur mon WiFi peut faire ?
- Depuis Kubernetes — si quelqu'un obtient un accès au cluster, jusqu'où peut-il aller ?
- Les images Docker — est-ce que mes containers embarquent des failles connues ?
Phase 1 — Attaque depuis Internet
Qu'est-ce qu'on trouve en cherchant "home-fonta.fr" ?
La première étape c'est l'énumération DNS : on cherche tous les sous-domaines existants. subfinder interroge des dizaines de sources publiques pour trouver des sous-domaines sans même toucher au serveur cible.
subfinder -d home-fonta.fr
16 sous-domaines trouvés. Parmi eux, 4 sous-domaines de test (radarr-test, sonarr-test, etc.) que j'avais oubliés — ils n'existent plus dans Cloudflare mais traînent encore dans les caches publics. Un attaquant peut les voir.
Tous pointent vers la même IP. Et le reverse DNS révèle immédiatement mon FAI. Pas de proxy Cloudflare, mon IP réelle est exposée.
Quels ports sont ouverts ?
nmap scanne les ports ouverts sur l'IP publique. C'est la base de toute reconnaissance : savoir quels services sont accessibles.
nmap -sS -sV --top-ports 1000 IP

| Port | Service | C'est quoi ? |
|---|---|---|
| 21 | FTP | Ma Livebox (pas le cluster) |
| 53 | DNS | Ma Livebox |
| 80/443 | HTTP/HTTPS | Mon cluster K3s (le seul port que j'ai forwardé) |
| 554 | RTSP | Ma Livebox (décodeur TV) |
Bonne nouvelle : seul le port 443 est réellement forwardé vers le cluster. Les autres ports sont des services de la Livebox elle-même, pas super grave mais ils donnent des informations à un attaquant (modèle de box, FAI, etc.).
Le certificat SSL est-il solide ?
testssl.sh analyse en profondeur la configuration TLS : protocoles supportés, force des ciphers, vulnérabilités connues (Heartbleed, POODLE et compagnie).
testssl.sh --fast https://home-fonta.fr
Grade A+. TLS 1.2 et 1.3 uniquement, Forward Secrecy activé, certificat Let's Encrypt valide, zéro vulnérabilité. Rien à redire, la config nginx par défaut fait bien son boulot.
Est-ce qu'on peut se connecter avec des mots de passe par défaut ?
Test classique : essayer admin/admin sur les interfaces d'administration exposées.
# ArgoCD
curl -sk https://argo.home-fonta.fr/api/v1/session -X POST \
-d '{"username":"admin","password":"admin"}'
→ "Invalid username or password"
# Grafana
curl -sk https://grafana.home-fonta.fr/login -X POST \
-d '{"user":"admin","password":"admin"}'
→ "Invalid username or password"
Tout est refusé. Par contre, ArgoCD donne gratuitement sa version :
curl -sk https://argo.home-fonta.fr/api/version
→ {"Version":"v3.2.1+8c4ab63"}
C'est une fuite d'information : un attaquant peut chercher les CVE spécifiques à cette version.
Le scan de vulnérabilités automatisé
nuclei c'est le couteau suisse du scan web, je l'ai trouvé en faisant ce pentest. Il teste des milliers de scénarios d'attaque (5934 templates dans notre cas) : CVE connues, fichiers sensibles exposés, credentials par défaut, erreurs de configuration...
echo "https://argo.home-fonta.fr
https://grafana.home-fonta.fr
https://blog.home-fonta.fr
..." > urls.txt
nuclei -l urls.txt -severity critical,high,medium

Après 16 minutes de scan, une seule CVE trouvée : CVE-2022-41697 sur Ghost, qui permet de savoir si un email est enregistré ou non via l'API admin. Pas catastrophique, mais à corriger.
Le résultat le plus intéressant c'est l'effet secondaire : mes Raspberry Pi ont complètement saturé sous la charge du scan. Tous les services sont devenus inaccessibles pendant plusieurs minutes. Aucun rate-limiting n'était en place — un simple script pouvait faire tomber tout mon cluster. Il faudra revenir là-dessus.
Phase 2 — Attaque depuis le réseau local
Maintenant, on se met dans la peau de quelqu'un qui est sur mon WiFi. C'est un scénario réaliste : un appareil IoT compromis, un invité malveillant, ou simplement un voisin qui a trouvé le mot de passe WiFi.
Scan complet du réseau
nmap -sS -sV -p- --min-rate 1000 192.168.1.15,18,33,36,50,51



Un scan de tous les ports (65535) sur les 5 nœuds du cluster + le NAS. C'est beaucoup plus bavard que depuis l'extérieur, je n'ai pu capturer tout le résultat en image.
Sur chaque nœud K3s, on retrouve les mêmes services : SSH, l'ingress nginx, et quelques ports Kubernetes. Le plus important : le Kubelet API (port 10250) est bien protégé — il retourne "Unauthorized" sans token. C'est K3s qui gère ça par défaut, bonne surprise.
Le NAS QNAP, par contre, c'est une autre histoire : 19 ports ouverts. SMB, AFP, NFS, rsync, InfluxDB, Plex...
La découverte critique : InfluxDB sans mot de passe
En testant les services du NAS un par un, je tombe sur InfluxDB (port 8086) :
curl -s http://192.168.1.18:8086/query?q=SHOW+DATABASES
→ {"results":[{"series":[{"values":[["qnap_metrics"],["_internal"]]}]}]}
Pas de mot de passe. Pas de token. Rien. N'importe qui sur le réseau peut lire toutes les métriques du NAS.
Pire, on peut écrire :
curl -s -X POST http://192.168.1.18:8086/write?db=qnap_metrics \
--data-binary 'pentest_marker,host=exegol value=1'
→ Succès !
J'ai pu injecter des fausses données dans ma base de métriques. Un attaquant pourrait manipuler mes dashboards Grafana, ou pire, effacer toute la base avec un simple DROP DATABASE. J'ai nettoyé ma trace de test et stoppé InfluxDB dans la foulée.
Les partages NFS grand ouverts
showmount -e 192.168.1.18
→ /Series 192.168.1.0/24
→ /Films 192.168.1.0/24
→ /Downloads 192.168.1.0/24
→ /Animes 192.168.1.0/24
→ /k3s-data 192.168.1.0/24
Tous mes partages NFS étaient accessibles à n'importe quel appareil sur le réseau local (192.168.1.0/24). En théorie, seuls les nœuds du cluster devraient pouvoir y accéder et mon pc local.
Les services applicatifs résistent
Bonne nouvelle : toutes les APIs (Radarr, Sonarr, Home Assistant, Grafana) nécessitent une authentification. Pas de faille côté applicatif.
Phase 3 — Audit Kubernetes
Avec un kubectl configuré (kubeconfig copié dans Exegol), on peut auditer la configuration interne du cluster.
Pas de barrières entre les pods
kubectl get networkpolicies -A
Seul ArgoCD a des Network Policies (installées par défaut). Tous les autres namespaces n'en ont aucune. Concrètement, si un pod est compromis, il peut parler à tous les autres pods du cluster sans restriction.
Le problème c'est que Flannel (le CNI par défaut de K3s) ne supporte pas les Network Policies. Il faudrait migrer vers Calico — c'est un chantier pour plus tard.
Home Assistant : le pod le plus dangereux
kubectl get pods -A -o json | jq '... select(.securityContext.privileged==true) ...'
→ homeassistant/homeassistant-579b8c6679-dsdlm
Home Assistant tourne en mode privileged avec hostNetwork, SYS_ADMIN, NET_ADMIN... C'est l'équivalent de donner les clés root de la machine hôte au container. C'est nécessaire pour le Zigbee et le Bluetooth, mais si ce pod est compromis, c'est game over pour le nœud entier.
Aucune restriction sur les pods
kubectl get namespaces -o json | jq '... pod-security.kubernetes.io/enforce ...'
→ (rien)
Aucun namespace n'avait de Pod Security Standards configurés. N'importe quel déploiement pouvait demander le mode privileged sans que Kubernetes ne bronche.
K3s a 2 ans de retard
kubectl version
→ Server Version: v1.28.5+k3s1
Ma version de K3s date de fin 2023. Plus de 2 ans sans mise à jour — il y a certainement des CVE non patchées.
Phase 4 — Les images Docker
trivy scanne les images Docker pour y trouver des CVE connues dans les packages installés j'ai également connu cet outils en faisant cet audit.
trivy image --severity HIGH,CRITICAL ghost:5-alpine


| Image | Vulnérabilités | Verdict |
|---|---|---|
| Ghost 5.96-alpine | 1 CRITICAL + 5 HIGH | A mettre à jour |
| Seerr (latest) | ~6 HIGH | Restart du pod suffit |
| Gluetun | 0 | Propre |
| Radarr | 0 | Propre |
Ghost embarquait une faille critique dans gosu (un utilitaire Go) lié à crypto/tls. La mise à jour vers la version 6.22.1 corrige tout.
Le bilan
Ce qui va bien
Le cluster est globalement bien sécurisé côté Internet :
- TLS Grade A+ — la configuration SSL est parfaite
- Tous les mots de passe par défaut ont été changés
- Les APIs nécessitent toutes une authentification
- Les secrets sont gérés proprement via Sealed Secrets
- Le Kubelet et l'API K3s sont bien protégés
Ce qui ne va pas
Le réseau local c'est une autre histoire. Le NAS était le maillon faible :
- InfluxDB sans aucune authentification — le finding le plus critique
- Les partages NFS accessibles à tout le réseau
- Le cluster n'avait aucun rate-limiting — un simple scan le faisait tomber
- K3s pas mis à jour depuis 2 ans
- Aucun Pod Security Standard pour limiter les pods privilégiés
Les corrections
Appliquées dans la session
| Correction | Détail |
|---|---|
| InfluxDB stoppé | Plus de base de données exposée sans auth |
| NFS restreint | Seules les 5 IPs du cluster peuvent monter les partages |
| Rate-limiting | 10 requêtes/seconde par IP sur tous les ingress |
| ArgoCD version masquée | /api/version retourne maintenant 403 |
| Ghost mis à jour | 5.96 → 6.22.1 (corrige la CVE critique) |
| Pod Security Standards | Labels baseline sur 5 namespaces |
| rpcbind désactivé | Service inutile retiré sur les 4 workers |
| CNAME pihole corrigé | Pointait vers ha.home-fonta.fr au lieu de home-fonta.fr |
| Seerr redémarré | Pull de la dernière image pour corriger les CVE |
À planifier
| Action | Quand |
|---|---|
| Mise à jour K3s (v1.28 → dernière stable) | Pendant que j'écris ce post |
| Migration Flannel → Calico | Moyen terme (nécessaire pour les Network Policies) |
| Renovate Bot | Moyen terme (auto-update des images Docker) |
Tableau récapitulatif des findings
| # | Finding | Sévérité | Statut |
|---|---|---|---|
| 1 | InfluxDB sans auth (lecture + écriture) | Critique | Corrigé |
| 2 | NFS exports accessibles à tout le LAN | Haut | Corrigé |
| 3 | K3s v1.28.5 obsolète (2+ ans) | Haut | Planifié |
| 4 | Aucune NetworkPolicy (hors ArgoCD) | Haut | Migration Calico nécessaire |
| 5 | Home Assistant en mode privileged | Haut | Risque accepté (Zigbee/BT) |
| 6 | Aucun Pod Security Standard | Haut | Corrigé |
| 7 | Ghost CVE critique | Haut | Corrigé |
| 8 | Pas de rate-limiting | Medium | Corrigé |
| 9 | ArgoCD expose sa version | Medium | Corrigé |
| 10 | CVE Ghost énumération utilisateurs | Medium | Corrigé |
| 11 | Seerr CVE multiples | Medium | Corrigé |
| 12 | rpcbind sur les workers | Medium | Corrigé |
| 13 | Node-exporter sans auth | Medium | A restreindre |
| 14 | IP publique non masquée | Medium | Incompatible reverse proxy |
| 15 | SMB Samba 4.6.2 | Medium | MAJ QTS à planifier |
| 16 | Sous-domaines -test dans les caches | Medium | Déjà supprimés |
| 17 | CNAME pihole incorrect | Faible | Corrigé |
| 18 | AFP obsolète sur le NAS | Faible | A désactiver |
| 19 | Livebox expose 21/53/554 | Faible | Services de la box |