306 lines
13 KiB
Bash
Executable File
306 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
# Partie 1 - Pré-requis et installation des composants Kubernetes
|
|
# À exécuter sur TOUS les nœuds (master + workers)
|
|
# Compatible CentOS Stream 10
|
|
|
|
set -e
|
|
|
|
# --- Détermination du rôle du nœud ---
|
|
NODE_ROLE=""
|
|
|
|
usage() {
|
|
echo "Usage: $0 [--role master|worker] | [master|worker]"
|
|
echo " --role master|worker : forcer le rôle explicitement"
|
|
echo " master|worker : argument positionnel (compatibilité)"
|
|
echo " (aucun argument) : autodetect via hostname (*master*, *control* → master)"
|
|
exit 1
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--role)
|
|
shift
|
|
case "$1" in
|
|
master|worker) NODE_ROLE="$1" ;;
|
|
*) echo "Erreur: --role attend 'master' ou 'worker', reçu '$1'"; usage ;;
|
|
esac
|
|
shift
|
|
;;
|
|
master|worker)
|
|
NODE_ROLE="$1"
|
|
shift
|
|
;;
|
|
-h|--help) usage ;;
|
|
*) echo "Argument inconnu: $1"; usage ;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$NODE_ROLE" ]]; then
|
|
if [[ "$(hostname)" == *"master"* ]] || [[ "$(hostname)" == *"control"* ]]; then
|
|
NODE_ROLE="master"
|
|
echo "Autodetect: rôle 'master' détecté via hostname ($(hostname))"
|
|
else
|
|
NODE_ROLE="worker"
|
|
echo "Autodetect: rôle 'worker' (hostname: $(hostname))"
|
|
fi
|
|
else
|
|
echo "Rôle: '$NODE_ROLE' (explicite)"
|
|
fi
|
|
|
|
echo "=== Installation des pré-requis Kubernetes sur CentOS 10 [rôle: $NODE_ROLE] ==="
|
|
|
|
# Désactiver le swap (requis par Kubernetes)
|
|
echo "Désactivation du swap..."
|
|
sudo swapoff -a
|
|
sudo sed -i '/ swap / s/^/#/' /etc/fstab
|
|
|
|
# Installer container-selinux AVANT containerd pour que les labels SELinux soient corrects
|
|
# POURQUOI: container-selinux fournit la policy qui confine chaque container dans le label
|
|
# container_t — un container compromis ne peut pas lire /etc/passwd ni écrire
|
|
# dans les répertoires système de l'hôte, même en root dans le container.
|
|
echo "Installation de container-selinux..."
|
|
sudo dnf install -y container-selinux
|
|
|
|
# Maintenir SELinux en mode enforcing
|
|
# POURQUOI: Le mode permissive ne fait que logger les violations sans les bloquer —
|
|
# c'est une fausse sécurité. En enforcing + container-selinux, les appels
|
|
# système non autorisés depuis les containers sont bloqués au niveau kernel.
|
|
echo "Vérification SELinux en mode enforcing..."
|
|
sudo setenforce 1 || true
|
|
SELINUX_CURRENT=$(getenforce 2>/dev/null || echo "Unknown")
|
|
if [[ "$SELINUX_CURRENT" != "Enforcing" ]]; then
|
|
echo "ATTENTION: SELinux n'est pas en mode Enforcing (actuel: $SELINUX_CURRENT)"
|
|
echo "Vérifier /etc/selinux/config — SELINUX doit être 'enforcing'"
|
|
fi
|
|
|
|
# Charger les modules kernel nécessaires
|
|
# overlay, br_netfilter : requis par containerd et le CNI
|
|
# ip_vs, ip_vs_rr/rr/wrr/sh : requis par Cilium en mode kube-proxy replacement (IPVS)
|
|
# nf_conntrack : suivi des connexions réseau (requis par iptables/eBPF)
|
|
echo "Configuration des modules kernel..."
|
|
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
|
|
overlay
|
|
br_netfilter
|
|
ip_vs
|
|
ip_vs_rr
|
|
ip_vs_wrr
|
|
ip_vs_sh
|
|
nf_conntrack
|
|
EOF
|
|
|
|
sudo modprobe overlay
|
|
sudo modprobe br_netfilter
|
|
sudo modprobe ip_vs
|
|
sudo modprobe ip_vs_rr
|
|
sudo modprobe ip_vs_wrr
|
|
sudo modprobe ip_vs_sh
|
|
sudo modprobe nf_conntrack 2>/dev/null || sudo modprobe nf_conntrack_ipv4 2>/dev/null || true
|
|
|
|
# Configuration sysctl
|
|
# bridge-nf-call-iptables/ip6tables : trafic bridgé traité par iptables (requis CNI)
|
|
# ip_forward : routage IP entre interfaces (requis pod-to-pod)
|
|
# rp_filter=0 : Cilium eBPF requiert que le reverse path filtering soit désactivé
|
|
# inotify params : requis par Cilium pour surveiller les changements de configuration
|
|
echo "Configuration sysctl..."
|
|
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
|
|
net.bridge.bridge-nf-call-iptables = 1
|
|
net.bridge.bridge-nf-call-ip6tables = 1
|
|
net.ipv4.ip_forward = 1
|
|
net.ipv4.conf.all.rp_filter = 0
|
|
fs.inotify.max_user_watches = 524288
|
|
fs.inotify.max_user_instances = 512
|
|
EOF
|
|
|
|
# Hardening kernel (defense in depth pour le Kube Battle)
|
|
# POURQUOI:
|
|
# kptr_restrict=2 : masque les adresses kernel dans /proc/kallsyms (anti-leak)
|
|
# dmesg_restrict=1 : dmesg réservé à root (évite leak d'infos kernel)
|
|
# ptrace_scope=2 : ptrace réservé à root (bloque attaques mémoire inter-process)
|
|
# bpf_jit_harden=2 : JIT BPF durci contre les attaques Spectre
|
|
# protected_* : durcit les liens symboliques et FIFO dans /tmp
|
|
# NOTE: unprivileged_bpf_disabled=1 est DÉLIBÉRÉMENT omis car Cilium en a besoin.
|
|
echo "Configuration sysctl hardening kernel..."
|
|
cat <<EOF | sudo tee /etc/sysctl.d/99-kube-hardening.conf
|
|
kernel.kptr_restrict = 2
|
|
kernel.dmesg_restrict = 1
|
|
kernel.yama.ptrace_scope = 2
|
|
kernel.kexec_load_disabled = 1
|
|
net.core.bpf_jit_harden = 2
|
|
fs.protected_hardlinks = 1
|
|
fs.protected_symlinks = 1
|
|
fs.protected_fifos = 2
|
|
fs.protected_regular = 2
|
|
fs.suid_dumpable = 0
|
|
EOF
|
|
|
|
sudo sysctl --system
|
|
|
|
# Configurer firewalld avec les règles Kubernetes
|
|
# POURQUOI: Désactiver firewalld entièrement supprime toute isolation réseau au niveau hôte.
|
|
# Si un pod bypass le CNI (via un exploit), firewalld local est la dernière barrière.
|
|
# Defense in depth : security group cloud + firewalld hôte + NetworkPolicy CNI.
|
|
echo "Configuration de firewalld avec les règles Kubernetes..."
|
|
sudo systemctl enable firewalld
|
|
sudo systemctl start firewalld
|
|
|
|
if [[ "$NODE_ROLE" == "master" ]]; then
|
|
echo " Règles control plane..."
|
|
sudo firewall-cmd --permanent --add-port=6443/tcp # API server
|
|
sudo firewall-cmd --permanent --add-port=2379-2380/tcp # etcd client + peer
|
|
sudo firewall-cmd --permanent --add-port=10257/tcp # kube-controller-manager
|
|
sudo firewall-cmd --permanent --add-port=10259/tcp # kube-scheduler
|
|
fi
|
|
|
|
echo " Règles communes (kubelet + réseau)..."
|
|
sudo firewall-cmd --permanent --add-port=10250/tcp # kubelet API
|
|
sudo firewall-cmd --permanent --add-port=30000-32767/tcp # NodePort services
|
|
sudo firewall-cmd --permanent --add-port=53/tcp # DNS (CoreDNS)
|
|
sudo firewall-cmd --permanent --add-port=53/udp # DNS (CoreDNS)
|
|
|
|
echo " Règles Cilium..."
|
|
sudo firewall-cmd --permanent --add-port=4240/tcp # Cilium health checks
|
|
sudo firewall-cmd --permanent --add-port=4244/tcp # Hubble server
|
|
sudo firewall-cmd --permanent --add-port=4245/tcp # Hubble relay
|
|
sudo firewall-cmd --permanent --add-port=51871/udp # WireGuard (chiffrement inter-nœuds)
|
|
sudo firewall-cmd --permanent --add-port=8472/udp # VXLAN (fallback)
|
|
|
|
sudo firewall-cmd --permanent --add-masquerade
|
|
sudo firewall-cmd --reload
|
|
echo " ✓ firewalld configuré"
|
|
|
|
# Installation de containerd depuis le repo Docker
|
|
echo "Ajout du repo Docker pour containerd..."
|
|
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
|
|
|
echo "Installation de containerd..."
|
|
sudo dnf install -y containerd.io
|
|
|
|
sudo mkdir -p /etc/containerd
|
|
containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
|
|
|
|
# Activer systemd cgroup driver (OBLIGATOIRE pour kubeadm)
|
|
echo "Activation du driver cgroup systemd pour containerd..."
|
|
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
|
|
|
|
# Forcer le profil seccomp par défaut sur tous les containers
|
|
# POURQUOI: Sans cette option, les pods démarrent en seccomp "Unconfined" — tous les
|
|
# syscalls sont permis, y compris ceux utilisés pour l'évasion de container
|
|
# (keyctl, add_key, bpf, perf_event_open, etc.).
|
|
# Avec "runtime/default", containerd applique le profil seccomp standard
|
|
# qui bloque ~50 syscalls dangereux. Un pod qui en a besoin doit explicitement
|
|
# demander securityContext.seccompProfile.type=Unconfined (bloqué par PSA).
|
|
echo "Configuration du profil seccomp par défaut dans containerd..."
|
|
if ! grep -q 'unset_seccomp_profile' /etc/containerd/config.toml; then
|
|
sudo sed -i '/\[plugins\."io\.containerd\.grpc\.v1\.cri"\]/a\ unset_seccomp_profile = "runtime/default"' /etc/containerd/config.toml
|
|
fi
|
|
|
|
sudo systemctl restart containerd
|
|
sudo systemctl enable containerd
|
|
|
|
# Installation des paquets Kubernetes
|
|
echo "Configuration du repo Kubernetes..."
|
|
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
|
|
[kubernetes]
|
|
name=Kubernetes
|
|
baseurl=https://pkgs.k8s.io/core:/stable:/v1.34/rpm/
|
|
enabled=1
|
|
gpgcheck=1
|
|
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.34/rpm/repodata/repomd.xml.key
|
|
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
|
|
EOF
|
|
|
|
if [[ "$NODE_ROLE" == "master" ]]; then
|
|
echo "Installation de kubeadm, kubelet, kubectl (control plane)..."
|
|
sudo dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
|
|
else
|
|
echo "Installation de kubeadm, kubelet (worker)..."
|
|
sudo dnf install -y kubelet kubeadm --disableexcludes=kubernetes
|
|
fi
|
|
|
|
echo "Verrouillage des versions..."
|
|
sudo dnf install -y 'dnf-command(versionlock)' 2>/dev/null || true
|
|
if [[ "$NODE_ROLE" == "master" ]]; then
|
|
sudo dnf versionlock add kubelet kubeadm kubectl 2>/dev/null || echo "Note: versionlock non disponible"
|
|
else
|
|
sudo dnf versionlock add kubelet kubeadm 2>/dev/null || echo "Note: versionlock non disponible"
|
|
fi
|
|
|
|
# Installer Helm sur le master (requis pour Cilium, KubeArmor, Kyverno)
|
|
if [[ "$NODE_ROLE" == "master" ]]; then
|
|
if ! command -v helm &>/dev/null; then
|
|
echo "Installation de Helm..."
|
|
HELM_VERSION="v3.17.3"
|
|
curl -L "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz" -o /tmp/helm.tar.gz
|
|
tar -xzf /tmp/helm.tar.gz -C /tmp
|
|
sudo mv /tmp/linux-amd64/helm /usr/local/bin/helm
|
|
rm -rf /tmp/linux-amd64 /tmp/helm.tar.gz
|
|
echo " ✓ Helm ${HELM_VERSION} installé"
|
|
else
|
|
echo " ✓ Helm déjà installé: $(helm version --short)"
|
|
fi
|
|
fi
|
|
|
|
sudo systemctl enable kubelet
|
|
|
|
# Installation et configuration d'auditd
|
|
# POURQUOI: Lors du Kube Battle, on veut savoir QUI a fait QUOI sur les VMs.
|
|
# auditd trace les execve, l'écriture de fichiers sensibles, les changements
|
|
# de configuration K8s. Indispensable pour le post-mortem et la détection
|
|
# d'évasion de container (un syscall depuis l'hôte = trace).
|
|
echo "Installation d'auditd..."
|
|
sudo dnf install -y audit
|
|
sudo systemctl enable --now auditd
|
|
|
|
sudo tee /etc/audit/rules.d/k8s.rules > /dev/null <<'EOF'
|
|
-w /etc/kubernetes/ -p wa -k k8s-config
|
|
-w /var/lib/kubelet/ -p wa -k kubelet-data
|
|
-w /var/lib/etcd/ -p wa -k etcd-data
|
|
-w /etc/containerd/ -p wa -k containerd-config
|
|
-w /usr/bin/kubectl -p x -k kubectl-exec
|
|
-w /usr/bin/kubeadm -p x -k kubeadm-exec
|
|
-w /usr/bin/crictl -p x -k crictl-exec
|
|
-a always,exit -F arch=b64 -S execve -F euid=0 -k root-exec
|
|
-a always,exit -F arch=b64 -S mount -k mount-syscall
|
|
EOF
|
|
sudo augenrules --load 2>/dev/null || sudo systemctl restart auditd
|
|
echo " ✓ auditd configuré"
|
|
|
|
# Durcissement des permissions sur les répertoires sensibles
|
|
# POURQUOI: Les répertoires K8s contiennent les certificats, les kubeconfigs et les
|
|
# données etcd. En mode 755 (défaut), tout utilisateur de la VM peut les lire.
|
|
echo "Durcissement des permissions des répertoires Kubernetes..."
|
|
sudo install -d -m 0700 /etc/kubernetes 2>/dev/null || sudo chmod 700 /etc/kubernetes 2>/dev/null || true
|
|
sudo install -d -m 0700 /var/lib/kubelet 2>/dev/null || sudo chmod 700 /var/lib/kubelet 2>/dev/null || true
|
|
if [[ "$NODE_ROLE" == "master" ]]; then
|
|
sudo install -d -m 0700 /var/lib/etcd 2>/dev/null || sudo chmod 700 /var/lib/etcd 2>/dev/null || true
|
|
fi
|
|
echo " ✓ /etc/kubernetes, /var/lib/kubelet, /var/lib/etcd en mode 0700"
|
|
|
|
echo ""
|
|
echo "=== Vérifications ==="
|
|
echo "Swap désactivé: $(free -h | grep Swap | awk '{print $2}') (doit être 0)"
|
|
SELINUX_STATUS=$(getenforce 2>/dev/null || echo "Unknown")
|
|
if [[ "$SELINUX_STATUS" == "Enforcing" ]]; then
|
|
echo "SELinux: $SELINUX_STATUS ✓"
|
|
else
|
|
echo "SELinux: $SELINUX_STATUS ⚠ ATTENTION: doit être Enforcing"
|
|
fi
|
|
echo "Modules kernel: $(lsmod | grep -E 'overlay|br_netfilter|ip_vs|nf_conntrack' | wc -l)/7 chargés"
|
|
echo "firewalld: $(systemctl is-active firewalld)"
|
|
echo "auditd: $(systemctl is-active auditd)"
|
|
echo "containerd: $(systemctl is-active containerd)"
|
|
echo "SystemdCgroup: $(grep 'SystemdCgroup = true' /etc/containerd/config.toml > /dev/null && echo 'activé ✓' || echo 'ATTENTION: non activé!')"
|
|
echo "seccomp default: $(grep 'unset_seccomp_profile' /etc/containerd/config.toml > /dev/null && echo 'runtime/default ✓' || echo 'ATTENTION: Unconfined!')"
|
|
echo "kptr_restrict: $(sysctl -n kernel.kptr_restrict 2>/dev/null) (doit être 2)"
|
|
echo "ptrace_scope: $(sysctl -n kernel.yama.ptrace_scope 2>/dev/null) (doit être 2)"
|
|
echo ""
|
|
echo "✓ Pré-requis installés avec succès!"
|
|
echo "Version kubeadm: $(kubeadm version -o short)"
|
|
echo "Version kubelet: $(kubelet --version)"
|
|
if command -v kubectl &>/dev/null; then
|
|
echo "Version kubectl: $(kubectl version --client -o yaml | grep gitVersion)"
|
|
fi
|
|
if command -v helm &>/dev/null; then
|
|
echo "Version helm: $(helm version --short)"
|
|
fi
|