Aller au contenu

akko-policy-sync — auto-propagation PII

akko-policy-sync ferme la boucle entre la classification des données et l'application des accès. Un steward classifie une colonne en PII.Sensitive dans Catalogue. Le daemon récupère le tag à la prochaine boucle de synchronisation, calcule les masques de colonnes correspondants pour chaque sujet non administrateur dont le périmètre couvre la table, puis fait un PATCH sur le ConfigMap du bundle Moteur de politiques. Moteur de requête recharge le bundle et commence à masquer la colonne pour les analystes — typiquement en moins de 30 secondes, ou en moins de 5 secondes si le steward déclenche une synchro manuelle depuis le cockpit.

Le daemon est désactivé par défaut. Activez enabled: true seulement après avoir provisionné un JWT bot Catalogue.

Sprint 67 PR-D / ADR-055 — V1 timer + déclenchement manuel

La V1 livre l'ordonnanceur périodique (30 min), le déclenchement manuel, le panneau cockpit et l'audit OCSF 3007. La V2 ajoutera un EventListener OM par webhook quand le retour client montrera que la fenêtre 30 min est trop longue. Voir ADR-055.

Quand l'activer

Activez-le quand les quatre conditions sont vraies :

  1. Catalogue est la source de vérité du tagging PII colonne par colonne.
  2. Le bundle Moteur de politiques détient vos policies de masquage ligne + colonne (topologie AKKO par défaut — Sprint 67 PR-C).
  3. Vous voulez que le travail de classification déclenche l'enforcement automatiquement, sans édition manuelle.
  4. Vous pouvez créer un user bot OM avec accès en lecture à /api/v1/tables + /api/v1/classifications.

Si l'une de ces conditions est fausse, gardez enabled: false. Les stewards continuent d'éditer les masques manuellement via l'assistant de la page Data Access du cockpit (Sprint 67 PR-C) — cette boucle fonctionne déjà seule.

Architecture

flowchart LR
  subgraph Déclencheurs
    T1[CronJob 30 min]
    T2[POST /api/v1/sync<br/>panneau cockpit]
  end
  T1 --> SVC
  T2 --> SVC
  SVC[akko-policy-sync<br/>FastAPI + asyncio]
  SVC -->|GET tags<br/>Bearer JWT| OM[OpenMetadata<br/>:8585]
  SVC -->|PATCH ConfigMap<br/>If-Match resourceVersion| OPA[bundle OPA<br/>ConfigMap]
  SVC -->|HTTP /insert/jsonline<br/>OCSF 3007| VL[VictoriaLogs<br/>:9428]
  OPA --> Trino[Enforcement Trino<br/>masquage SHA-256]

Configuration

Le values.yaml umbrella expose akko-policy-sync. Le minimum viable est :

akko-policy-sync:
  enabled: true
  openmetadata:
    baseUrl: http://akko-openmetadata-server:8585
    jwtSecret:
      name: akko-openmetadata-bot-jwt
      key: token
  piiTagToMask:
    PII.Sensitive: sha256
    PII.NonSensitive: identity
  adminSubjects:
    - akko-admin
    - AD_admin
  syncIntervalSeconds: 1800

akko-cockpit-backend:
  policySync:
    url: http://akko-akko-policy-sync:8080

Remplacez akko par votre release name s'il diffère.

JWT bot — création d'un token Catalogue

# 1. Ouvrir l'UI admin OM avec un user qui a `Settings` / `Bots`.
# 2. Créer un nouveau bot, p.ex. `akko-policy-sync`.
# 3. Générer un JWT avec rôle `DataStewardRole` (lecture tables/tags)
#    et expiration 1 an.
# 4. Persister dans le namespace AKKO :
kubectl -n akko create secret generic akko-openmetadata-bot-jwt \
  --from-literal=token="<coller-le-jwt-ici>"

À la rotation du token, mettez le Secret à jour. Le daemon relit le fichier à chaque cycle ; pas de redémarrage nécessaire.

Mapping tag → masque

piiTagToMask est le seul levier produit. Tout tag OM à gauche mappe vers le nom de masque à droite. Le nom doit exister dans le mask_library Moteur de politiques (sha256, md5, null, redact, identity, partial_last4 par défaut).

piiTagToMask:
  PII.Sensitive:           sha256
  PII.NonSensitive:        identity
  PCI.PAN:                 partial_last4
  Compliance.HealthRecord: null

Un tag absent du map ne déclenche aucune auto-propagation — la colonne passe sans modification.

Exempter des sujets

Les sujets dans adminSubjects sont sautés : le propagateur ne hashe jamais leurs requêtes. Par défaut = [akko-admin, AD_admin]. Ajoutez tout sujet qui doit toujours voir le clair (auditeurs, DBA d'astreinte, comptes break-glass).

Exploitation

Lancer une synchro depuis le cockpit

Ouvrez https://demo.<votre-domaine>/#data-access, descendez jusqu'à Auto-propagation PII, cliquez Lancer la synchro maintenant. La grille sous la barre d'outils s'actualise avec le résumé du run :

  • timestamp + durée du dernier run
  • tables traitées
  • sujets impactés
  • échecs de publication (doit rester à 0)
  • prochaine exécution planifiée

Le déclencheur est protégé par akko-admin et émet un événement OCSF 3007 vers Couche Logs (VictoriaLogs) (activity_id = OTHER, cible = service:policy-sync). L'onglet Audit liste chaque run manuel à côté des éditions humaines de la Data Access.

Inspection en kubectl

kubectl -n akko port-forward svc/akko-akko-policy-sync 8080:8080 &
curl -s localhost:8080/api/v1/status   | jq .
curl -s localhost:8080/api/v1/config   | jq .
curl -X POST localhost:8080/api/v1/sync | jq .

Couche Métriques (Prometheus)

akko_policy_sync_runs_total{outcome="success"}
akko_policy_sync_runs_total{outcome="failure"}
akko_policy_sync_tables_processed_total
akko_policy_sync_subjects_affected_total
akko_policy_sync_publish_failures_total
akko_policy_sync_last_run_timestamp_seconds

Un publish_failures_total non nul sur deux runs consécutifs doit appeler l'astreinte. Causes courantes :

Cause Symptôme Correction
JWT bot OM expiré Tous les runs échouent en HTTP 401 OM Régénérer le token + mettre à jour le Secret
OM injoignable connect-refused dans les logs du pod Vérifier le Service OM + l'egress NetworkPolicy en 8585
Drift du configmap Moteur de politiques HTTP 409 sur le PATCH Un second writer édite le bundle ; vérifier les logs de l'éditeur Data Access
pii_tag_to_mask réfère un masque inexistant Le rechargement Moteur de politiques garde silencieusement l'ancien bundle Ajouter le masque à l'overlay mask_library Moteur de politiques

Masques manuels vs masques auto

Le daemon marque chaque entrée qu'il écrit avec source: "auto-pii". À chaque cycle de synchro, seules les entrées avec ce source sont remplacées. Les masques manuels ajoutés via l'assistant de la Data Access (Sprint 67 PR-C) ne portent pas de champ source et passent sans modification. Les stewards peuvent mélanger les deux librement : un masque manuel partial_last4 sur customers.phone cohabite avec un masque auto-propagé sha256 sur customers.email sans conflit.

Piste d'audit

Chaque run de synchro produit un événement OCSF 3007 :

  • class_uid = 3007 (Data Access Management)
  • activity_id = 99 (OTHER)
  • actor.user.name = system:policy-sync pour les runs périodiques, le preferred_username de l'admin pour les runs manuels
  • metadata.product.name = akko-policy-sync
  • unmapped.tables_processed, unmapped.subjects_affected, unmapped.publish_failures
  • resources = liste complète des sujets dont la policy a été touchée

Requête Couche Logs :

_msg:AKKO_AUDIT class_uid:3007 metadata.product.name:akko-policy-sync

Ou ouvrez l'onglet Audit du cockpit — les entrées arrivent dans le même flux que les éditions humaines.

Sécurité

Vecteur Mitigation
Élévation de privilèges RoleBinding scopé à un seul ConfigMap par resourceNames, verbes limités à get / patch / update (pas de list / create / delete / *)
Tag-injection depuis un OM compromis Le propagateur ne peut poser que des masques existant dans mask_library ; un tag malveillant mappé sur rien est un no-op
Exfiltration réseau Allow-list NetworkPolicy egress : OM 8585, VL 9428, kube API 443 + 6443 (kube-router post-DNAT) ; tout le reste refusé
Contournement audit Chaque run émet OCSF 3007 ; les déclencheurs manuels portent le sub de l'admin appelant
Vol du JWT bot Token dans un Secret namespace-scopé ; rotation annuelle

Voir aussi