Introduction

Flux Operator est un logiciel libre et open source qui étend la capacités de FluxCD avec des fonctionnalités en libre-service, une page d’état et des environnements de prévisualisation.

Flux Operator offre une jolie interface graphique pour gérer les déploiements via FluxCD sur Kubernetes. C’est sympa, mais encore faut-il éviter que n’importe qui puisse venir y mettre le bazar ! C’est là qu’entre en scène OIDC (OpenID Connect), qui permet une authentification centralisée et une gestion fine des autorisations basée sur les groupes d’utilisateurs.

Dans ce guide, on va voir comment brancher tout ça avec GitLab comme fournisseur d’identité. Spoiler alert : les utilisateurs pourront se connecter avec leur compte GitLab habituel.

Architecture de l’authentification

Le flux d’authentification OIDC fonctionne selon le schéma suivant :

┌─────────────┐         ┌──────────────────┐         ┌─────────────────┐
│             │         │                  │         │                 │
│  Navigateur │────────>│  Flux Web UI     │────────>│  OIDC Provider  │
│  Utilisateur│         │                  │         │    (GitLab)     │
│             │<────────│                  │<────────│                 │
└─────────────┘         └──────────────────┘         └─────────────────┘
     1. Accès                2. Redirection               3. Authentification
                             OAuth2                       4. Token JWT
                             5. Callback
                             6. Impersonation K8s
  1. L’utilisateur accède à l’interface web Flux
  2. L’application redirige vers le fournisseur OIDC (GitLab)
  3. L’utilisateur s’authentifie auprès du fournisseur
  4. Le fournisseur retourne un token JWT contenant les claims (email, groupes, etc.)
  5. L’application échange le code d’autorisation contre un token d’accès
  6. L’application utilise le token pour impersonner l’utilisateur dans Kubernetes

Configuration avec GitLab

La bonne nouvelle, c’est que GitLab sait nativement parler OIDC. Nous allons pouvoir réutiliser nos comptes GitLab existants sans avoir à créer une énième base d’utilisateurs.

Prérequis

Avant de plonger dans le YAML (il y en aura, promis), il faut d’abord créer une application OAuth2 dans GitLab :

  1. Direction : https://gitlab.my-domain.com/groups/my-gitlab-group/-/settings/applications
  2. Définir l’URL de callback : https://flux.my-domain.com/oauth2/callback ⚠️ Important : Cette URL doit correspondre exactement à la baseURL + /oauth2/callback, sinon on aura droit à des erreurs cryptiques !
  3. Cocher les scopes : openid, profile, email
  4. Sauvegarder et récupérer le Client ID et le Client Secret (à conserver précieusement, on en aura besoin juste après)

Configuration

L’application web de Flux Operator se configure via un fichier config.yaml injecté via un secret Kubernetes :

# flux-status-web-ui-config.secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: flux-status-web-ui-config
  namespace: flux-system
type: Opaque
stringData:
  config.yaml: |
    apiVersion: web.fluxcd.controlplane.io/v1
    kind: Config
    spec:
      baseURL: https://flux.my-domain.com
      authentication:
        type: OAuth2
        oauth2:
          provider: OIDC
          issuerURL: https://gitlab.my-domain.com
          clientID: <your-gitlab-client-id>
          clientSecret: <your-gitlab-client-secret>
          scopes:
            - openid
            - profile
            - email
          variables:
            - name: gitlab_groups
              expression: "claims.groups_direct"
          impersonation:
            username: "claims.email"
            groups: "variables.gitlab_groups"    

Explication des paramètres

Paramètre Description
issuerURL URL du serveur GitLab qui fournit l’authentification OIDC
clientID Identifiant de l’application OAuth2 créée dans GitLab
clientSecret Secret de l’application OAuth2 (à protéger)
scopes Les permissions demandées : identité openid, profil utilisateur, email
variables.gitlab_groups Extraction des groupes GitLab depuis le claim groups_direct
impersonation.username L’email de l’utilisateur sera utilisé comme identifiant Kubernetes
impersonation.groups Les groupes GitLab seront mappés aux groupes Kubernetes

Mapping des groupes GitLab

Le petit truc magique ici, c’est le claim groups_direct. GitLab nous envoie gentiment la liste des groupes de l’utilisateur dans le token JWT, et on va s’en servir pour définir qui a le droit de faire quoi dans Kubernetes. Pratique !

Voici à quoi ressemble un claim JWT GitLab :

{
  "sub": "123",
  "email": "me@my-domain.com",
  "groups_direct": ["my-gitlab-namespace/my-gitlab-group"]
}

Impersonation Kubernetes

Alors là, accrochez-vous, c’est la partie vraiment cool de l’architecture !

L’impersonation permet à l’application web de se faire passer pour l’utilisateur authentifié lors des appels à l’API Kubernetes. Au lieu de donner des permissions de super-admin à l’application (ce qui serait un peu risqué, avouons-le), elle se contente d’emprunter l’identité de l’utilisateur connecté.

