Aller au contenu

Rundeck

Une instance Rundeck par cluster applicatif (horsprod et prod) permet aux équipes applicatives de lancer des jobs d'exploitation sans accès direct au cluster ni aux bases. Le premier usage est le job run-sql-script de DPE : il exécute un script SQL versionné dans un repo GitLab sur la base d'un namespace DPE.

Cette page s'adresse aux mainteneurs de la plateforme. La documentation utilisateur du job DPE est le README du repo ademe-group/dpe/scripts-reprise.

Où se trouve quoi

Élément Emplacement
URL https://rundeck.direct.horsprod.ademe-scw.fr, https://rundeck.direct.prod.ademe-scw.fr (CNAME géré par main-infra 021.dns_scaleway/rundeck.tf)
Chart Helm repo argocd-ademe-appofapps, dossier charts/rundeck
Applications ArgoCD argocd-system/rundeck/rundeck-horsprod.yaml, rundeck-prod.yaml (projet system, namespace rundeck)
Values par environnement argocd-system/rundeck/helm-values/horsprod.yaml, prod.yaml
Configuration Rundeck (projets, ACL, jobs) repo main-infra, Terraform scw-application/<horsprod\|prod>/terraform/070.rundeck
Image Rundeck repo tooling-infra, build/rundeck (Rundeck 5.8.0 + plugin Kubernetes 3.0.5), job CI manuel build-rundeck → rg.fr-par.scw.cloud/namespace-scw-service/rundeck:latest
Image des jobs SQL repo tooling-infra, build/rundeck-sql-runner (script run-sql-script.sh), job CI manuel build-rundeck-sql-runner → rg.fr-par.scw.cloud/namespace-scw-service/rundeck-sql-runner:latest
Scripts SQL DPE repo ademe-group/dpe/scripts-reprise, dossier scripts/
Secrets Rundeck Vault ademe/apps/rundeck/horsprod, ademe/apps/rundeck/production
Base Rundeck RDB rundeck-<env>.rdb.ademe-cloud.internal, base <env>-rundeck

Architecture

flowchart LR
    U[Utilisateur] -->|HTTPS, IP whitelist| T[Traefik]
    T --> O[oauth2-proxy<br/>Keycloak OIDC]
    O -->|headers pré-auth<br/>user + groupes| R[Rundeck]
    TF[Terraform 070.rundeck] -->|/api/ + token statique| O
    R --> DB[(RDB Rundeck)]
    R -->|kubeconfig in-cluster<br/>ServiceAccount rundeck| K[API Kubernetes]
    K --> J[Job Kubernetes<br/>namespace applicatif]
    J -->|git clone<br/>deploy token| G[GitLab]
    J -->|psql| A[(RDB applicative)]
  • Authentification : oauth2-proxy (Keycloak, realm master) fait l'authentification et transmet l'utilisateur et ses groupes à Rundeck en pré-auth (X-Forwarded-Preferred-Username, X-Forwarded-Groups). Seuls les groupes adminGroups et userGroups peuvent se connecter. Une NetworkPolicy n'autorise que oauth2-proxy à joindre Rundeck.
  • API : les routes ^/api/ ne passent pas par l'OIDC, l'API utilise ses propres tokens.
  • Exécution : les jobs tournent sur le nœud local de Rundeck et créent des Jobs Kubernetes avec le plugin Kubernetes. Le ServiceAccount rundeck a un Role dans chaque namespace de rbac.namespaces (jobs, pods, pods/log).

Droits

Qui Droits Où
adminGroups (/APPSVC-Operator/rundeck) Tout ACL du chart (configmap_acl.yaml)
Token terraform (rôle terraform-api) Tout ACL du chart, token dans Vault rundeck_terraform_api_token
userGroups (ex. /APPSVC-Operator/dpe) Connexion uniquement Values du chart (oauth2-proxy)
Groupes d'un projet Voir le projet, lancer / arrêter ses jobs, voir l'historique ACL Terraform (rundeck_acl_policy)

