## Introduction

Seccomp (Secure Computing Mode) dans Kubernetes est une fonctionnalité du noyau Linux qui restreint les appels système qu'un conteneur peut effectuer, réduisant ainsi la surface d'attaque en cas de compromission d'un conteneur. Gérer les profils seccomp lors d'une mise à niveau ou d'une migration Kubernetes est une opération délicate : un profil mal configuré peut casser le fonctionnement d'une application ou, pire, affaiblir silencieusement la sécurité. Ce guide fournit une approche pratique et éprouvée sur le terrain pour la mise à niveau et la migration de Seccomp dans Kubernetes, destinée aux développeurs, consultants DevOps et équipes techniques de startups qui doivent passer d'un problème observé à un résultat vérifié.

Nous couvrirons l'ensemble du cycle de vie : inventaire des versions et de l'environnement, chemins de configuration sûrs, vérification et diagnostic, modes de défaillance et récupération, ainsi qu'une liste de contrôle opérationnelle. Chaque section comprend des commandes concrètes, les sorties attendues, les signaux de défaillance et les décisions de récupération. Notre objectif est la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, utiliser des espaces réservés au lieu de secrets, vérifier le résultat et documenter comment récupérer si l'état attendu n'est pas atteint.

## Inventaire des versions et de l'environnement

Avant toute mise à niveau ou migration de seccomp, vous devez comprendre votre environnement actuel. Cette section fournit un inventaire systématique des versions, des prérequis et de l'état actuel.

### Identifier les versions de Kubernetes et du runtime de conteneurs

Le support de seccomp et le comportement par défaut dépendent de la version de Kubernetes et du runtime de conteneurs. Exécutez les commandes suivantes pour capturer votre environnement :

kubectl version --short
# Exemple de sortie :
# Client Version: v1.27.3
# Server Version: v1.27.3 
 Pour le runtime de conteneurs (en supposant containerd) :

kubectl get nodes -o wide
# Exemple de sortie :
# NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
# worker-1 Ready <none> 10d v1.27.3 10.0.0.4 <none> Ubuntu 22.04.2 LTS 5.15.0-76-generic containerd://1.7.2 
 Notez la version du runtime de conteneurs ; la mise en place par défaut de seccomp a été introduite dans Kubernetes v1.25 et est stable en v1.27. Si vos nœuds exécutent une version plus ancienne de Kubernetes ou un runtime sans cette mise en place par défaut, votre chemin de mise à niveau sera différent.

### Vérifier l'utilisation actuelle de Seccomp sur les charges de travail

Pour comprendre quels pods utilisent des profils seccomp, inspectez le contexte de sécurité des pods en cours d'exécution. Utilisez la commande suivante pour lister les pods avec leurs annotations seccomp (si vous utilisez la méthode d'annotation obsolète) ou les champs securityContext :

kubectl get pods --all-namespaces -o json | jq -r '.items[] | select(.spec.securityContext.seccompProfile != null or .metadata.annotations."seccomp.security.alpha.kubernetes.io/pod" != null) | .metadata.namespace + "/" + .metadata.name' 
 Si vous utilisez la deprecated annotation alpha, vous devez migrer vers le champ seccompProfile dans le contexte de sécurité du pod. L'annotation seccomp.security.alpha.kubernetes.io/pod a été dépréciée en v1.19 et supprimée en v1.25.

### Inventorier les profils Seccomp existants

Les profils seccomp personnalisés sont généralement stockés sous forme de fichiers sur le nœud ou de ConfigMaps. Pour trouver les profils basés sur ConfigMap :

kubectl get configmaps --all-namespaces -o json | jq -r '.items[] | select(.data | keys[] | test("seccomp")) | .metadata.namespace + "/" + .metadata.name' 
 Pour les profils locaux au nœud, vous devrez vérifier le répertoire par défaut des profils seccomp, généralement /var/lib/kubelet/seccomp/ . Vous pouvez utiliser un DaemonSet pour inspecter les fichiers du nœud, mais pour l'inventaire, il peut suffire de savoir si des pods référencent des profils locaux.

### Prérequis pour la mise à niveau

- Version de Kubernetes : Au moins v1.25 pour le champ stable seccompProfile et la mise en place par défaut vers RuntimeDefault . Vérifiez avec kubectl version .

- Runtime de conteneurs : Doit supporter seccomp et le profil RuntimeDefault . containerd et CRI-O le font ; Docker Engine avec les dépréciations peut nécessiter une configuration supplémentaire.

- Permissions RBAC : Vous avez besoin des permissions get et list sur les pods et les configmaps dans tous les namespaces, et patch ou update sur les charges de travail que vous comptez modifier.

- Environnement de test : Un cluster de staging ou de développement avec le même runtime et la même version de Kubernetes pour valider les modifications avant la production.

### Commandes d'observation en lecture seule

Avant de faire des modifications, enregistrez le comportement de base :

# Lister tous les pods et leur statut
kubectl get pods -A -o wide

# Décrire un pod spécifique pour voir les événements et le contexte de sécurité
kubectl describe pod <pod-name> -n <namespace>

# Vérifier les journaux d'un conteneur en cours d'exécution
kubectl logs <pod-name> -n <namespace>

# Vérifier les journaux d'un conteneur qui a planté
kubectl logs <pod-name> -n <namespace> --previous 

### Plus petit changement justifié

 Pour une mise à niveau seccomp, le plus petit changement justifié pourrait être d'activer RuntimeDefault sur un seul pod de test qui a actuellement Unconfined . Cela permet de vérifier que le profil seccomp par défaut du runtime ne casse pas l'application avant de l'appliquer à plus grande échelle.

Exemple de manifeste avant changement ( unconfined-pod.yaml ) :

apiVersion: v1
kind: Pod
metadata:
 name: test-pod
spec:
 containers:
 - name: app
 image: nginx:1.25
 securityContext:
 seccompProfile:
 type: Unconfined 
 Après changement ( runtime-default-pod.yaml ) :

apiVersion: v1
kind: Pod
metadata:
 name: test-pod
spec:
 containers:
 - name: app
 image: nginx:1.25
 securityContext:
 seccompProfile:
 type: RuntimeDefault 
 Appliquez et vérifiez :

kubectl apply -f runtime-default-pod.yaml
kubectl get pod test-pod
kubectl logs test-pod 
 Si le pod fonctionne et que les journaux montrent un démarrage normal, vous pouvez procéder à l'extension du changement.

### Rayon d'impact et chemin de récupération

Testez toujours sur une charge de travail non critique d'abord. Si le pod échoue avec des violations seccomp (par exemple, « opération non permise » dans les journaux), vous pouvez rapidement revenir en arrière en réappliquant le manifeste précédent avec Unconfined . Documentez cette étape de récupération avant de faire le changement.

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Question rapide">
Question rapide 1 sur 2

Quelle est la version minimale de Kubernetes requise pour utiliser le champ stable `seccompProfile` et la valeur par défaut `RuntimeDefault` ?

- v1.19
- v1.23
- v1.25
- v1.27

<button type="button" class="eno-quiz-submit" disabled>Valider</button>
<div class="eno-quiz-explanation">D&#39;après la référence, la définition par défaut de seccomp a été introduite dans Kubernetes v1.25 et est stable dans v1.27. Le champ stable `seccompProfile` est disponible à partir de la v1.25.</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Chemin de configuration sûr

Cette section détaille comment configurer les profils seccomp en toute sécurité lors d'une mise à niveau ou d'une migration. Nous couvrons à la fois les profils intégrés et les profils personnalisés.

### Comprendre les types de profils Seccomp

Kubernetes supporte trois types de profils seccomp dans le champ seccompProfile :

- Unconfined : Aucune restriction seccomp (par défaut si non spécifié dans de nombreux anciens clusters, mais non recommandé).

- RuntimeDefault : Utilise le profil seccomp par défaut du runtime de conteneurs, généralement sûr pour la plupart des applications.

- Localhost : Utilise un profil personnalisé défini dans un fichier sur le nœud sous le répertoire racine des profils seccomp du kubelet (généralement /var/lib/kubelet/seccomp/ ).

### Migration des annotations obsolètes vers les champs du contexte de sécurité

Si vos charges de travail utilisent encore l'annotation alpha seccomp.security.alpha.kubernetes.io/pod , vous devez migrer vers le champ seccompProfile . L'annotation est interprétée comme un chemin relatif à la racine des profils seccomp, et elle est équivalente à type: Localhost avec localhostProfile: <path> .

Exemple d'ancienne annotation :

metadata:
 annotations:
 seccomp.security.alpha.kubernetes.io/pod: "localhost/profiles/audit.json" 
 Nouveau champ équivalent :

spec:
 securityContext:
 seccompProfile:
 type: Localhost
 localhostProfile: "profiles/audit.json" 
 Notez que le chemin localhostProfile ne doit pas inclure le préfixe « localhost/ » ; il est relatif à la racine des profils seccomp.

Pour automatiser cette migration, vous pouvez utiliser kubectl avec un patch JSON ou un outil comme kube-neat pour réécrire les manifestes. Pour un grand parc, envisagez d'utiliser un moteur de politiques comme Kyverno ou OPA Gatekeeper pour muter les pods à l'admission.

Exemple de patch kubectl pour un déploiement :

kubectl patch deployment myapp -n production --type='json' -p='[{"op": "remove", "path": "/spec/template/metadata/annotations/seccomp.security.alpha.kubernetes.io~1pod"}, {"op": "add", "path": "/spec/template/spec/securityContext/seccompProfile", "value": {"type": "Localhost", "localhostProfile": "profiles/audit.json"}}]' 
 Attention : Le ~1 est l'échappement du pointeur JSON pour / . Testez cela sur un déploiement de staging d'abord.

### Création et déploiement de profils Seccomp personnalisés

Les profils seccomp personnalisés sont des fichiers JSON qui définissent les appels système autorisés, l'action par défaut, et plus encore. Voici un profil personnalisé minimal qui refuse l'appel système chmod et journalise tous les autres :

custom-profile.json :

{
 "defaultAction": "SCMP_ACT_LOG",
 "architectures": [
 "SCMP_ARCH_X86_64",
 "SCMP_ARCH_X86",
 "SCMP_ARCH_X32"
 ],
 "syscalls": [
 {
 "names": ["chmod"],
 "action": "SCMP_ACT_ERRNO"
 }
 ]
} 
 Pour déployer ce profil sur les nœuds, vous devez le placer dans le répertoire des profils seccomp sur chaque nœud. La méthode sûre la plus simple est d'utiliser un DaemonSet qui copie le profil d'une ConfigMap vers le système de fichiers du nœud. Voici un exemple de DaemonSet :

apiVersion: apps/v1
kind: DaemonSet
metadata:
 name: seccomp-profile-installer
 namespace: kube-system
spec:
 selector:
 matchLabels:
 app: seccomp-profile-installer
 template:
 metadata:
 labels:
 app: seccomp-profile-installer
 spec:
 initContainers:
 - name: installer
 image: alpine:3.18
 command: ["sh", "-c", "cp /profiles/* /host/seccomp/ && chmod 644 /host/seccomp/*"]
 volumeMounts:
 - name: profiles
 mountPath: /profiles
 - name: host-seccomp
 mountPath: /host/seccomp
 containers:
 - name: pause
 image: gcr.io/google_containers/pause:3.9
 volumes:
 - name: profiles
 configMap:
 name: seccomp-profiles
 - name: host-seccomp
 hostPath:
 path: /var/lib/kubelet/seccomp
 type: DirectoryOrCreate 
 Assurez-vous que le hostPath est le même que la racine des profils seccomp du kubelet. Vous devrez peut-être ajuster le chemin en fonction de votre distribution Kubernetes.

### Application des profils Seccomp aux charges de travail

Pour appliquer un profil personnalisé à un pod, utilisez le type Localhost avec le chemin relatif à la racine des profils :

apiVersion: v1
kind: Pod
metadata:
 name: custom-seccomp-pod
spec:
 securityContext:
 seccompProfile:
 type: Localhost
 localhostProfile: "custom-profile.json"
 containers:
 - name: app
 image: busybox:1.36
 command: ["sh", "-c", "sleep 3600"] 
 Appliquez et testez :

kubectl apply -f custom-seccomp-pod.yaml
kubectl exec -it custom-seccomp-pod -- sh -c "chmod 777 /tmp/test"
# Attendu : Opération non permise
kubectl exec -it custom-seccomp-pod -- sh -c "ls /tmp"
# Attendu : fonctionne correctement 
 L'appel système chmod devrait retourner EPERM, et les journaux du profil peuvent être inspectés avec dmesg | grep seccomp sur le nœud.

### Stratégie de déploiement progressif

Lors de la mise à niveau des profils seccomp sur de nombreuses charges de travail, utilisez un déploiement progressif :

