@@ -6,11 +6,44 @@ set -e
|
||||
|
||||
echo "=== Initialisation du Control Plane Kubernetes (hardened) ==="
|
||||
|
||||
APISERVER_IP=$(hostname -I | awk '{print $1}')
|
||||
# --- 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 [<IP>] ou APISERVER_IP=<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 "IP API server : $APISERVER_IP"
|
||||
echo "Pod CIDR : $POD_CIDR"
|
||||
echo "Service CIDR : $SERVICE_CIDR"
|
||||
echo ""
|
||||
@@ -123,11 +156,11 @@ plugins:
|
||||
warn: "restricted"
|
||||
warn-version: "latest"
|
||||
exemptions:
|
||||
# Cilium est déployé dans kube-system (pas cilium-system)
|
||||
namespaces:
|
||||
- kube-system
|
||||
- kubearmor
|
||||
- kyverno
|
||||
- cilium-system
|
||||
usernames: []
|
||||
runtimeClasses: []
|
||||
EOF
|
||||
@@ -177,6 +210,9 @@ apiServer:
|
||||
value: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
- name: request-timeout
|
||||
value: "300s"
|
||||
# Désactiver l'endpoint /debug/pprof (leak d'informations de débogage)
|
||||
- name: profiling
|
||||
value: "false"
|
||||
extraVolumes:
|
||||
- name: audit-log
|
||||
hostPath: /var/log/kubernetes/audit
|
||||
@@ -203,12 +239,43 @@ controllerManager:
|
||||
value: "50"
|
||||
- name: use-service-account-credentials
|
||||
value: "true"
|
||||
- name: profiling
|
||||
value: "false"
|
||||
scheduler:
|
||||
extraArgs:
|
||||
- name: profiling
|
||||
value: "false"
|
||||
---
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
kind: KubeletConfiguration
|
||||
cgroupDriver: systemd
|
||||
protectKernelDefaults: true
|
||||
# protectKernelDefaults désactivé car CentOS 10 a kernel.panic=0 et kernel.panic_on_oops=0
|
||||
# par défaut (kubelet attend ≥10 et ≥1) — kubelet crasherait au démarrage.
|
||||
# La sécurité kernel est gérée par /etc/sysctl.d/99-kube-hardening.conf sur l'hôte.
|
||||
protectKernelDefaults: false
|
||||
# Le port 10255 (lecture seule, sans auth) DOIT être désactivé
|
||||
readOnlyPort: 0
|
||||
# Coupe les exec/attach après 5 minutes d'inactivité (un attaquant peut laisser un shell ouvert)
|
||||
streamingConnectionIdleTimeout: "5m"
|
||||
# Limite les events kubelet (anti-flooding)
|
||||
eventRecordQPS: 5
|
||||
eventBurst: 10
|
||||
# Recrée les chaînes iptables à chaque sync (cohérent avec kube-proxy/Cilium)
|
||||
makeIPTablesUtilChains: true
|
||||
# TLS minimum
|
||||
tlsMinVersion: "VersionTLS12"
|
||||
# Authentification kubelet stricte
|
||||
authentication:
|
||||
anonymous:
|
||||
enabled: false
|
||||
webhook:
|
||||
enabled: true
|
||||
x509:
|
||||
clientCAFile: /etc/kubernetes/pki/ca.crt
|
||||
authorization:
|
||||
mode: Webhook
|
||||
# Rotation auto des certificats serveur kubelet (CSR à approuver côté master)
|
||||
serverTLSBootstrap: true
|
||||
EOF
|
||||
|
||||
# --- Initialisation du cluster ---
|
||||
@@ -236,10 +303,17 @@ rm -f /tmp/kubeadm-config.yaml
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ AVERTISSEMENT SÉCURITÉ ║"
|
||||
echo "║ admin.conf = system:masters = bypass RBAC complet ║"
|
||||
echo "║ Usage : bootstrap initial uniquement ║"
|
||||
echo "║ Utiliser 08-generate-restricted-kubeconfig.sh pour ║"
|
||||
echo "║ générer un accès limité pour l'équipe externe ║"
|
||||
echo "║ admin.conf = bypass RBAC ║"
|
||||
echo "║ super-admin.conf = bypass RBAC + bypass kube-apiserver TLS ║"
|
||||
echo "║ ║"
|
||||
echo "║ ACTIONS CRITIQUES À FAIRE IMMÉDIATEMENT : ║"
|
||||
echo "║ 1. Mettre /etc/kubernetes/super-admin.conf hors-ligne ║"
|
||||
echo "║ (clé USB, gestionnaire de secrets, etc.) ║"
|
||||
echo "║ 2. Une fois les workers joints, révoquer les tokens : ║"
|
||||
echo "║ kubeadm token list ║"
|
||||
echo "║ kubeadm token delete <token> ║"
|
||||
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!"
|
||||
|
||||
Reference in New Issue
Block a user