Les définitions de jobs ne sont modifiables que par Terraform : les opérateurs ne peuvent ni créer ni éditer de job.

Terraform 070.rundeck

Le module configure l'instance via l'API (provider rundeck/rundeck, token lu dans Vault).

Prérequis : VAULT_TOKEN exporté et session az login active (backend azurerm, clé <env>/rundeck.tfstate).

Fichier Contenu
locals.tf URL, image du runner, repo de scripts, namespaces DPE
project_dpe.tf Projet <env>-dpe, son ACL, le job run-sql-script

Les deux environnements ont le même project_dpe.tf. Différences portées par locals.tf :

  • horsprod : 7 namespaces DPE, option git_ref (branche ou tag, main par défaut)
  • prod : prod-dpe uniquement, toujours la branche main (pas d'option git_ref)

Job run-sql-script

Déroulement

  1. Kubernetes-Create-Job : crée le Job rundeck-<execid> dans le namespace choisi, image rundeck-sql-runner, Secret rundeck-job-secrets chargé en variables d'environnement (envFrom), pull secret dpe-docker-registry-auth-service (créé par l'app-template-v2 de DPE, la clé Scaleway de DPE a ContainerRegistryReadOnly sur tout le registry du projet service).
  2. Kubernetes-Wait-Job : attend la fin du Job (10 s × 360, soit 1 h) et affiche ses logs dans Rundeck.
  3. Kubernetes-Delete-Job : supprime le Job et ses pods, même en cas d'échec (continue_on_error). L'exécution reste en échec si le script a échoué.

Le runner :

  1. vérifie le chemin du script (pas de chemin absolu, pas de .., extension .sql) ;
  2. clone le repo (--depth 1 --branch <git_ref>) avec le deploy token, puis retire GITLAB_USERNAME / GITLAB_TOKEN de l'environnement ;
  3. exécute psql --single-transaction -v ON_ERROR_STOP=1 --echo-errors -f scripts/<script>.

Codes retour : 2 = script invalide ou introuvable, 3 = erreur SQL (tout est annulé).

Secrets des jobs (jobSecrets)

Le chart crée un ExternalSecret rundeck-job-secrets dans chaque namespace de jobSecrets.targets. Le Secret ne contient que ce qui est déclaré :

jobSecrets:
  targets:
    - namespace: int-dpe
      # Valeurs en clair
      env: {PGHOST: dpe-horsprod.rdb.ademe-cloud.internal, PGDATABASE: int-dpe}
      # Propriétés Vault, VARIABLE: {path, property}
      secrets:
        PGUSER: {path: ademe/apps/dpe/integration, property: db_app_username}
        PGPASSWORD: {path: ademe/apps/dpe/integration, property: db_app_password}

S'y ajoutent toujours GITLAB_USERNAME et GITLAB_TOKEN, lus dans externalSecrets.vaultPath (propriétés gitlab_scripts_username et gitlab_scripts_token). Les noms de variables sont vérifiés au rendu du chart. Les scripts lisent les variables avec \getenv.

Secrets dans les logs

Avec --echo-errors, une requête en erreur est affichée dans les logs Rundeck avec ses valeurs, y compris un secret lu par \getenv. Le message d'erreur PostgreSQL peut aussi contenir des valeurs (ex. DETAIL: Key (...)=(...) sur une contrainte d'unicité).

Procédures

Donner un nouveau secret aux scripts

  1. Vérifier que la propriété existe dans Vault.
  2. L'ajouter dans secrets du namespace concerné, dans argocd-system/rundeck/helm-values/<horsprod|prod>.yaml.
  3. Pousser, attendre la synchro ArgoCD. Le job n'a pas besoin d'être modifié.

Ajouter un namespace cible (nouvel environnement DPE)

  1. Values du chart (helm-values/<env>.yaml) : ajouter le namespace dans rbac.namespaces et une entrée dans jobSecrets.targets.
  2. Terraform : ajouter le namespace dans dpe_namespaces (locals.tf), puis plan / apply.

Ouvrir Rundeck à une nouvelle application

  1. Chart : ajouter le groupe Keycloak dans userGroups, les namespaces dans rbac.namespaces et jobSecrets.targets.
  2. Terraform : créer un fichier project_<app>.tf sur le modèle de project_dpe.tf (projet, ACL, jobs) et les locals associés.
  3. Pull secret : vérifier qu'un secret de type dockerconfigjson pour rg.fr-par.scw.cloud/namespace-scw-service existe dans les namespaces (pour DPE, dpe-docker-registry-auth-service).
  4. Repo de scripts : créer un deploy token pour le nouveau repo. Le Secret ne porte qu'un deploy token par namespace (gitlabUsernameProperty / gitlabTokenProperty sont globaux) : un deuxième repo demande de rendre ces propriétés configurables par cible.

Rotation des tokens

Token Procédure
Token API Terraform Nouvelle valeur (openssl rand -hex 32, sans virgule) dans rundeck_terraform_api_token, puis redémarrer le pod Rundeck : le fichier de tokens n'est lu qu'au démarrage.
Deploy token GitLab Créer le nouveau token sur le repo de scripts (scope read_repository), mettre à jour gitlab_scripts_username / gitlab_scripts_token dans les deux paths Vault, forcer la synchro (ci-dessous), puis révoquer l'ancien. Pas de redémarrage.

Forcer la resynchronisation d'un ExternalSecret sans attendre le refreshInterval (1 h) :

kubectl annotate externalsecret rundeck-job-secrets -n int-dpe force-sync=$(date +%s) --overwrite

Mettre à jour les images

  • Rundeck : modifier tooling-infra/build/rundeck puis lancer build-rundeck. Les values utilisent le tag latest en pullPolicy: Always : redémarrer le pod Rundeck.
  • Runner SQL : modifier tooling-infra/build/rundeck-sql-runner puis lancer build-rundeck-sql-runner. Pris en compte à l'exécution suivante (image_pull_policy: Always).

Dépannage

Symptôme Piste
L'utilisateur est renvoyé sur une erreur 403 par oauth2-proxy Son groupe Keycloak n'est ni dans adminGroups ni dans userGroups.
L'utilisateur se connecte mais ne voit pas le projet ACL Terraform du projet (rundeck_acl_policy) : le groupe doit être le chemin complet (/APPSVC-Operator/dpe).
Terraform : 401 / 403 sur l'API Token absent ou changé sans redémarrage du pod Rundeck.
Étape Create-Job en erreur Forbidden Namespace absent de rbac.namespaces.
Pod en CreateContainerConfigError Secret rundeck-job-secrets absent : vérifier kubectl get externalsecret -n <ns> (propriété Vault manquante → SecretSyncedError).
Pod en ImagePullBackOff Pull secret absent du namespace ou image non buildée.
fatal: Authentication failed / Remote branch ... not found Deploy token expiré ou révoqué, ou branche / tag inexistant. Un SHA de commit n'est pas accepté.
Script not found (code 2) Chemin relatif à scripts/, sans le préfixe scripts/.
Job Kubernetes resté dans le namespace Exécution interrompue avant l'étape de suppression : kubectl delete job -n <ns> -l app.kubernetes.io/managed-by=rundeck.

Points d'attention

  • Les scripts tournent avec le compte applicatif (db_app_username), avec les mêmes droits que l'application.
  • La protection de la prod repose sur la branche main protégée du repo de scripts (merge request avec approbation) : c'est la seule branche exécutable en prod.
  • Les Jobs ont un activeDeadlineSeconds de 3600 s et le job Rundeck un timeout d'1 h.
  • L'image Rundeck officielle contient un realm.properties avec des comptes par défaut. L'accès web passe par oauth2-proxy, mais leur suppression reste à vérifier.