Fonctionnement

  1. L’utilisateur s’authentifie via OIDC et obtient un token JWT
  2. L’application extrait du JWT :
    • Le username (email)
    • Les groupes
  3. Lors des appels à l’API Kubernetes, l’application ajoute les headers :
    Impersonate-User: me@my-domain.com
    Impersonate-Group: my-gitlab-namespace/my-gitlab-group
    
  4. Kubernetes applique les permissions RBAC de l’utilisateur/groupe impersonné

Pourquoi c’est cool ?

  • Sécurité : L’application n’a pas besoin de permissions élevées (fini le stress des service accounts tout-puissants)
  • Audit : Les actions sont tracées avec l’identité réelle de l’utilisateur (plus moyen de dire “c’était pas moi !”)

Configuration RBAC et Tenants

OK, maintenant qu’on peut s’authentifier, il faut quand même définir qui a le droit de voir/faire quoi. Bienvenue dans le monde merveilleux des RoleBindings Kubernetes !

Exemple de RoleBinding

Un RoleBinding définit les permissions de l’application pour un groupe GitLab donné sur un namespace Kubernetes donné :

# flux-status-web-ui-tenant.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: flux-status-web-ui-tenant
  namespace: my-namespace
subjects:
  - kind: Group
    name: "my-gitlab-namespace/my-gitlab-group"
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: flux-web-user
  apiGroup: rbac.authorization.k8s.io

Cette configuration signifie :

  • Les membres du groupe GitLab my-gitlab-namespace/my-gitlab-group
  • Auront les permissions définies dans le ClusterRole flux-web-user
  • Uniquement dans le namespace my-namespace

ClusterRoles

Le ClusterRole flux-web-user est fourni nativement par Flux Operator et permet un accès en mode lecture seule. A noter qu’un second ClusterRole flux-web-admin est également disponible pour disposer d’un accès total.

Activation de la configuration

1. Injection de la configuration dans Flux Operator

Pour injecter la configuration nous allons patcher le Deployment de Flux Operator pour lui indiquer le Secret contenant la configuration :

# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
 - flux-operator.yaml  # Le yaml d'installation de Flux Operator
 - flux-status-web-ui-config.secret.yaml
 - flux-status-web-ui-tenant.yaml

patches:
  - patch: |-
      - op: add
        path: /spec/template/spec/containers/0/args
        value: ["--web-config-secret-name=flux-status-web-ui-config"]      
    target:
      kind: Deployment
      namespace: flux-system
      name: flux-operator

2. Application de la configuration

# Appliquer la configuration complète
kubectl apply -k sandbox/flux-operator/

# Vérifier que le Secret est créé
kubectl get secret flux-status-web-ui-config -n flux-system

# Vérifier que Flux Operator a redémarré avec la nouvelle config
kubectl rollout status deployment/flux-operator -n flux-system

3. Test de l’authentification

Le moment de vérité ! Voyons si tout ce YAML a servi à quelque chose :

  1. Accéder à https://flux.my-domain.com
  2. Cliquer sur “Login”
  3. Hop ! Redirection vers GitLab
  4. S’authentifier avec les identifiants GitLab
  5. Re-hop ! Retour vers l’interface Flux Web UI
  6. Vérifier les informations sur l’utilisateur effectivement connecté : https://flux.my-domain.com/user/profile
  7. Vérifier qu’on voit uniquement les ressources du namespace autorisé dans le RoleBinding

Sécurité

Allez, un petit rappel des bonnes pratiques pour dormir tranquille la nuit.

Bonnes pratiques

  1. Secrets sensibles : Sérieux, ne JAMAIS commiter les clientSecret en clair dans Git !

    • Utiliser Sealed Secrets, SOPS ou External Secrets Operator.
  2. HTTPS obligatoire : En production, on oublie le HTTP. Point.

    • baseURL: https://flux.my-domain.com
    • Un beau certificat TLS valide sur un Ingress ou un HTTPRoute, et c’est parti !
  3. Principe du moindre privilège :

    • Ne donner que les permissions strictement nécessaires (non, tout le monde n’a pas besoin d’être cluster-admin)
    • Préférer les RoleBindings (limités à un namespace) aux ClusterRoleBindings (globaux) quand c’est possible
  4. Audit :

    • Activer l’audit logging de Kubernetes pour garder une trace de qui a fait quoi
    • Surveiller les échecs d’authentification répétés (quelqu’un essaie peut-être de forcer la porte)

Conclusion

Et voilà ! Nous avons maintenant une authentification OIDC solide sur Flux Operator Web UI avec GitLab. L’impersonation Kubernetes fait le boulot pour nous en matière de sécurité et de traçabilité, et les utilisateurs peuvent se connecter avec leurs comptes GitLab habituels.

Bref, une pierre deux coups : moins de comptes à gérer, et les utilisateurs n’ont pas besoin de retenir un énième mot de passe. Tout le monde est content !

Bon déploiement ! 🚀

Ressources