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-scriptde 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 groupesadminGroupsetuserGroupspeuvent 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
rundecka un Role dans chaque namespace derbac.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,mainpar défaut) - prod :
prod-dpeuniquement, toujours la branchemain(pas d'optiongit_ref)
Job run-sql-script¶
Déroulement¶
- Kubernetes-Create-Job : crée le Job
rundeck-<execid>dans le namespace choisi, imagerundeck-sql-runner, Secretrundeck-job-secretschargé en variables d'environnement (envFrom), pull secretdpe-docker-registry-auth-service(créé par l'app-template-v2 de DPE, la clé Scaleway de DPE aContainerRegistryReadOnlysur tout le registry du projet service). - Kubernetes-Wait-Job : attend la fin du Job (10 s × 360, soit 1 h) et affiche ses logs dans Rundeck.
- 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 :
- vérifie le chemin du script (pas de chemin absolu, pas de
.., extension.sql) ; - clone le repo (
--depth 1 --branch <git_ref>) avec le deploy token, puis retireGITLAB_USERNAME/GITLAB_TOKENde l'environnement ; - 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¶
- Vérifier que la propriété existe dans Vault.
- L'ajouter dans
secretsdu namespace concerné, dansargocd-system/rundeck/helm-values/<horsprod|prod>.yaml. - Pousser, attendre la synchro ArgoCD. Le job n'a pas besoin d'être modifié.
Ajouter un namespace cible (nouvel environnement DPE)¶
- Values du chart (
helm-values/<env>.yaml) : ajouter le namespace dansrbac.namespaceset une entrée dansjobSecrets.targets. - Terraform : ajouter le namespace dans
dpe_namespaces(locals.tf), puis plan / apply.
Ouvrir Rundeck à une nouvelle application¶
- Chart : ajouter le groupe Keycloak dans
userGroups, les namespaces dansrbac.namespacesetjobSecrets.targets. - Terraform : créer un fichier
project_<app>.tfsur le modèle deproject_dpe.tf(projet, ACL, jobs) et les locals associés. - Pull secret : vérifier qu'un secret de type
dockerconfigjsonpourrg.fr-par.scw.cloud/namespace-scw-serviceexiste dans les namespaces (pour DPE,dpe-docker-registry-auth-service). - Repo de scripts : créer un deploy token pour le nouveau repo. Le Secret ne porte qu'un deploy token par namespace (
gitlabUsernameProperty/gitlabTokenPropertysont 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/rundeckpuis lancerbuild-rundeck. Les values utilisent le taglatestenpullPolicy: Always: redémarrer le pod Rundeck. - Runner SQL : modifier
tooling-infra/build/rundeck-sql-runnerpuis lancerbuild-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
mainprotégée du repo de scripts (merge request avec approbation) : c'est la seule branche exécutable en prod. - Les Jobs ont un
activeDeadlineSecondsde 3600 s et le job Rundeck un timeout d'1 h. - L'image Rundeck officielle contient un
realm.propertiesavec des comptes par défaut. L'accès web passe par oauth2-proxy, mais leur suppression reste à vérifier.