Skip to content

feat(tsa-server): scellement périodique du journal et copie S3 (constat J-1) - #101

Merged
PhilippeVienne merged 1 commit into
devfrom
feat/tsa-audit-seal
Oct 13, 2026
Merged

PhilippeVienne merged 1 commit into
devfrom
feat/tsa-audit-seal

Conversation

@PhilippeVienne

Copy link
Copy Markdown
Contributor

Objet

Première tranche du constat J-1 de l'audit du 2026-09-25, seule non-conformité majeure restante (EN 319 401 REQ-7.10-02, REQ-7.10-08). Base dev, PR indépendante. Le chaînage SHA-256 du journal n'a pas de clé : qui peut écrire dans le fichier peut le réécrire en recalculant la chaîne. Les variables de scellement étaient lues, aucun binaire ne scellait.

  • Scellement : tsa-server serve lance une tâche de fond. Au démarrage puis à chaque OPENEIDAS_AUDIT_SEAL_INTERVAL, la TSU horodate la tête de chaîne et consigne log.sealed avec la tête scellée et le jeton RFC 3161, certificat compris.
  • Copie hors site : après chaque scellement, le journal entier est déposé sur un stockage objet compatible S3 s'il est configuré (log.replicated), avec les mêmes variables OPENEIDAS_S3_* que ca-server et ra-console.
  • Santé : /healthz rend l'état du journal (journal.last_seal, seal_failures, offsite_copy, last_copy, copy_failures, degraded).
  • Retrait du réplica WebDAV : le chart et le compose le déployaient pour la TSA sans jamais l'alimenter. Il disparaît, avec OPENEIDAS_AUDIT_REPLICA_*, auditReplica.* et la clé webdav-password. Le chart gagne tsa.audit.s3.*.

Décisions prises avec l'utilisateur :

  • Copie périodique, pas à chaque jeton : le journal de la TSA reçoit une entrée par horodatage, et l'émission ne dépend pas du stockage distant.
  • Un échec prolongé dégrade /healthz sans suspendre l'émission.
  • S3 pour les trois journaux, donc retrait du WebDAV. Le crate oe-replicate reste, appelé par aucun binaire.

Points d'attention pour la revue :

  • /healthz reste en 200 quand le journal est dégradé : la sonde sert aussi à la vivacité et à la disponibilité dans le chart, un 503 ferait redémarrer ou retirer le service. Le libellé passe à « dégradé — journal d'audit : … » et journal.degraded à true : c'est à la supervision d'alerter, et aucune alerte n'est branchée par cette PR.
  • Le sceau reste auto-référentiel tant que le contreseing par une TSA tierce n'est pas câblé (tranche suivante) : il ne prouve rien à qui ne fait pas déjà confiance à la TSU. La matrice garde donc l'exigence en « écart documenté ».
  • Le jeton du sceau est un jeton comme un autre : il est journalisé en timestamp.granted avant log.sealed. Le test du binaire qui comptait un seul timestamp.granted est ajusté en conséquence.
  • Reprise rapide : un scellement en échec est retenté après une minute au plus (au démarrage sous enforce, l'heure peut ne pas être encore traçable). Une copie en échec attend l'intervalle, pour ne pas ajouter un sceau au journal à chaque essai. « Trois passes » peut donc valoir trois minutes pour le scellement, trois intervalles pour la copie.
  • Effet au déploiement : sur le staging, ArgoCD supprimera le Deployment, le Service et le volume du réplica WebDAV (vide, jamais alimenté). Le Secret scellé garde une clé webdav-password devenue inutile, sans conséquence. Aucun S3 n'y est configuré : le journal de la TSA y sera scellé mais pas copié, ce que /healthz dira (offsite_copy: "non configurée").
  • Chart : tsa.audit.s3.existingSecret doit porter access-key et secret-key ; le rendu échoue si le point d'accès est renseigné sans compartiment ni Secret (étape ajoutée au job helm-lint).

Vérifications

  • cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings (toolchain locale et 1.98.1) : verts.
  • cargo test --workspace --features oe-castore/postgres -- --nocapture avec PostgreSQL et SoftHSM : vert, 96 suites, aucun test ignoré. Après l'ajout de la reprise rapide, seuls clippy et les tests de tsa-server ont été rejoués.
  • Sur le binaire réel (bin/tsa-server/tests/serve.rs, token SoftHSM) : au démarrage, un log.sealed apparaît, son jeton est vérifié par openssl ts -verify -digest <tête> -CAfile chain.pem, et /healthz rend l'état du journal.
  • cargo audit --ignore RUSTSEC-2023-0071, cargo deny check licenses : verts. helm lint (défaut et console), rendu avec values-staging.yaml, docker compose config : verts. docs/CONFORMITE-ETSI.md régénéré.
  • Mutations, toutes tuées par le test attendu :
Mutation Test qui échoue
le jeton ne couvre pas la tête de chaîne a_pass_seals_the_head_with_a_token_openssl_verifies
échec de copie non compté prolonged_failures_degrade_health_until_a_pass_succeeds
échec de scellement non compté idem
copie consignée sans avoir abouti idem
scellement en échec jamais signalé idem
/healthz tait le journal dégradé health_reports_the_journal_without_taking_the_service_down

Non vérifié : la copie contre un vrai MinIO ou Garage (les tests utilisent un service HTTP qui se comporte comme S3, comme ceux de ca-server), et les jobs de démonstration kind et compose, qui ne tournent qu'en CI.

Revue humaine obligatoire

Voir PROVENANCE.md. Chaque case est cochée par le
contributeur humain qui valide la PR, après l'avoir fait lui-même.

  • Revue d'architecture validée par l'humain
  • Code relu et tests unitaires/intégration vérifiés localement
  • Absence de dépendances tierces incompatibles avec la double licence EUPL-1.2 / AGPL-3.0 (make licenses)
  • Validation de l'apport intellectuel et de la paternité humaine sur la modification

Assistance par IA

  • Cette PR a été produite avec l'assistance de Claude Code : les commits concernés portent la remorque Co-authored-by: Claude <noreply@anthropic.com>, auteur et committer restent humains, et scripts/provenance.py archive a été lancé
  • Cette PR a été écrite sans assistance par IA

https://claude.ai/code/session_01YRg8X9dbmth9GXcR9hP4QQ

…at J-1)

