#!/bin/bash # Partie 1 - Initialisation du control plane # À exécuter UNIQUEMENT sur le nœud MASTER set -e echo "=== Initialisation du Control Plane Kubernetes (hardened) ===" # --- Détermination de l'IP API server --- # POURQUOI: `hostname -I | awk '{print $1}'` peut renvoyer l'IP PUBLIQUE en premier # sur certains providers cloud (Exoscale, AWS, ...). Conséquence: l'API # server (port 6443) serait advertisé sur Internet — un cauchemar pour un CTF. # Priorité : variable d'env APISERVER_IP > argument $1 > détection sur l'interface # par défaut (route par défaut). if [[ -n "$APISERVER_IP" ]]; then echo "IP API server : $APISERVER_IP (variable d'environnement)" elif [[ -n "$1" ]] && [[ "$1" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then APISERVER_IP="$1" echo "IP API server : $APISERVER_IP (argument positionnel)" elif [[ -n "$1" ]]; then echo "ERREUR: '$1' n'est pas une IP valide." echo "Usage: $0 [] ou APISERVER_IP= $0" exit 1 else DEFAULT_IFACE=$(ip -o -4 route show to default | awk '{print $5}' | head -n1) APISERVER_IP=$(ip -o -4 addr show dev "$DEFAULT_IFACE" 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -n1) if [[ -z "$APISERVER_IP" ]]; then echo "ERREUR: impossible de détecter l'IP de l'API server." echo " Fournir explicitement: APISERVER_IP=10.0.0.1 $0" echo " ou: $0 10.0.0.1" exit 1 fi echo "IP API server : $APISERVER_IP (interface: $DEFAULT_IFACE)" fi # Validation : l'IP doit être privée (RFC1918) pour éviter l'exposition publique if ! echo "$APISERVER_IP" | grep -qE '^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.)'; then echo "⚠ ATTENTION: $APISERVER_IP n'est pas une IP privée RFC1918." echo " Confirmer l'exposition publique de l'API server ? (y/N)" read -r CONFIRM [[ "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]] && { echo "Abandon."; exit 1; } fi POD_CIDR="10.244.0.0/16" SERVICE_CIDR="10.96.0.0/12" echo "Pod CIDR : $POD_CIDR" echo "Service CIDR : $SERVICE_CIDR" echo "" # --- Audit logging --- echo "Création de la politique d'audit..." sudo mkdir -p /var/log/kubernetes/audit sudo mkdir -p /etc/kubernetes/audit sudo tee /etc/kubernetes/audit/audit-policy.yaml > /dev/null <<'EOF' apiVersion: audit.k8s.io/v1 kind: Policy omitStages: - RequestReceived rules: # Tracer tous les accès aux secrets et configmaps (données sensibles) - level: RequestResponse resources: - group: "" resources: ["secrets", "configmaps"] # Tracer les modifications RBAC (vecteur d'escalade de privilèges) - level: RequestResponse resources: - group: "rbac.authorization.k8s.io" resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"] # Tracer exec/portforward/attach (accès interactif aux pods — vecteur d'attaque courant) - level: RequestResponse resources: - group: "" resources: ["pods/exec", "pods/portforward", "pods/attach"] # Tracer toutes les créations/suppressions/modifications (niveau Metadata pour réduire le volume) - level: Metadata verbs: ["create", "delete", "patch", "update"] # Ignorer le bruit des health checks et composants systèmes - level: None users: ["system:kube-proxy"] verbs: ["watch"] resources: - group: "" resources: ["endpoints", "services", "services/status"] - level: None users: ["system:apiserver"] verbs: ["get"] resources: - group: "" resources: ["namespaces"] - level: None nonResourceURLs: ["/healthz*", "/readyz*", "/livez*", "/metrics"] # Défaut : niveau Metadata pour tout le reste - level: Metadata EOF # --- Chiffrement etcd at-rest --- echo "Génération de la clé de chiffrement etcd..." sudo mkdir -p /etc/kubernetes/encryption ENCRYPTION_KEY=$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64) sudo tee /etc/kubernetes/encryption/encryption-config.yaml > /dev/null < /dev/null <<'EOF' apiVersion: apiserver.config.k8s.io/v1 kind: AdmissionConfiguration plugins: - name: EventRateLimit configuration: apiVersion: eventratelimit.admission.k8s.io/v1alpha1 kind: Configuration limits: - type: Namespace qps: 50 burst: 100 cacheSize: 2000 - type: User qps: 10 burst: 50 - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1 kind: PodSecurityConfiguration defaults: enforce: "baseline" enforce-version: "latest" audit: "restricted" audit-version: "latest" warn: "restricted" warn-version: "latest" exemptions: # Cilium est déployé dans kube-system (pas cilium-system) namespaces: - kube-system - kubearmor - kyverno usernames: [] runtimeClasses: [] EOF # --- kubeadm config --- echo "Création de la configuration kubeadm..." cat > /tmp/kubeadm-config.yaml < ║" echo "║ 3. Utiliser 08-generate-restricted-kubeconfig.sh pour ║" echo "║ générer le kubeconfig de l'équipe externe ║" echo "╚══════════════════════════════════════════════════════════════╝" echo "" echo "✓ Control plane initialisé avec succès!" echo "" echo "Sécurité activée:" echo " ✓ Audit logs → /var/log/kubernetes/audit/audit.log" echo " ✓ etcd chiffré → AES-CBC 256 bits at-rest" echo " ✓ Admission → NodeRestriction + PodSecurity + EventRateLimit" echo " ✓ Auth anonyme → désactivée" echo " ✓ TLS min → 1.2" echo "" echo "Prochaines étapes:" echo " 1. Joindre les workers : 03-join-workers.sh" echo " 2. Installer Cilium : 04-install-cilium.sh" echo " (commande join à récupérer ci-dessus ou via: kubeadm token create --print-join-command)" echo "" echo "Statut du cluster:" kubectl get nodes kubectl get pods -n kube-system