# 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 ```bash scp -i ~/.ssh/xpeditis_prod infra/prod/scripts/02-setup-k3s-server.sh \ deploy@:/tmp/ ssh -i ~/.ssh/xpeditis_prod deploy@ \ "sudo K3S_VERSION=v1.31.5+k3s1 PUBLIC_IP= 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 les `audit_logs` verraient 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-Agent` et `Cf-Connecting-Ip` sont 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 ```bash ssh -i ~/.ssh/xpeditis_prod deploy@ 'cat ~/.kube/config' \ | sed "s/127.0.0.1//" > ~/.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 : ```bash echo 'export KUBECONFIG=~/.kube/xpeditis-prod.yaml' >> ~/.zshrc ``` --- ## 3. Vérifications ```bash 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@ 'sudo k3s secrets-encrypt status' # Encryption Status: Enabled # Journal d'audit alimenté ssh deploy@ 'sudo tail -2 /var/log/k3s/audit.log | head -c 300' # Traefik écoute bien sur 80 et 443 ssh deploy@ 'sudo ss -tlnp | grep -E ":(80|443) "' ``` --- ## 4. Composants du cluster Depuis votre poste, kubeconfig chargé : ```bash cd infra/prod REGISTRY_TOKEN='' bash scripts/03-install-cluster-addons.sh ``` Le script installe : 1. les namespaces `xpeditis-prod` et `monitoring`, avec l'*admission de sécurité des pods* — `restricted` pour l'application, `baseline` pour la supervision (Promtail doit lire les journaux de l'hôte) ; 2. **cert-manager** depuis son manifeste officiel épinglé (pas de Helm à maintenir) ; 3. le `Secret` d'accès au registre Scaleway (`regcred`) ; 4. le `ClusterIssuer` Let'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](./06-secrets-sops.md). ```bash 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. ```bash # 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 : ```bash 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](./06-secrets-sops.md)**