Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
182 lines
6.6 KiB
Markdown
182 lines
6.6 KiB
Markdown
# 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@<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 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@<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 :
|
|
```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@<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é :
|
|
|
|
```bash
|
|
cd infra/prod
|
|
REGISTRY_TOKEN='<jeton Scaleway>' 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)**
|