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
- L’utilisateur accède à l’interface web Flux
- L’application redirige vers le fournisseur OIDC (GitLab)
- L’utilisateur s’authentifie auprès du fournisseur
- Le fournisseur retourne un token JWT contenant les claims (email, groupes, etc.)
- L’application échange le code d’autorisation contre un token d’accès
- 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 :
- Direction :
https://gitlab.my-domain.com/groups/my-gitlab-group/-/settings/applications - Définir l’URL de callback :
https://flux.my-domain.com/oauth2/callback⚠️ Important : Cette URL doit correspondre exactement à labaseURL+/oauth2/callback, sinon on aura droit à des erreurs cryptiques ! - Cocher les scopes :
openid,profile,email - 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
- L’utilisateur s’authentifie via OIDC et obtient un token JWT
- L’application extrait du JWT :
- Le username (email)
- Les groupes
- 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 - 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 :
- Accéder à
https://flux.my-domain.com - Cliquer sur “Login”
- Hop ! Redirection vers GitLab
- S’authentifier avec les identifiants GitLab
- Re-hop ! Retour vers l’interface Flux Web UI
- Vérifier les informations sur l’utilisateur effectivement connecté :
https://flux.my-domain.com/user/profile - 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
-
Secrets sensibles : Sérieux, ne JAMAIS commiter les
clientSecreten clair dans Git !- Utiliser Sealed Secrets, SOPS ou External Secrets Operator.
-
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 !
-
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
-
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 ! 🚀