Security musings

Catégories

Tags

🔍 Licence d'Utilisation 🔍

Sauf mention contraire, le contenu de ce blog est sous licence CC BY-NC-ND 4.0.

© 2025 à 2042 Sébastien Gioria. Tous droits réservés.

Sécuriser Kubernetes n’est pas une démarche optionnelle : il s’agit d’un impératif opérationnel. Les clusters contiennent souvent des secrets, gèrent l’exécution d’applications critiques et sont connectés aux pipelines CI/CD — autant de vecteurs qui rendent une compromission coûteuse.

L’étendue de la surface d’attaque Kubernetes

Composants exposés et points d’entrée

Un cluster Kubernetes présente plusieurs interfaces potentiellement vulnérables :

Plan de contrôle :

  • API server (port 6443) - point d’entrée principal
  • etcd (port 2379/2380) - base de données du cluster
  • Controller manager et scheduler

Noeuds worker :

  • kubelet (port 10250) - API locale des noeuds
  • kube-proxy - gestion du réseau
  • Runtime de conteneurs (Docker/containerd)

Applications et services :

  • Services LoadBalancer exposés sur Internet
  • Ingress controllers
  • Dashboards et interfaces d’administration
# Audit rapide des ports exposés
#!/bin/bash
echo "=== Services exposés publiquement ==="
kubectl get services -A --field-selector spec.type=LoadBalancer
kubectl get services -A --field-selector spec.type=NodePort

echo "\n=== Ingress exposés ==="
kubectl get ingress -A

echo "\n=== Ports ouverts sur les noeuds ==="
# À exécuter sur chaque noeud
# netstat -tlnp | grep -E ':(6443|10250|2379|2380)\b'

echo "\n=== Dashboards potentiellement exposés ==="
kubectl get pods -A | grep -i dashboard
kubectl get services -A | grep -i dashboard

Identités et permissions : l’escalade silencieuse

Les erreurs RBAC sont particulièrement pernicieuses car elles permettent une escalade progressive :

# Exemple de permissions problématiques (NE PAS FAIRE)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dangerous-binding
subjects:
- kind: ServiceAccount
  name: default  # ⚠️ ServiceAccount par défaut
  namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-admin  # ⚠️ Permissions administrateur
  apiGroup: rbac.authorization.k8s.io
# Détection des permissions excessives
echo "=== Bindings dangereux ==="
kubectl get clusterrolebindings -o json | jq -r '.items[] | select(.roleRef.name == "cluster-admin") | .metadata.name'

echo "\n=== ServiceAccounts avec accès aux secrets ==="
kubectl get roles,clusterroles -A -o json | \
  jq -r '.items[] | select(.rules[]? | .resources[]? == "secrets" and (.verbs[]? | IN("get", "list", "*"))) | "\(.metadata.namespace // "cluster")/\(.metadata.name)"'

echo "\n=== Tokens ServiceAccount non expirés ==="
kubectl get secrets -A -o json | \
  jq -r '.items[] | select(.type == "kubernetes.io/service-account-token") | "\(.metadata.namespace)/\(.metadata.name)"'

Scénarios d’incidents concrets et réels

Cas 1 : Tesla - Cryptominage via cluster Kubernetes non sécurisé (2018)

Vecteur d’attaque : Dashboard Kubernetes exposé sans authentification
Impact : Mining de cryptomonnaie, accès aux credentials AWS
Leçons :

  • Toujours authentifier les interfaces d’administration
  • Surveiller l’usage CPU anormal
  • Limiter l’accès des pods aux métadonnées cloud
# Protection contre l'accès aux métadonnées AWS/Azure
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-metadata-access
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  # Bloquer l'accès aux métadonnées
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 169.254.169.254/32  # AWS metadata
        - 168.63.129.16/32    # Azure metadata
    ports:
    - protocol: TCP
      port: 80
    - protocol: TCP
      port: 443

Cas 2 : Exfiltration via ServiceAccount sur-privilégié

Scénario type :

  1. Pod applicatif compromis (vulnérabilité web)
  2. ServiceAccount avec permissions de lecture sur tous les secrets
  3. Exfiltration des credentials de base de données et API keys
# Simulation d'attaque depuis un pod compromis
# Ces commandes montrent ce qu'un attaquant peut faire

# 1. Découverte de l'environnement
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace

# 2. Test des permissions
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)

curl -k -H "Authorization: Bearer $TOKEN" \
  https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/secrets

# 3. Exfiltration si permissions insuffisantes
curl -k -H "Authorization: Bearer $TOKEN" \
  https://kubernetes.default.svc/api/v1/secrets | jq '.items[].data'

Contre-mesures :

# ServiceAccount avec permissions minimales
apiVersion: v1
kind: ServiceAccount
metadata:
  name: restricted-app
  namespace: production
automountServiceAccountToken: false  # Désactiver le token auto
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: app-minimal
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]
  resourceNames: ["app-config"]  # Seulement cette ConfigMap

Cas 3 : Compromission de la chaîne CI/CD

Vecteur : Repository compromis -> image malveillante -> déploiement automatique
Impact : Backdoor dans tous les environnements, escalade vers l’infrastructure

# Policy d'admission pour empêcher les images non signées
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: enforce
  background: false
  rules:
  - name: check-signature
    match:
      any:
      - resources:
          kinds:
          - Pod
    verifyImages:
    - image: "registry.company.com/*"
      key: |-
        -----BEGIN PUBLIC KEY-----
        MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
        -----END PUBLIC KEY-----

Quantification des risques et priorités

Métriques de sécurité Kubernetes