Le chaînage SHA-256 du journal n'a pas de clé : qui peut écrire dans le
fichier peut le réécrire en recalculant la chaîne. Les variables de
scellement étaient lues, aucun binaire ne scellait (audit du 2026-09-25,
EN 319 401 REQ-7.10-02 et REQ-7.10-08).

- `tsa-server serve` lance une tâche de fond : au démarrage puis à chaque
  OPENEIDAS_AUDIT_SEAL_INTERVAL, la TSU horodate la tête de chaîne et
  consigne `log.sealed` avec la tête scellée et le jeton RFC 3161,
  certificat compris. `openssl ts -verify -digest <tête>` le vérifie.
- Après chaque scellement, le journal entier est déposé sur un stockage
  objet compatible S3 s'il est configuré (`log.replicated`). Mêmes
  variables OPENEIDAS_S3_* que ca-server et ra-console ; un point d'accès
  sans compartiment ni identifiants refuse le démarrage.
- La copie est périodique, pas faite à chaque jeton : l'émission ne
  dépend pas du stockage. Un échec se compte ; au-delà de trois passes,
  `/healthz` se déclare dégradé (`journal.degraded`) sans passer en
  erreur, la sonde servant aussi à la vivacité. Un scellement en échec
  est retenté après une minute au plus.
- Le réplica WebDAV que le chart et le compose déployaient pour la TSA
  sans jamais l'alimenter est retiré, avec OPENEIDAS_AUDIT_REPLICA_* et
  la clé `webdav-password`. Le chart gagne `tsa.audit.s3.*`.
- Matrice, ARCHITECTURE §7, CPS B.3, API : l'écart J-1 reste ouvert pour
  le contreseing tiers et pour les journaux de la CA et de la console.

Six mutations tuées : jeton ne couvrant pas la tête de chaîne, échec de
copie ou de scellement non compté, copie consignée sans avoir abouti,
scellement en échec jamais signalé, /healthz taisant le journal dégradé.

Co-authored-by: Claude <noreply@anthropic.com>
@PhilippeVienne
PhilippeVienne merged commit b29bdf3 into dev Oct 13, 2026
20 checks passed
@PhilippeVienne
PhilippeVienne deleted the feat/tsa-audit-seal branch October 13, 2026 05:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant