~18 minutes
Cet article fait partie de la série Kubernetes Managé & Edge. Si tu cherches le guide de choix et le tableau comparatif global, commence par là.
Voilà plusieurs années qu’on travaille sur des déploiements edge industriels, et k3s nous a régulièrement surpris ; positivement. Sa réputation de “Kubernetes pour Raspberry Pi” lui colle à la peau injustement. En production dans des usines, des véhicules connectés et des environnements air-gapped, c’est l’outil le plus adapté qu’on connaisse pour ce type de contraintes.
k3s en une phrase : tout Kubernetes, sans les 40 dépendances, dans un binaire de 70 MB qui tourne offline.
Architecture k3s
Commençons par ce qui change par rapport à Kubernetes upstream On commence par là parce que beaucoup de gens pensent que k3s est “juste Kubernetes avec des trucs enlevés”. C’est plus subtil.
Le binaire unique
k3s consolide l’ensemble du plan de contrôle ET de l’agent en un seul binaire statiquement lié. Concrètement :
kube-apiserver,kube-controller-manager,kube-scheduler→ intégrés dans le binairek3s serverkubelet,kube-proxy→ intégrés dansk3s agentcontainerd→ embarqué (plus besoin de l’installer séparément)CNI plugins→ intégrés (Flannel + plugins bridge/host-gw)
Ce design réduit radicalement la surface d’installation et simplifie les mises à jour : un seul binaire à remplacer.
Ce qui est remplacé ou retiré
| Composant upstream | Équivalent k3s | Raison |
|---|---|---|
etcd |
SQLite (défaut) ou etcd embarqué | Légèreté, offline |
cloud-controller-manager |
Retiré | Pas de cloud provider |
| Alpha features | Retirées | Sécurité, légèreté |
kube-proxy |
Retiré (remplacé par klipper ou CNI) | Simplification réseau |
Composants additionnels pré-intégrés
k3s intègre aussi, prêts à l’emploi :
- Traefik : ingress controller
- CoreDNS : DNS interne
- Local Path Provisioner : stockage local automatique (PVC vers hostpath)
- Metrics Server : métriques kubelet
C’est le côté “batteries included” qui permet d’avoir un cluster fonctionnel en 90 secondes sur du hardware modeste.
Installation
Single node — Le cas le plus simple
# Installation en une commande
curl -sfL https://get.k3s.io | sh -
# Vérifier le statut
systemctl status k3s
# Récupérer le kubeconfig (attention aux permissions : lisible uniquement par root par défaut)
sudo cat /etc/rancher/k3s/k3s.yaml
En 90 secondes, tu as un cluster Kubernetes complet avec Traefik, CoreDNS et le Local Path Provisioner opérationnels.
Cluster HA avec etcd embarqué
Pour la production, SQLite est insuffisant. k3s supporte etcd natif depuis la v1.19. On recommande systématiquement 3 serveurs minimum pour le quorum :
# Serveur 1 — init du cluster
curl -sfL https://get.k3s.io | \
K3S_TOKEN=MON-TOKEN-SECRET \
sh -s - server \
--cluster-init \
--tls-san mon-vip.domaine.com \
--tls-san 192.168.1.100
# Serveurs 2 et 3 — rejoindre le cluster
curl -sfL https://get.k3s.io | \
K3S_TOKEN=MON-TOKEN-SECRET \
sh -s - server \
--server https://serveur1:6443 \
--tls-san mon-vip.domaine.com
Le --tls-san est critique : il permet d’accéder à l’API via un VIP (Virtual IP) sans erreur de certificat. Si tu utilises un load balancer devant les 3 serveurs, son IP/DNS doit figurer dans le TLS SAN.
Agents (workers)
Ajouter des nodes est aussi simple que cela :
# Récupérer le token depuis n'importe quel serveur
sudo cat /var/lib/rancher/k3s/server/node-token
# Ajouter un agent
curl -sfL https://get.k3s.io | \
K3S_URL=https://mon-vip.domaine.com:6443 \
K3S_TOKEN=<token-récupéré> \
sh -
CNI et réseau — Flannel, Cilium et les NetworkPolicies
Le problème avec Flannel par défaut
Flannel est le CNI par défaut de k3s. Il est léger et fonctionne partout ; mais il ne supporte pas les NetworkPolicies. C’est un angle mort sécurité majeur : sans NetworkPolicies, tous les pods peuvent se parler librement dans le cluster.
Deux approches se distinguent selon le contexte :
Option 1 : Passer à Cilium (recommandé)
Cilium apporte eBPF, les NetworkPolicies et une observabilité réseau avancée. C’est ce qu’on utilise en production :
# Installer k3s sans Flannel
curl -sfL https://get.k3s.io | \
sh -s - server \
--flannel-backend=none \
--disable-network-policy \
--cluster-cidr=10.42.0.0/16
# Installer Cilium via Helm
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium \
--namespace kube-system \
--set operator.replicas=1 \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=true \
--set k8sServiceHost=mon-vip.domaine.com \
--set k8sServicePort=6443
Option 2 : Canal (Flannel + NetworkPolicies via Calico)
Pour les environnements avec des contraintes de ressources plus strictes, Canal est un bon compromis :
curl -sfL https://get.k3s.io | \
sh -s - server \
--flannel-backend=none \
--disable-network-policy
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/canal.yaml
NetworkPolicy de base — Isolation par namespace
Quelle que soit l’option choisie, voici la politique de base à appliquer sur tous les namespaces de production :
# Deny-all par défaut, puis autoriser sélectivement
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Autoriser uniquement le trafic DNS vers kube-system
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
Stockage — SQLite, etcd et Longhorn
Le choix du backend datastore
| Backend | Cas d’usage | Notes |
|---|---|---|
| SQLite | Dev, single-node, lab | Pas de HA, fichier unique /var/lib/rancher/k3s/server/db/state.db |
| etcd embarqué | Production HA (3+ serveurs) | Recommandé, intégré dans k3s |
| PostgreSQL externe | Déjà une infra DB | Connexion via --datastore-endpoint |
| MySQL / MariaDB | Déjà une infra DB | Idem PostgreSQL |
Pour le stockage persistent des workloads, Longhorn s’est imposé comme le standard dans l’écosystème k3s/Rancher :
# Prérequis : open-iscsi sur chaque node
sudo apt install -y open-iscsi
# Installer Longhorn
helm repo add longhorn https://charts.longhorn.io
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultReplicaCount=2
Longhorn distribue les volumes sur plusieurs nodes avec réplication, ce qui est essentiel pour la HA dans un contexte edge où les devices peuvent redémarrer sans préavis.
Sécurité de k3s
Maintenant voyons les bonnes pratiques pour sécuriser un cluster k3s en production, que ce soit à l’edge ou dans le cloud.
1. Chiffrement des secrets au repos
Par défaut, les secrets Kubernetes sont stockés en base64 (non chiffrés) dans SQLite ou etcd. C’est un risque majeur si le disque est physiquement accessible(meme si il est recommandé de chiffré les disques au niveau OS, il est préférable d’avoir une couche de chiffrement native Kubernetes pour les secrets histoire d’être indépendant du chiffrement disque):
# Activer le chiffrement des secrets dès l'installation
curl -sfL https://get.k3s.io | \
sh -s - server \
--secrets-encryption
# Vérifier la configuration de chiffrement
sudo cat /var/lib/rancher/k3s/server/cred/encryption-config.json
Si le cluster est déjà déployé sans chiffrement, la procédure de rotation est plus complexe et nécessite un redémarrage des composants.
2. RBAC, Principe du moindre privilège
k3s active RBAC par défaut. Le piège courant : utiliser le kubeconfig root (/etc/rancher/k3s/k3s.yaml) pour tous les usages. Ce fichier contient les credentials cluster-admin.
On génère des kubeconfigs restreints pour chaque usage :
# Créer un ServiceAccount limité
kubectl create serviceaccount lecteur-prod -n production
kubectl create rolebinding lecteur-prod-binding \
--clusterrole=view \
--serviceaccount=production:lecteur-prod \
-n production
# Générer un kubeconfig pour ce SA
TOKEN=$(kubectl create token lecteur-prod -n production --duration=8760h)
kubectl config set-credentials lecteur-prod --token=$TOKEN
Comme cela même si un token est compromis, l’attaquant n’aura accès qu’à une partie limitée du cluster.
3. Traefik, Sécuriser l’ingress par défaut
L’ingress par défaut de K3s est Traefik. Il est fonctionnel mais pas forcément super sécurisé en production par défaut (enfin comme tous les ingress controller….). Il faut penser a désactiver systématiquement le dashboard Traefik en production et forcer TLS :
# Désactiver le dashboard Traefik
cat > /var/lib/rancher/k3s/server/manifests/traefik-config.yaml << 'EOF'
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
dashboard:
enabled: false
ingressRoute:
dashboard:
enabled: false
additionalArguments:
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
EOF
Cela permet de réduire la surface d’attaque en désactivant les interfaces d’administration exposées et en forçant le chiffrement pour tout le trafic entrant.
4. Pod Security Admission (PSA)
k3s 1.25+ inclut PSA. On peut configurer le niveau restricted sur les namespaces de production :
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
Cela apporte une couche de sécurité supplémentaire en empêchant les pods de s’exécuter avec des privilèges élevés, des volumes hostPath, ou d’autres configurations risquées.
5. Audit logging
L’audit logging n’est pas forcément activé par défaut dans k3s. On l’active avec un fichier de politique minimale :
# /etc/rancher/k3s/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets", "configmaps"]
- level: Request
verbs: ["create", "update", "patch", "delete"]
resources:
- group: ""
resources: ["pods", "deployments"]
- level: None
resources:
- group: ""
resources: ["events"]
# Démarrer k3s avec audit logging
curl -sfL https://get.k3s.io | \
sh -s - server \
--kube-apiserver-arg="audit-policy-file=/etc/rancher/k3s/audit-policy.yaml" \
--kube-apiserver-arg="audit-log-path=/var/log/k3s/audit.log" \
--kube-apiserver-arg="audit-log-maxage=30" \
--kube-apiserver-arg="audit-log-maxsize=100"
Cela permet de garder une trace des actions sensibles dans le cluster, ce qui est crucial pour la détection d’incidents et les audits de sécurité.
Déploiement Kubernetes sans internet
C’est la vraie force de k3s dans les environnements industriels. Une usine, un véhicule, une infrastructure critique : ces environnements ne peuvent pas dépendre d’une connexion internet pour tirer des images.
Pour cela on peut recourir à la fonctionnalité “air-gapped” de k3s : on télécharge en avance un bundle d’images et le binaire, on les transfère sur le site, et k3s démarre sans tenter de se connecter à internet.
Registry locale pour les images applicatives
Pour les images applicatives, on peut déployer une registry locale (Harbor ou Registry simple) sur le réseau interne :
# /etc/rancher/k3s/registries.yaml
mirrors:
"docker.io":
endpoint:
- "https://registry.interne.local"
"registry.k8s.io":
endpoint:
- "https://registry.interne.local"
configs:
"registry.interne.local":
tls:
ca_file: /etc/ssl/certs/registry-ca.crt
auth:
username: k3s-pull
password: MON-MOT-DE-PASSE
Cela permet de mieux maîtriser les images utilisées en production, d’avoir un cache local pour les déploiements et de réduire la dépendance à internet même pour les mises à jour d’applications.
Sécurité physique — L’angle mort de l’edge
C’est le point sur lequel on insiste le plus dans les déploiements edge : un attaquant avec accès physique à un device edge peut potentiellement compromettre tout le cluster.
Pour cela j’aurait tendance a vous donner une checklist de hardening physique à appliquer systématiquement sur tous les devices edge, en complément des bonnes pratiques logicielles :
- Activer le chiffrement disque avec LUKS2
- Utiliser TPM pour l’attestation d’intégrité du bootloader
- Désactiver les accès physiques non nécessaires (boot USB, ports série, modules USB de stockage)
- Sécuriser la console physique (mot de passe fort, désactivation du login root local)
- Chiffrer les secrets Kubernetes au repos (même si le disque est chiffré, c’est une couche de sécurité supplémentaire)
- Utiliser des tokens Kubernetes à durée de vie longue mais avec des permissions restreintes (pas de cluster-admin pour les applications)
- Mettre en place une surveillance de l’intégrité du système (Falco, auditd) pour détecter les accès physiques suspects ou les modifications non autorisées
- Former les équipes de maintenance à la sécurité physique et aux risques liés à l’edge
- Mettre en place une politique de gestion des incidents spécifique à l’edge, incluant la réponse à un accès physique compromis (rotation des tokens, réinitialisation du cluster, etc.)
- Documenter clairement les procédures de sécurité physique et les intégrer dans les processus de déploiement et de maintenance des clusters k3s à l’edge.
- Utiliser des boîtiers sécurisés pour les devices edge, avec des mécanismes de verrouillage physique et des alertes en cas d’ouverture non autorisée.
- Mettre en place une politique de rotation régulière des tokens Kubernetes utilisés par les applications sur les devices edge, pour limiter l’impact d’un token compromis.
- Utiliser des solutions de gestion des secrets externes (HashiCorp Vault, AWS KMS, etc.) pour éviter de stocker les secrets directement sur les devices edge, même chiffrés.
Latéralisation depuis un device edge compromis
Voici un scénario potentiel dans un contexte industriel.
Contexte : Une usine déploie 50 clusters k3s sur des Raspberry Pi 5 pour collecter des données de capteurs. Les clusters sont connectés aussi bien au réseau OT de l’usine qu’au réseau interne de l’IT (via des passerelles ou non)
Scénario d’attaque :
- Un technicien de maintenance branche une clé USB sur un device edge pendant une intervention. Cette clé contient un script qui extrait le token k3s node.
- Avec ce token, l’attaquant peut lister les pods du cluster :
kubectl --token=<token> get pods -A - Avec les bonnes permissions (souvent trop larges en prod), il peut exécuter des commandes dans les pods :
kubectl exec -it capteur-collector -- /bin/sh - Depuis le pod, l’accès au metadata server interne du cluster (via l’API Kubernetes) lui permet de récupérer les secrets du namespace : volumes montés, variables d’environnement, ServiceAccount tokens.
- Ces secrets contiennent les credentials de connexion à la base de données centrale IT — le pivot est réalisé.
Ce qui aurait évité cette attaque :
- Chiffrement disque LUKS + TPM → le token ne peut pas être extrait par boot externe
- Désactivation USB-storage → la clé USB ne se monte pas
- NetworkPolicies strictes → les pods ne peuvent pas joindre le réseau IT directement
- Secrets chiffrés au repos → même avec accès physique, les secrets ne sont pas lisibles
- RBAC restreint sur les ServiceAccounts → les pods ne peuvent pas lister les autres secrets
Quelques références pour aller plus loin
- k3s — Documentation officielle SUSE Rancher
- Rancher Fleet — GitOps pour multi-clusters
- Longhorn — Stockage distribué pour k3s
- Cilium — CNI eBPF pour k3s
- OWASP Kubernetes Top 10
- CIS Kubernetes Benchmark
- NSA/CISA Kubernetes Hardening Guidance
- kube-bench — Audit CIS Benchmark
✓ À retenir 📌
✓ k3s n'est pas un "petit Kubernetes" : c'est une distribution production-ready certifiée CNCF dans un binaire de 70 MB, conçue pour les contraintes edge. En 90 secondes, tu as un cluster complet.
✓ Flannel par défaut = pas de NetworkPolicies : remplace-le systématiquement par Cilium (eBPF, observabilité avancée) ou Canal (Flannel + Calico policies) en production.
✓ Active le chiffrement des secrets dès le déploiement (--secrets-encryption) — la migration après coup est complexe et les secrets non chiffrés sont lisibles directement depuis le disque.
✓ La sécurité physique est le premier vecteur d'attaque en edge : LUKS + TPM pour le disque, désactivation USB-storage, accès console sécurisé. Sans ça, toute la sécurité logicielle peut être contournée par un accès physique.
✓ Le mode air-gapped est une vraie force : télécharge le bundle d'images en avance, copie-le sur le device, k3s démarre sans internet. Combine avec une registry locale pour les images applicatives.
✓ Pour gérer 10+ clusters edge, Fleet est indispensable : GitOps avec ciblage par labels de cluster, déploiement différencié par site, synchronisation sur connexions intermittentes.