#!/bin/bash
# Script de calcul d'un "score de sécurité" basique

SCORE=100
echo "=== Évaluation de sécurité Kubernetes ==="

# Vérifier les bindings cluster-admin (-20 points chacun)
ADMIN_BINDINGS=$(kubectl get clusterrolebindings -o json | jq '[.items[] | select(.roleRef.name == "cluster-admin")] | length')
SCORE=$((SCORE - ADMIN_BINDINGS * 20))
echo "ClusterRoleBindings cluster-admin: $ADMIN_BINDINGS (-$((ADMIN_BINDINGS * 20)) points)"

# Vérifier les pods privilégiés (-15 points chacun)
PRIV_PODS=$(kubectl get pods -A -o json | jq '[.items[] | select(.spec.securityContext.privileged == true)] | length')
SCORE=$((SCORE - PRIV_PODS * 15))
echo "Pods privilégiés: $PRIV_PODS (-$((PRIV_PODS * 15)) points)"

# Vérifier les NetworkPolicies (+10 points si présentes)
NETWORK_POLICIES=$(kubectl get networkpolicies -A --no-headers 2>/dev/null | wc -l)
if [ "$NETWORK_POLICIES" -gt 0 ]; then
    SCORE=$((SCORE + 10))
    echo "NetworkPolicies présentes: $NETWORK_POLICIES (+10 points)"
else
    SCORE=$((SCORE - 30))
    echo "Aucune NetworkPolicy (-30 points)"
fi

# Services LoadBalancer exposés (-10 points chacun)
LB_SERVICES=$(kubectl get services -A --field-selector spec.type=LoadBalancer --no-headers | wc -l)
SCORE=$((SCORE - LB_SERVICES * 10))
echo "Services LoadBalancer: $LB_SERVICES (-$((LB_SERVICES * 10)) points)"

echo "\n=== Score final: $SCORE/100 ==="
if [ "$SCORE" -ge 80 ]; then
    echo "Status: EXCELLENT ✅"
elif [ "$SCORE" -ge 60 ]; then
    echo "Status: BON ✅"
elif [ "$SCORE" -ge 40 ]; then
    echo "Status: AMÉLIORABLE ⚠️"
else
    echo "Status: CRITIQUE ❌"
fi

Coût de la non-sécurité

Estimation des impacts business :

Scénario Probabilité Impact financier Temps réparation Coût réputation
Fuite de secrets Haute 50k-500k€ 1-3 jours Moyen
Cryptominage Moyenne 5k-50k€ Quelques heures Faible
Ransom/DoS Faible 100k-1M€ 1 semaine Élevé
Compromission client Très faible 1M€+ Plusieurs mois Critique

KPIs de sécurité recommandés :

# Script de monitoring des KPIs sécurité
#!/bin/bash

echo "Date,Pods_Privilegies,Services_LoadBalancer,Bindings_ClusterAdmin,NetworkPolicies" > security_metrics.csv

while true; do
    DATE=$(date +"%Y-%m-%d %H:%M")
    PRIV=$(kubectl get pods -A -o json | jq '[.items[] | select(.spec.securityContext.privileged == true)] | length')
    LB=$(kubectl get svc -A --field-selector spec.type=LoadBalancer --no-headers | wc -l)
    ADMIN=$(kubectl get clusterrolebindings -o json | jq '[.items[] | select(.roleRef.name == "cluster-admin")] | length')
    NP=$(kubectl get networkpolicies -A --no-headers | wc -l)
    
    echo "$DATE,$PRIV,$LB,$ADMIN,$NP" >> security_metrics.csv
    sleep 3600  # Chaque heure
done

Priorités d’action pour les équipes

Matrice de priorité (Impact vs Effort)

Actions à faire immédiatement (Fort impact, Faible effort) :

  1. Désactiver le ServiceAccount default
  2. Ajouter des NetworkPolicies de base (deny-all)
  3. Scanner les images avec Trivy
  4. Auditer les ClusterRoleBindings

Actions à planifier (Fort impact, Effort moyen) :

  1. Implémenter des admission controllers
  2. Chiffrer etcd au repos
  3. Configurer l’audit logging
  4. Déployer Falco pour la surveillance runtime

Actions long terme (Impact variable, Effort élevé) :

  1. Migration vers des runtimes sécurisés (gVisor, Kata)
  2. Implémentation de l’identité workload (SPIFFE/SPIRE)
  3. Zero-trust networking complet
  4. Automatisation complète des audits
# Checklist d'actions rapides (< 30 minutes)
#!/bin/bash
echo "=== Actions immédiates de sécurité Kubernetes ==="

# 1. Désactiver automount des tokens par défaut
kubectl patch serviceaccount default -p '{"automountServiceAccountToken":false}'

# 2. Créer une NetworkPolicy deny-all basique
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Ingress
EOF

# 3. Scanner une image critique
# trivy image nginx:latest

# 4. Audit rapide RBAC
echo "\n=== Bindings à vérifier ==="
kubectl get clusterrolebindings -o json | \
  jq -r '.items[] | select(.roleRef.name | IN("cluster-admin", "admin")) | .metadata.name'

echo "\nActions terminées. Vérifiez les résultats et planifiez les actions suivantes."

Ressources pour l’évaluation des risques

Frameworks et méthodologies :

Outils d’évaluation :

La sécurité Kubernetes n’est pas un état mais un processus continu. En comprenant les enjeux financiers et opérationnels, vous pourrez justifier les investissements nécessaires et prioriser efficacement vos actions. Le prochain article détaillera les risques spécifiques à surveiller.