Aller au contenu

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 :

  1. 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.
  2. Votre registre Harbor contient l'image akko-charter-runner:2026.05 (build par helm/scripts/build-images.sh).
  3. 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>.xml dans le pod. Utilisez kubectl cp pour 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

Voir aussi