Skip to content

🐛 [BUG] - CrashLoopBackOff gitlab-shell et minio sous profil CIS (mismatch UID init/main container) #1082

Description

@Baran-Aksoy

Description

Symptôme

J'ai deux pods distincts avec le même symptôme — les deux restent en CrashLoopBackOff dans le namespace dso-gitlab :

gitlab-gitlab-shell-* — logs du conteneur principal gitlab-shell :

Begin parsing .tpl templates from /etc/gitlab-shell
Writing /srv/gitlab-shell/config.yml
Copying other config files found in /etc/gitlab-shell to /srv/gitlab-shell
Using existing Host Keys
cp: cannot open '/etc/gitlab-secrets/ssh/ssh_host_ecdsa_key' for reading: Permission denied
cp: cannot open '/etc/gitlab-secrets/ssh/ssh_host_ecdsa_key.pub' for reading: Permission denied
cp: cannot open '/etc/gitlab-secrets/ssh/ssh_host_ed25519_key' for reading: Permission denied
cp: cannot open '/etc/gitlab-secrets/ssh/ssh_host_ed25519_key.pub' for reading: Permission denied
cp: cannot open '/etc/gitlab-secrets/ssh/ssh_host_rsa_key' for reading: Permission denied
cp: cannot open '/etc/gitlab-secrets/ssh/ssh_host_rsa_key.pub' for reading: Permission denied