- Mode audit : Déployez le profil avec defaultAction: SCMP_ACT_LOG pour journaliser les refus d'appels système sans les appliquer (comme montré dans le profil personnalisé ci-dessus). Surveillez les journaux pour les appels système légitimes qui seraient bloqués en mode application.

- Déploiement canari : Appliquez le profil en mode application à une seule réplique ou à un déploiement canari. Surveillez les métriques de l'application et les journaux.

- Étendre progressivement : Augmentez le pourcentage de pods utilisant le nouveau profil tout en surveillant.

- Déploiement complet : Une fois confiant, appliquez à toutes les charges de travail.

Pour implémenter le mode audit avec le profil RuntimeDefault , vous ne pouvez pas directement définir l'audit ; vous devez créer un profil personnalisé qui imite le défaut du runtime mais journalise au lieu de bloquer. Cependant, pour de nombreux runtimes, le profil par défaut peut être copié et modifié.

## Vérification et diagnostic

La vérification garantit que les profils seccomp sont correctement appliqués et que les charges de travail fonctionnent comme prévu. Cette section fournit des commandes et des techniques concrètes.

### Vérifier que le profil Seccomp est appliqué à un pod

Utilisez kubectl get pod -o yaml pour inspecter le contexte de sécurité du pod :

kubectl get pod myapp-<hash> -n production -o jsonpath='{.spec.securityContext.seccompProfile}{"\n"}'
# Exemple de sortie : {"type":"RuntimeDefault"} 
 Si le pod hérite de seccomp d'un niveau supérieur (par exemple, PodSecurityPolicy, maintenant remplacé par Pod Security Admission, ou une politique au niveau du namespace), le champ peut ne pas être directement défini. Vous pouvez vérifier le seccomp effectif en examinant la configuration du runtime de conteneurs sur le nœud, mais c'est plus avancé. Dans la plupart des cas, si le champ est défini, il est effectif.

### Vérifier les refus liés à Seccomp dans les journaux

Si une application est bloquée par seccomp, les journaux du conteneur peuvent montrer des erreurs comme « Operation not permitted » ou « Invalid argument », et les journaux du noyau du nœud auront des messages d'audit seccomp.

Pour voir les journaux du pod :

kubectl logs myapp-<hash> -n production --tail=50 
 Exemple de journal de refus d'un conteneur :

2024/05/20 10:15:32 [error] 1234#1234: *1 chmod() "/var/www/html/test" failed (1: Operation not permitted) 
 Pour voir les journaux du noyau du nœud (si vous avez accès au nœud) :

journalctl -k | grep seccomp
# Exemple de sortie :
# audit: type=1326 audit(1716198932.123:456): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=1234 comm="nginx" exe="/usr/sbin/nginx" sig=0 arch=c000003e syscall=90 compat=0 ip=0x7f... code=0x50000 
 Dans cet exemple, l'appel système 90 est chmod sur x86_64.

### Utiliser Seccomp Notify pour un diagnostic avancé

Certains runtimes supportent seccomp notify, qui permet à un processus en espace utilisateur de gérer les décisions seccomp. C'est complexe et dépasse le cadre de ce guide ; cependant, des outils comme oci-seccomp-bpf-hook peuvent aider à simuler et déboguer les profils.

### Tester les profils en isolation

Créez un pod de test qui exécute la même image de conteneur et les mêmes commandes que votre charge de travail de production, mais avec le profil seccomp candidat. Utilisez kubectl run pour des tests rapides :

kubectl run seccomp-test --image=nginx:1.25 --restart=Never --dry-run=client -o yaml | kubectl apply -f - 
 Ensuite, patchez le pod de test avec le profil souhaité et exécutez des commandes spécifiques à l'application dans le conteneur avec exec .

### Valider la syntaxe du profil

Avant de déployer un profil seccomp personnalisé, validez la syntaxe JSON et la sémantique. Utilisez jq pour parser :

jq empty custom-profile.json && echo "Valid JSON" 
 Pour une validation sémantique, vous pouvez utiliser l'outil seccomp de libseccomp ou un validateur en ligne. Il n'y a pas de validation intégrée dans Kubernetes pour le contenu des profils ; des profils invalides peuvent empêcher les conteneurs de démarrer.

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Question rapide">
Question rapide 2 sur 2

Lorsqu'un Pod est exécuté en tant que conteneur privilégié, quel profil seccomp utilise-t-il ?

- RuntimeDefault
- Localhost
- Unconfined
- Le profil spécifié dans le manifeste du Pod

<button type="button" class="eno-quiz-submit" disabled>Valider</button>
<div class="eno-quiz-explanation">Les conteneurs privilégiés s&#39;exécutent avec le profil seccomp `Unconfined`, en remplaçant tout profil seccomp spécifié dans le manifeste.</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Modes de défaillance et récupération

Malgré une planification soignée, des échecs peuvent survenir. Cette section identifie les modes de défaillance courants et fournit des procédures de récupération.

### Le pod ne démarre pas avec une erreur Seccomp

Symptôme : Le pod reste en ContainerCreating ou CrashLoopBackOff , et kubectl describe pod montre une erreur comme :

Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting seccomp filter: invalid argument 
 Cause : Profil seccomp invalide (erreur de syntaxe, architecture non supportée, etc.) ou fichier de profil manquant.

Récupération :

- Vérifiez si le fichier de profil existe sur le nœud et a les bonnes permissions.

- Validez le JSON du profil.

- Si le profil est invalide, corrigez-le et redéployez.

- En atténuation immédiate, changez le type seccompProfile du pod en Unconfined (temporairement) ou RuntimeDefault pour permettre au pod de démarrer.

kubectl patch pod myapp-<hash> -n production --type='json' -p='[{"op": "replace", "path": "/spec/securityContext/seccompProfile", "value": {"type": "Unconfined"}}]' 
 Note : Vous ne pouvez pas patcher le securityContext d'un pod en cours d'exécution ; vous devez supprimer et recréer le pod ou mettre à jour le déploiement.

### L'application casse après l'application de Seccomp

Symptôme : Le pod démarre mais les fonctionnalités de l'application échouent (par exemple, impossible d'écrire des fichiers, les appels réseau échouent). Les journaux montrent « Operation not permitted » pour des appels système que le profil bloque.

Cause : Le profil seccomp est trop restrictif pour les appels système légitimes de l'application.

Récupération :

- Identifiez quel appel système est bloqué à partir des journaux ou de l'audit seccomp.

- Modifiez le profil personnalisé pour autoriser cet appel système (ajoutez-le dans names avec l'action SCMP_ACT_ALLOW ).

- Si vous utilisez RuntimeDefault et qu'il est trop restrictif, vous devrez peut-être passer à Unconfined ou créer un profil personnalisé basé sur le défaut du runtime avec des autorisations supplémentaires.

- Revenez en arrière sur le déploiement à la version précédente avec une configuration seccomp connue comme bonne.

Exemple de commande de rollback :

kubectl rollout undo deployment/myapp -n production 

### Fichier de profil manquant sur certains nœuds

 Symptôme : Les pods planifiés sur certains nœuds échouent avec « cannot load seccomp profile », tandis que d'autres réussissent.

Cause : Le DaemonSet qui copie les profils n'a pas fonctionné ou a échoué sur ces nœuds, ou la racine seccomp du kubelet diffère.

Récupération :

- Vérifiez le statut du DaemonSet : kubectl get ds seccomp-profile-installer -n kube-system .

- Vérifiez les pods sur les nœuds en échec : kubectl get pods -n kube-system -o wide | grep seccomp-profile-installer .

- Assurez-vous que le hostPath est correct ; certaines distributions Kubernetes utilisent /var/lib/kubelet/seccomp tandis que d'autres utilisent /var/lib/rancher/rke2/agent/containerd/seccomp ou similaire.

- Copiez manuellement le profil sur le nœud comme mesure d'urgence.

### La mise à niveau de Kubernetes provoque un changement de comportement de Seccomp

Symptôme : Après la mise à niveau de la version de Kubernetes, des pods qui fonctionnaient auparavant avec des profils seccomp personnalisés échouent.

Cause : La sémantique du champ seccomp a changé, ou le profil par défaut du runtime a changé.

Récupération :

- Consultez le changelog de Kubernetes pour les changements liés à seccomp.

- Assurez-vous que toutes les annotations obsolètes sont migrées avant de passer à la v1.25+.

- Si le profil RuntimeDefault a changé, testez vos applications contre le nouveau défaut dans un environnement de staging.

- Si nécessaire, figez la version du runtime ou ajustez les profils pour correspondre au nouveau comportement.

### Liste de contrôle générale de récupération

- Documentez les commandes de rollback pour chaque changement.

- Utilisez le contrôle de version pour les manifestes et les profils ; git revert peut rapidement revenir en arrière sur la configuration.

- Implémentez des déploiements canari pour limiter l'impact.

- Surveillez les refus seccomp avec des métriques ou l'agrégation de journaux pour détecter les problèmes tôt.

## Liste de contrôle opérationnelle

Utilisez cette liste de contrôle avant, pendant et après une mise à niveau ou une migration seccomp pour garantir l'exhaustivité.

### Avant la mise à niveau

- [ ] Vérifiez que la version de Kubernetes supporte le champ seccomp stable (>= v1.25). Exécutez kubectl version --short .

- [ ] Inventoriez tous les pods utilisant des annotations ou des champs seccomp avec kubectl get pods -A -o json .

- [ ] Identifiez les profils seccomp personnalisés et leurs emplacements (ConfigMaps, fichiers sur les nœuds).

- [ ] Validez la syntaxe JSON des profils personnalisés.

- [ ] Testez le profil candidat dans un environnement de staging avec des charges de travail représentatives.

- [ ] Configurez l'agrégation de journaux pour les journaux d'audit du noyau afin de capturer les refus seccomp.

- [ ] Documentez le plan de rollback pour chaque charge de travail.

### Pendant la mise à niveau

- [ ] Migrez les annotations vers le champ seccompProfile en utilisant un patch ou un moteur de politiques.

- [ ] Déployez les profils personnalisés sur tous les nœuds via DaemonSet ; vérifiez que les pods du DaemonSet tournent sur tous les nœuds.

- [ ] Appliquez d'abord les changements seccomp aux charges de travail canari et surveillez pendant 24 à 48 heures.

- [ ] Vérifiez les journaux de l'application pour les appels système refusés ; ajustez les profils si nécessaire.

- [ ] Étendez progressivement aux charges de travail restantes par lots.

### Vérification après la mise à niveau

- [ ] Confirmez que le profil seccomp est effectif sur les pods avec kubectl get pod -o yaml .

- [ ] Exécutez la suite de tests spécifique à l'application pour garantir la pleine fonctionnalité.

- [ ] Surveillez les journaux du noyau des nœuds pour des refus seccomp inattendus pendant une semaine.

- [ ] Passez en revue la posture de sécurité : aucun pod ne devrait tourner en Unconfined sauf si explicitement requis.

### Exemples de commandes de vérification

# Vérifier le type seccomp pour tous les pods d'un namespace
kubectl get pods -n production -o json | jq -r '.items[] | .metadata.name + " -> " + (.spec.securityContext.seccompProfile.type // "Unconfined")'

# Sortie attendue (abrégée) :
# myapp-6d4b7c9f8-abcde -> RuntimeDefault
# myapp-6d4b7c9f8-fghij -> RuntimeDefault
# legacy-app-5c8b6f4d9-klmno -> Localhost 
 Si un pod montre Unconfined de manière inattendue, enquêtez et appliquez le profil prévu.

## Conclusion

La mise à niveau et la migration de Seccomp dans Kubernetes nécessitent une planification et une exécution soignées. En suivant le guide de terrain de cet article, vous pouvez minimiser les risques et assurer une transition en douceur vers un cluster plus sécurisé. Rappelez-vous de :

- Observer avant de modifier : Inventoriez l'état actuel et validez les profils dans un environnement de test.

- Limiter le rayon d'impact : Utilisez des déploiements canari et un déploiement progressif.

- Vérifier les résultats : Vérifiez les contextes de sécurité des pods et surveillez les refus.

- Planifier la récupération : Documentez les commandes de rollback et gardez les manifestes sous contrôle de version.

Comme prochaine étape, choisissez une charge de travail à faible risque et appliquez le profil seccomp RuntimeDefault (s'il n'est pas déjà défini). Enregistrez l'état actuel, exécutez la charge de travail, surveillez pendant une journée, puis étendez à d'autres charges de travail. Si vous rencontrez des problèmes, utilisez la section sur les modes de défaillance pour diagnostiquer et récupérer. Avec ces pratiques, vous pouvez obtenir un environnement Kubernetes sécurisé et stable.