akko-charter-runner — validation charter en cluster¶
akko-charter-runner est un CronJob Kubernetes souverain qui lance la
suite charter AKKO (campagnes C-10..C-15 + W2/W4/W5) depuis
l'intérieur du cluster, contre les Service URLs in-cluster (aucun
ingress public, aucun bypass Bearer-JWT). Il ferme la brèche laissée
par l'ADR-057 — pas de bypass Bearer-JWT sur l'ingress public :
les runners externes ne peuvent pas valider ~25 campagnes charter de
bout en bout parce que l'oauth2-proxy public refuse les JWT sans
session par design. Le runner in-cluster est la seule façon
sec-acceptable de produire une green-line qui inclut ces campagnes.
Le chart est livré désactivé par défaut. L'opérateur flip
akko-charter-runner.enabled: true dans son overlay de values après
que l'image runner soit dans son registre Harbor.
Phase 1 — runner seul ; sink Postgres + UI cockpit en Phase 2
La première PR livre le CronJob + RBAC minimal + NetworkPolicy.
Les résultats vivent dans kubectl logs job/... et le JUnit XML
que le runner écrit dans /tmp/charter-out. La Phase 2 ajoute
un sink Postgres et un widget cockpit "dernier charter run" pour
rendre la green-line visible sans kubectl.
Quand l'activer¶
Activez-le quand les trois conditions sont vraies :
- La plateforme est passée la phase d'install initiale et stable au point que pytest fait remonter des vraies régressions, pas du bruit de bootstrap.
- Votre registre Harbor contient l'image
akko-charter-runner:2026.05(build parhelm/scripts/build-images.sh). - Vous voulez une attestation quotidienne que les campagnes charter sont vertes de bout en bout, y compris celles que les runners externes ne peuvent pas atteindre (C-11 / C-15 / W2 / W4 / W5).
Sinon laissez enabled: false. Le cockpit et ADEN ne lisent pas son
output ; rien d'autre dans la plateforme ne dépend de son activation.
Architecture¶
flowchart LR
Cron[CronJob<br/>04:00 UTC chaque jour]
Job[Pod akko-charter-runner]
CB[akko-cockpit-backend:8080]
T[akko-trino:8080]
KC[akko-keycloak:8080]
OPA[akko-opa:8181]
PS[akko-policy-sync:8080]
Cron --> Job
Job -->|Service URL in-cluster| CB
Job -->|Service URL in-cluster| T
Job -->|Service URL in-cluster| KC
Job -->|Service URL in-cluster| OPA
Job -->|Service URL in-cluster| PS
Job -->|stdout + JUnit XML| Logs[kubectl logs job/...]
Le runner n'atteint jamais l'ingress public (trino.<domain>,
federation.<domain>, demo.<domain>). Il utilise les noms DNS
Service qui résolvent uniquement à l'intérieur du cluster —
akko-akko-trino:8080, etc. — donc un token ServiceAccount runner
volé est inutile depuis l'extérieur.
Configuration¶
Le values.yaml umbrella expose akko-charter-runner. Overlay
minimum viable :
akko-charter-runner:
enabled: true
schedule: "0 4 * * *" # 04:00 UTC quotidien, "" pour désactiver
suites: "charter" # ou "all" pour étendre
inClusterTargets:
cockpitBackend: http://akko-akko-cockpit-backend:8080
trino: http://akko-akko-trino:8080
keycloak: http://akko-akko-keycloak:8080
opa: http://akko-akko-opa:8181
policySync: http://akko-akko-policy-sync:8080
personaSecret:
name: akko-test-passwords
optional: true
Remplacez akko-akko- par votre release name si vous n'utilisez pas
le helm install akko ... par défaut.
Lancer un run manuel¶
# Job one-shot depuis le template du CronJob :
kubectl -n akko create job --from=cronjob/akko-akko-charter-runner \
manual-$(date +%Y%m%d-%H%M%S)
# Puis suivre les logs :
kubectl -n akko logs -f job/manual-...
# La ligne de résumé en fin donne le compte :
# [charter-runner] SUMMARY tests=42 pass=37 fail=0 err=0 skip=5
Posture sécurité¶
| Vecteur | Mitigation |
|---|---|
| Élévation de privilèges | Role namespace-scoped seul, aucune ressource cluster-scoped, pas de verbe wildcard, pas de pods/exec. |
| Lecture Secret arbitraire | Verrou resourceNames: — seul le Secret persona nommé dans personaSecret.name est lisible. |
| Egress vers Internet | NetworkPolicy egress allow-list — DNS, services AKKO in-cluster, kube-apiserver uniquement. Pas de 0.0.0.0/0. |
| Bypass ingress public | Le runner n'utilise jamais les Service URLs publiques ; il tape les DNS Service in-cluster par design. Contrat ADR-057 intact. |
| Tampering runtime | runAsNonRoot, allowPrivilegeEscalation: false, capabilities.drop: [ALL]. Image signée cosign (policy verify Kyverno). |
| Abus de concurrence | concurrencyPolicy: Forbid — un long run ne peut pas être racé par le tick CronJob suivant. |
Le modèle de confiance est le même qu'un service à long-running dans le namespace, pas un agent admin. Un runner compromis peut lire les ClusterIP des Services AKKO et le Secret persona ; il ne peut ni escalader, ni exec dans d'autres pods, ni atteindre Internet.
Exploitation¶
Où trouver le résultat d'un run¶
- Live tail :
kubectl -n akko logs -f job/<name> - JUnit XML (pour ingestion CI) : le runner écrit
/tmp/charter-out/charter-<host>-<ts>.xmldans le pod. Utilisezkubectl cppour l'extraire pendant la brève fenêtre avant completion du Job (la Phase 2 publie ça vers Postgres automatiquement). - Ligne de résumé : la dernière ligne de log est
[charter-runner] SUMMARY tests=N pass=N fail=N err=N skip=N.
Modes d'échec courants¶
| Symptôme | Cause probable | Correction |
|---|---|---|
kubectl logs montre connect-refused pour akko-akko-trino:8080 |
Moteur de requête (Trino) pas encore activé dans ce déploiement | flip trino.enabled: true OU pointer inClusterTargets.trino vers le vrai DNS Service |
La plupart des tests SKIP avec AKKO_TEST_PASSWORD_ALICE missing |
Secret persona pas monté | appliquer le Secret demo de akko-init OU mettre personaSecret.optional: false pour échouer fort |
Tous les tests FAIL avec connect-refused sur kube-API |
Régression NP Sprint 73 | vérifier kubectl get networkpolicy -n akko -l app.kubernetes.io/name=akko-charter-runner |
Pod runner bloqué Pending |
Ressources trop élevées pour le cluster | réduire resources.requests ; défauts = 200m / 512Mi |