gitlab-minio-* — logs du conteneur principal minio (12 redémarrages au moment où j'ai fait le diagnostic) :

time="2026-08-07T14:27:06Z" level=fatal msg="Config migration failed." cause="Failed to migrate
config from '20' to '21'. rename /tmp/.minio/$tmpfile.config.json.157491455 /tmp/.minio/config.json:
operation not permitted" source="[common-main.go:45:initConfig()]"

J'ai identifié que les deux erreurs viennent du même défaut sous-jacent (détaillé plus bas) — un Permission denied explicite pour gitlab-shell, un operation not permitted sur un rename pour minio (rejeté par le noyau Linux car le fichier cible appartient à un autre utilisateur que celui qui tente de le remplacer).

Ce bug me bloque toute la wave 30 de mon déploiement.

Root cause

Dans les deux cas, un pod contient deux conteneurs qui doivent se passer un fichier : un premier conteneur (init) l'écrit, un second (main) le lit ensuite. Le souci : les deux ne tournent pas sous le même utilisateur Linux (UID). Celui qui écrit est UID 65534, celui qui lit est UID 1000 — deux utilisateurs différents, ni l'un ni l'autre root, donc les permissions Unix classiques s'appliquent et bloquent l'accès croisé.

Sur un cluster sans profil de sécurité strict, ce problème n'existe pas : sans contrainte, les conteneurs tournent en root par défaut, et root peut lire/écrire n'importe quel fichier peu importe qui l'a créé. C'est uniquement parce que j'ai activé profile: cis (qui force runAsNonRoot sur tout) que cette différence d'UID, jusque-là invisible, devient bloquante.

gitlab-shell

  1. Un init container (configure) copie les clés SSH host depuis un Secret vers un emptyDir partagé (shell-secrets), monté ensuite en lecture seule par le conteneur principal.
  2. Le conteneur principal (gitlab-shell) lit ces fichiers copiés.

UID effectifs constatés (kubectl get pod -o json, .status.initContainerStatuses[].user / .status.containerStatuses[].user) :

Conteneur Type UID effectif
certificates initContainer 65534
configure initContainer 65534
gitlab-shell container (main) 1000

L'init écrit les clés en tant qu'UID 65534, le main essaie de les lire en tant qu'UID 1000 → Permission denied.

minio

Même schéma : un init container (configure) écrit un fichier config.json dans un emptyDir (minio-server-config, monté sur /tmp/.minio), le conteneur principal minio essaie ensuite de le migrer/remplacer.

Conteneur Type UID effectif
configure initContainer 65534
minio container (main) 1000

Même mismatch (65534 écrit, 1000 lit), mais l'erreur prend une forme différente ici : le noyau Linux refuse le rename du fichier de config avec operation not permitted, parce que le fichier cible appartient à un autre utilisateur que celui qui tente de le remplacer.

Où se trouve le vrai défaut

J'ai trouvé où se trouve le vrai défaut — socle/roles/kyverno/templates/cis.yml.j2, ClusterPolicy security-context-dso-gitlab (ligne 270), règle enforce-security-context :

              containers:
              - name: '*'
                securityContext:
                  +(allowPrivilegeEscalation): false
                  +(capabilities):
                    +(drop):
                    - ALL
                  +(runAsNonRoot): true
                  +(seccompProfile):
                    type: RuntimeDefault
              initContainers:
              - name: '*'
                securityContext:
                  +(allowPrivilegeEscalation): false
                  +(capabilities):
                    +(drop):
                    - ALL
                  +(runAsNonRoot): true
                  +(seccompProfile):
                    type: RuntimeDefault

Cette policy impose runAsNonRoot: true (via l'opérateur Kyverno +(), qui n'ajoute la valeur que si le champ est absent) sur les containers et initContainers, mais ne fixe jamais runAsUser — contrairement à plusieurs policies voisines du même fichier (security-context-dso-*, ex. lignes 41-42, 103-104, 168-169, 234-235, 355-356) qui, elles, imposent bien un runAsUser identique et cohérent sur containers ET initContainers (65534, 1001, ou 999 selon le composant). security-context-dso-gitlab est la seule policy du fichier à ne pas suivre cette logique.

Résultat : chaque conteneur du pod gitlab-shell garde l'UID que le chart lui a assigné par défaut (65534 pour les init containers, 1000 pour le main), sans qu'aucune règle ne les force à être identiques.

Résolution

J'ai corrigé socle/roles/kyverno/templates/cis.yml.j2, policy security-context-dso-gitlab — remplacé l'ajout conditionnel par un remplacement forcé (sans le +, pour écraser la valeur déjà posée par le chart) sur les trois blocs securityContext (pod, containers, initContainers) :

              securityContext:
                +(runAsNonRoot): true
                runAsUser: 1000
              containers:
              - name: '*'
                securityContext:
                  +(allowPrivilegeEscalation): false
                  +(capabilities):
                    +(drop):
                    - ALL
                  +(runAsNonRoot): true
                  +(seccompProfile):
                    type: RuntimeDefault
                  runAsUser: 1000
              initContainers:
              - name: '*'
                securityContext:
                  +(allowPrivilegeEscalation): false
                  +(capabilities):
                    +(drop):
                    - ALL
                  +(runAsNonRoot): true
                  +(seccompProfile):
                    type: RuntimeDefault
                  runAsUser: 1000

Correctif limité à security-context-dso-gitlab uniquement — aucune autre policy du fichier (-harbor, -keycloak, -vault...) touchée.

Pour l'appliquer, la tâche Ansible Create security-context ClusterPolicy for cis profile est create-only : elle vérifie l'existence de la policy security-context-dso (pas -gitlab elle-même) pour décider de régénérer tout le lot. Il a fallu supprimer cette policy pour forcer la recréation avec le contenu corrigé :

kubectl delete clusterpolicy security-context-dso
ansible-playbook install-gitops.yaml -e dsc_cr=dso-xxx -t kyverno

Puis forcer la recréation des pods déjà en échec :

kubectl delete pod -n dso-gitlab -l app=minio
kubectl rollout restart deployment gitlab-gitlab-shell -n dso-gitlab

Confirmé résolu : gitlab-shell et minio tournent tous les deux en 1/1 Running, zéro redémarrage depuis, plusieurs minutes après application.

Breaking change — vérification

Ce correctif force runAsUser: 1000 sur tous les Deployment/StatefulSet/Job du namespace dso-gitlab, pas seulement gitlab-shell/minio — j'ai donc vérifié l'impact sur le reste du namespace en supprimant tous les pods (kubectl delete pod --all -n dso-gitlab) pour forcer une recréation complète sous la nouvelle policy, y compris les composants avec stockage persistant (gitaly, PostgreSQL via CNPG) où un changement d'UID pourrait bloquer l'accès aux données déjà écrites par un autre utilisateur.

Résultat : gitaly, redis, pg-cluster (3/3 nœuds), pooler-pg-cluster, kas, gitlab-exporter — tous Running sans erreur après recréation. Aucune régression observée liée au changement d'UID.

Il est possible que ce changement puisse apporter d'autres soucis.

Etapes de reproduction

1. Aller à '...'
2. Cliquer sur '....'
3. Scroller jusqu'à '....'
4. Voir l'erreur

Captures d'écran

![DESCRIPTION](LINK.png)

Logs

No response

Navigateurs

No response

OS

No response

Version de la console impactée

No response

Définition du fini

  • Le correctif est terminé
  • Les tests liés à ce correctif ont été ajoutés
  • La communication avec les autres équipes impliquées par ce correctif a été faite

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions