Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
6.6 KiB
05 — Cluster k3s
Durée : environ 1 h 30.
k3s est une distribution Kubernetes complète en un seul binaire. Sur un nœud, il consomme environ 500 Mo de RAM — comparable à Docker Swarm, pour un modèle de déploiement bien plus riche (sondes, rolling updates sans coupure, politiques réseau, quotas).
1. Installation
scp -i ~/.ssh/xpeditis_prod infra/prod/scripts/02-setup-k3s-server.sh \
deploy@<app_public_ipv4>:/tmp/
ssh -i ~/.ssh/xpeditis_prod deploy@<app_public_ipv4> \
"sudo K3S_VERSION=v1.31.5+k3s1 PUBLIC_IP=<app_public_ipv4> PRIVATE_IP=10.10.1.10 \
bash /tmp/02-setup-k3s-server.sh"
Comptez trois à cinq minutes.
Ce que le script durcit, et pourquoi
| Option | Effet |
|---|---|
--secrets-encryption |
Les Secret sont chiffrés dans la base d'état de k3s. Sans cela, un instantané de disque ou un vol de volume livre tous les secrets en clair. |
--protect-kernel-defaults |
kubelet refuse de démarrer si les sysctl attendus ne sont pas posés. Échec bruyant plutôt que dérive silencieuse. |
audit-log-* |
Journal d'audit de l'API, 30 jours. Sans lui, « qui a supprimé ce déploiement ? » reste sans réponse. La politique fournie ne journalise jamais le contenu des Secret. |
--write-kubeconfig-mode=0600 |
Le kubeconfig n'est pas lisible par tous les utilisateurs du serveur. |
--advertise-address=10.10.1.10 |
Le cluster s'annonce sur le réseau privé. |
--etcd-expose-metrics=false |
Réduit la surface exposée. |
Traefik
Le script écrit /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
avant l'installation, pour que k3s applique la configuration dès le premier
démarrage :
- redirection HTTP → HTTPS au niveau de l'entrypoint — aucun Ingress ne peut l'oublier ;
- IP Cloudflare déclarées de confiance pour
X-Forwarded-For: sans cela, la limitation de débit et lesaudit_logsverraient tous l'IP de Cloudflare, et un seul abus bloquerait tout le monde ; - journaux d'accès en JSON, en-têtes filtrés (seuls
User-AgentetCf-Connecting-Ipsont conservés) — les autres peuvent contenir des jetons ; - tableau de bord Traefik désactivé ;
- métriques Prometheus activées.
2. Récupérer le kubeconfig
ssh -i ~/.ssh/xpeditis_prod deploy@<app_public_ipv4> 'cat ~/.kube/config' \
| sed "s/127.0.0.1/<app_public_ipv4>/" > ~/.kube/xpeditis-prod.yaml
chmod 600 ~/.kube/xpeditis-prod.yaml
export KUBECONFIG=~/.kube/xpeditis-prod.yaml
kubectl get nodes -o wide
Ce fichier donne les pleins pouvoirs sur le cluster. Traitez-le comme une clé privée :
chmod 600, jamais dans Git, jamais dans un secret GitHub. Il n'est utilisable que depuis vos IP d'administration (firewall Hetzner).
Pour l'utiliser en permanence :
echo 'export KUBECONFIG=~/.kube/xpeditis-prod.yaml' >> ~/.zshrc
3. Vérifications
kubectl get nodes
# NAME STATUS ROLES VERSION
# xpeditis-prod-app-01 Ready control-plane,master v1.31.5+k3s1
kubectl -n kube-system get pods
# coredns, local-path-provisioner, metrics-server, traefik : tous Running
# Chiffrement des Secrets au repos
ssh deploy@<app_public_ipv4> 'sudo k3s secrets-encrypt status'
# Encryption Status: Enabled
# Journal d'audit alimenté
ssh deploy@<app_public_ipv4> 'sudo tail -2 /var/log/k3s/audit.log | head -c 300'
# Traefik écoute bien sur 80 et 443
ssh deploy@<app_public_ipv4> 'sudo ss -tlnp | grep -E ":(80|443) "'
4. Composants du cluster
Depuis votre poste, kubeconfig chargé :
cd infra/prod
REGISTRY_TOKEN='<jeton Scaleway>' bash scripts/03-install-cluster-addons.sh
Le script installe :
- les namespaces
xpeditis-prodetmonitoring, avec l'admission de sécurité des pods —restrictedpour l'application,baselinepour la supervision (Promtail doit lire les journaux de l'hôte) ; - cert-manager depuis son manifeste officiel épinglé (pas de Helm à maintenir) ;
- le
Secretd'accès au registre Scaleway (regcred) ; - le
ClusterIssuerLet's Encrypt — seulement si le secret Cloudflare existe déjà, sinon il vous le rappelle. C'est normal à ce stade : il sera appliqué après 06 — Secrets.
kubectl -n cert-manager get pods # 3 pods Running
kubectl get ns # xpeditis-prod, monitoring, cert-manager
kubectl -n xpeditis-prod get secret regcred
5. Éprouver les garde-fous
Cette étape est facultative mais rassurante : elle vérifie que les protections mordent réellement.
# Un pod privilégié doit être REFUSÉ par le namespace restricted
kubectl -n xpeditis-prod run test-privilegie --image=alpine --restart=Never \
--overrides='{"spec":{"containers":[{"name":"c","image":"alpine","securityContext":{"privileged":true}}]}}' \
-- sleep 10
# Attendu : "violates PodSecurity restricted:latest"
# Un pod root doit être REFUSÉ
kubectl -n xpeditis-prod run test-root --image=alpine --restart=Never \
--overrides='{"spec":{"containers":[{"name":"c","image":"alpine","securityContext":{"runAsUser":0}}]}}' \
-- sleep 10
# Attendu : refus également
Si l'un des deux est accepté, les étiquettes du namespace n'ont pas été appliquées :
kubectl apply -f infra/prod/k8s/base/00-namespaces.yaml
kubectl get ns xpeditis-prod -o jsonpath='{.metadata.labels}' | jq
6. Ce qu'il ne faut pas faire
| À éviter | Pourquoi |
|---|---|
Ouvrir 6443 à 0.0.0.0/0 |
L'API Kubernetes exposée publiquement est une cible permanente. Terraform la restreint à vos IP. |
| Mettre le kubeconfig dans un secret GitHub | Un dépôt compromis donnerait le contrôle total du cluster. La CI passe par SSH avec une clé restreinte à un script. |
| Installer un tableau de bord Kubernetes | Surface d'attaque considérable pour un gain nul face à kubectl et Grafana. |
Exécuter des charges de travail en hostNetwork |
Seul node-exporter le fait, et c'est déjà un compromis assumé. |
Désactiver les NetworkPolicy |
Elles empêchent un pod compromis de balayer le réseau privé. |
7. Contrôle
[ ] Nœud Ready, k3s v1.31.x
[ ] secrets-encrypt status = Enabled
[ ] Journal d'audit alimenté dans /var/log/k3s/audit.log
[ ] Traefik écoute sur 80 et 443, tableau de bord désactivé
[ ] cert-manager : 3 pods Running
[ ] Namespaces xpeditis-prod (restricted) et monitoring (baseline) créés
[ ] Secret regcred présent
[ ] Un pod privilégié est refusé
[ ] kubeconfig récupéré en chmod 600, hors de Git
→ Suite : 06 — Secrets SOPS