-
Notifications
You must be signed in to change notification settings - Fork 0
678 lines (605 loc) · 27.5 KB
/
Copy pathci.yml
File metadata and controls
678 lines (605 loc) · 27.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
name: CI
on:
push:
branches: [dev]
pull_request:
permissions:
contents: read
jobs:
test:
name: Clippy, tests
runs-on: ubuntu-latest
# PostgreSQL réel pour les tests d'invariants du registre des opérateurs
# (crates/oe-castore/tests/operators_schema.rs) : ces contraintes et ces
# droits sont portés par la base, un test qui ne l'exécute pas ne prouve
# rien.
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_PASSWORD: test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 10
permissions:
contents: read
# rustsec/audit-check publie son résultat comme un check run —
# sans cette permission explicite, le GITHUB_TOKEN par défaut
# (contents: read seul) fait échouer l'action avec « Resource not
# accessible by integration », indépendamment de tout écart réel.
checks: write
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- name: cargo fmt --check
run: cargo fmt --check
- name: cargo clippy
run: cargo clippy --workspace --all-targets -- -D warnings
# SoftHSM2 pour le test du binaire tsa-server (bin/tsa-server/tests/serve.rs),
# que la matrice de conformité cite comme preuve de mise en service
# (constat D-2) : OE_REQUIRE_SOFTHSM le fait échouer, plutôt que s'ignorer,
# si SoftHSM venait à manquer. Les tests PostgreSQL restent gardés par
# OE_CASTORE_TEST_DSN, absente ici ; l'intégration complète est revalidée
# par les jobs de démonstration (kind, compose) plus bas.
- name: Installe SoftHSM2
run: sudo apt-get update && sudo apt-get install -y softhsm2
- name: cargo test
env:
OE_REQUIRE_SOFTHSM: "1"
run: cargo test --workspace
# Suites PostgreSQL, gardées par OE_CASTORE_TEST_DSN : schéma du registre,
# contraintes et droits du rôle de ra-console (oe-castore), actions
# d'opérateur signées, dont la preuve que la faille R2a est fermée
# (oe-actions, docs/WEBUI.md §16), et amorçage du premier administrateur
# (oe-actions, ca-server).
#
# Sans cette variable, ces tests s'ignorent EN RESTANT VERTS : un service
# PostgreSQL absent, une variable perdue dans un refactoring de workflow, et
# la CI ne prouverait plus rien sans le dire. Chaque test qui s'ignore écrit
# « test PostgreSQL ignoré » ; on lit donc la sortie (`--nocapture`) et on
# échoue si ce message y figure, ou si la base ne répond pas.
- name: cargo test (oe-castore, oe-actions, ca-server, PostgreSQL réel)
run: |
set -o pipefail
test -n "$OE_CASTORE_TEST_DSN" || { echo "::error::OE_CASTORE_TEST_DSN n'est pas définie"; exit 1; }
pg_isready -h localhost -p 5432 -U postgres || { echo "::error::PostgreSQL ne répond pas : les tests seraient ignorés"; exit 1; }
cargo test -p oe-castore -p oe-actions -p ca-server -p ra-console --features oe-castore/postgres -- --nocapture 2>&1 | tee pg-tests.log
if grep -q "test PostgreSQL ignoré" pg-tests.log; then
echo "::error::des tests PostgreSQL se sont ignorés alors que la base est censée être disponible"
grep -n "test PostgreSQL ignoré" pg-tests.log | head
exit 1
fi
env:
OE_CASTORE_TEST_DSN: postgres://postgres:test@localhost:5432/postgres
# RUSTSEC-2023-0071 (« Marvin Attack », rsa < aucune version corrigée
# à ce jour) est un canal auxiliaire temporel sur les opérations de
# clé PRIVÉE de la crate `rsa`. Vérifié dans ce dépôt : `rsa` n'y sert
# jamais qu'à VÉRIFIER une signature avec une clé PUBLIQUE (CSR
# entrantes dans oe-raflow, requêtes/réponses OCSP dans
# oe-ocsp-core) — la clé privée de chaque service ne quitte jamais
# le token PKCS#11 (voir oe-hsm::SigningToken). La seule opération de
# clé privée par `rsa` est oe_hsm::testing::SoftwareToken, documentée
# comme n'étant jamais utilisée en production. Ignoré ici faute de
# correctif amont disponible ; à retirer dès qu'un correctif existe.
- name: cargo audit
uses: rustsec/audit-check@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
ignore: RUSTSEC-2023-0071
licenses:
name: Licences des dépendances (EUPL-1.2 / AGPL-3.0)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Politique dans deny.toml : licences permissives et MPL-2.0 admises,
# tout copyleft qui ferait changer la licence du binaire refusé sauf
# exception humaine consignée. Lancé sur toutes les fonctionnalités.
- name: cargo deny check licenses
uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check licenses
conformance:
name: Matrice de conformité ETSI
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# La sous-commande sort en échec si la matrice est incohérente : une
# exigence déclarée couverte sans mécanisme ni test nommé, ou un écart
# sans cible de levée. C'est ce qui empêche docs/CONFORMITE-ETSI.md de
# se vider de son sens au fil des modifications.
- name: Vérifie la cohérence de la matrice
run: cargo run --bin ca-server -- conformance
# Le document publié est rendu depuis le code : s'il en diverge, c'est
# qu'il a été modifié à la main, ce qui n'a pas de sens ici.
- name: Vérifie que docs/CONFORMITE-ETSI.md est à jour
run: |
cargo run --bin ca-server -- conformance --markdown > /tmp/conformite.md
if ! diff -u docs/CONFORMITE-ETSI.md /tmp/conformite.md; then
echo "docs/CONFORMITE-ETSI.md est désynchronisé : lancez 'make conformance-doc'" >&2
exit 1
fi
# Une matrice plutôt que 3 constructions séquentielles dans un seul job :
# CA/TSA/OCSP tournent en parallèle sur 3 runners, comme le job publish
# ci-dessous — c'est ce qui faisait traîner la CI (~3x le temps d'une
# seule image, compilation Rust + scan Trivy compris).
build:
# Sans référencer explicitement matrix.image ici, GitHub affiche toutes
# les clés de la matrice dans le nom du check (illisible, et ça aurait
# changé le nom à chaque modification d'un champ interne comme
# dockerfile ou pin_env).
name: Construction et scan des images (${{ matrix.image }})
runs-on: ubuntu-latest
needs: test
strategy:
matrix:
include:
- image: CA
dockerfile: deploy/ca-server/Dockerfile
tag: openeidas-ca:ci
# SoftHSM exige un PIN de 4 à 255 caractères : l'entrypoint
# initialise ses tokens avant d'exécuter n'importe quelle
# sous-commande, y compris `version`.
pin_env: OPENEIDAS_ISSUING_PIN
- image: TSA
dockerfile: deploy/tsa/Dockerfile
tag: openeidas-tsa:ci
pin_env: OPENEIDAS_PIN
- image: répondeur OCSP
dockerfile: deploy/ocsp-responder/Dockerfile
tag: openeidas-ocsp-responder:ci
pin_env: OPENEIDAS_PIN
# Console d'exploitation : aucun token, donc aucun PIN, et pas de
# sous-commande `version` (option `--version`, clap).
- image: console RA
dockerfile: deploy/ra-console/Dockerfile
tag: openeidas-ra-console:ci
version_arg: --version
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
# cache-from/cache-to: type=gha rejoue sinon indéfiniment la couche
# apt-get de chaque Dockerfile, gelant les paquets Debian installés à
# leur version du premier build mis en cache — c'est ce qui a laissé
# passer un correctif de sécurité (libpcre2-8-0) malgré apt-get
# update. CACHEBUST change une fois par jour pour forcer sa
# réexécution au moins quotidiennement.
- name: Calcule le cache-bust journalier
id: cachebust
run: echo "value=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
- name: Construit l'image (sans publication)
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
push: false
load: true
tags: ${{ matrix.tag }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.sha }}
CACHEBUST=${{ steps.cachebust.outputs.value }}
- name: Vérifie que le binaire démarre
run: docker run --rm ${{ matrix.pin_env && format('-e {0}=1234', matrix.pin_env) || '' }} ${{ matrix.tag }} ${{ matrix.version_arg || 'version' }}
- name: Scan de vulnérabilités (Trivy)
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: ${{ matrix.tag }}
format: table
severity: CRITICAL,HIGH
exit-code: "1"
ignore-unfixed: true
# Un package GHCR nouvellement créé par GITHUB_TOKEN est parfois privé par
# défaut malgré un dépôt public : après le tout premier passage de ce job,
# vérifier la visibilité de chaque package sur
# https://github.com/orgs/otspi/packages et la passer sur « Public »
# si nécessaire (Package settings > Change visibility).
publish:
name: Publication des images (GHCR)
runs-on: ubuntu-latest
needs: build
# Seuls les push sur dev publient : une pull_request (y compris depuis
# un fork) n'a pas vocation à pousser d'image, et GITHUB_TOKEN y est de
# toute façon en lecture seule.
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
permissions:
contents: read
packages: write
strategy:
matrix:
include:
- image: open-eidas-ca
dockerfile: deploy/ca-server/Dockerfile
- image: open-eidas-tsa
dockerfile: deploy/tsa/Dockerfile
- image: open-eidas-ocsp-responder
dockerfile: deploy/ocsp-responder/Dockerfile
# Image par défaut de raConsole.image dans le chart. Pas épinglée sur
# le staging (job suivant) : la console n'y est pas déployée.
- image: open-eidas-ra-console
dockerfile: deploy/ra-console/Dockerfile
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Voir le commentaire équivalent du job build : sans cache-bust, le
# cache GHA gèlerait les paquets Debian installés à leur version du
# premier build en cache.
- name: Calcule le cache-bust journalier
id: cachebust
run: echo "value=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
- name: Construit et publie l'image
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
push: true
tags: |
ghcr.io/otspi/${{ matrix.image }}:latest
ghcr.io/otspi/${{ matrix.image }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.sha }}
CACHEBUST=${{ steps.cachebust.outputs.value }}
# Épingle les images de staging sur ce commit dans otspi/deploy —
# jamais sur le cluster ni sur ArgoCD directement : cette CI n'a et
# n'aura aucun accès à l'un ou l'autre. C'est le self-heal d'ArgoCD,
# déjà configuré sur l'Application open-eidas-staging, qui applique le
# changement une fois poussé ici.
#
# Nécessite que le secret organisation OPENEIDAS_DEPLOY_APP_ID (identifiant
# de la GitHub App dédiée, écriture seule sur otspi/deploy) et le
# secret OPENEIDAS_DEPLOY_APP_PRIVATE_KEY existent — sans eux, ce job est
# ignoré (pas d'échec bloquant tant que la App n'est pas créée).
pin-staging:
name: Épingle les images de staging (otspi/deploy)
runs-on: ubuntu-latest
needs: publish
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
steps:
- name: Vérifie que la GitHub App est configurée
id: has-app
run: |
if [ -n "${{ secrets.OPENEIDAS_DEPLOY_APP_ID }}" ]; then
echo "configured=true" >> "$GITHUB_OUTPUT"
else
echo "configured=false" >> "$GITHUB_OUTPUT"
echo "::warning::OPENEIDAS_DEPLOY_APP_ID absent — pin de staging ignoré. Voir README pour créer la GitHub App dédiée."
fi
- uses: actions/create-github-app-token@v1
id: app-token
if: steps.has-app.outputs.configured == 'true'
with:
app-id: ${{ secrets.OPENEIDAS_DEPLOY_APP_ID }}
private-key: ${{ secrets.OPENEIDAS_DEPLOY_APP_PRIVATE_KEY }}
owner: open-eidas
repositories: deploy
- uses: actions/checkout@v4
if: steps.has-app.outputs.configured == 'true'
with:
repository: otspi/deploy
token: ${{ steps.app-token.outputs.token }}
- name: Met à jour les tags d'image dans apps/open-eidas-staging.yaml
if: steps.has-app.outputs.configured == 'true'
run: |
python3 -m venv "$RUNNER_TEMP/pin-staging-venv"
"$RUNNER_TEMP/pin-staging-venv/bin/pip" install --quiet ruamel.yaml
"$RUNNER_TEMP/pin-staging-venv/bin/python" - "$GITHUB_SHA" <<'PYEOF'
import io, sys
from ruamel.yaml import YAML
from ruamel.yaml.scalarstring import LiteralScalarString
# ruamel.yaml (round-trip) plutôt que PyYAML : préserve les
# commentaires et le style bloc du fichier — un yaml.safe_load/dump
# classique les efface tous à chaque pin.
yaml = YAML()
yaml.preserve_quotes = True
yaml.indent(mapping=2, sequence=2, offset=0)
sha = sys.argv[1]
path = "apps/open-eidas-staging.yaml"
with open(path) as f:
doc = yaml.load(f)
values = yaml.load(doc["spec"]["source"]["helm"]["values"])
for component in ("ca", "tsa", "ocsp"):
values.setdefault(component, {}).setdefault("image", {})["tag"] = sha
buf = io.StringIO()
yaml.dump(values, buf)
doc["spec"]["source"]["helm"]["values"] = LiteralScalarString(buf.getvalue())
with open(path, "w") as f:
yaml.dump(doc, f)
PYEOF
- name: Commit et push si changement
if: steps.has-app.outputs.configured == 'true'
run: |
git config user.name "open-eidas-deploy-bot[bot]"
git config user.email "open-eidas-deploy-bot[bot]@users.noreply.github.com"
git add apps/open-eidas-staging.yaml
if git diff --cached --quiet; then
echo "Aucun changement de tag, rien à pousser."
exit 0
fi
git commit -m "Épingle le staging sur otspi/open-eidas@${GITHUB_SHA}"
git push origin main
frontend:
name: Frontend de ra-console (typage, build, bout en bout)
runs-on: ubuntu-latest
# La console réelle tourne sur PostgreSQL (examples/e2e_console.rs), avec un
# opérateur dont la clé est confiée à l'authentificateur WebAuthn virtuel de
# Chromium : connexion, déconnexion et verrouillage sont prouvés dans un vrai
# navigateur, en-têtes de sécurité et CSP compris (docs/UI-UX.md §6.3).
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_PASSWORD: test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 10
defaults:
run:
working-directory: bin/ra-console/web
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
cache-dependency-path: bin/ra-console/web/package-lock.json
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Dépendances de développement (jamais dans l'image)
run: npm ci
- name: Typage strict (navigateur et tests)
run: npm run typecheck
- name: web/dist correspond aux sources
# Les assets sont versionnés pour que la compilation Rust n'exige pas
# Node ; un dist/ désynchronisé servirait autre chose que le code relu.
run: |
npm run build
git diff --exit-code -- dist || {
echo "::error::web/dist est désynchronisé : lancez 'npm run build' dans bin/ra-console/web"
exit 1
}
- name: Navigateur de test
run: npx playwright install --with-deps chromium
- name: Parcours de bout en bout (Playwright)
env:
OE_CASTORE_TEST_DSN: postgres://postgres:test@localhost:5432/postgres
run: npx playwright test
- name: Rapport Playwright (en cas d'échec)
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: bin/ra-console/web/playwright-report
retention-days: 7
helm-lint:
name: Lint du chart Helm
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: helm lint
run: helm lint deploy/helm/open-eidas
- name: helm template (rendu complet)
run: helm template open-eidas deploy/helm/open-eidas > /dev/null
- name: helm template refuse l'approbation automatique en production
# Constat R-2 : le rendu doit échouer, pas seulement le documenter.
run: |
if helm template open-eidas deploy/helm/open-eidas \
--set production=true --set ca.autoApprove.enabled=true > /dev/null 2>&1; then
echo "le chart accepte ca.autoApprove.enabled avec production=true" >&2
exit 1
fi
helm template open-eidas deploy/helm/open-eidas --set production=true > /dev/null
- name: helm lint et rendu avec ra-console
run: |
helm lint deploy/helm/open-eidas -f deploy/helm/open-eidas/ci/ra-console-values.yaml
helm template open-eidas deploy/helm/open-eidas \
-f deploy/helm/open-eidas/ci/ra-console-values.yaml > /dev/null
- name: helm template refuse ra-console sans le lien interne de la CA
# La console n'agit que par le lien interne : le rendu doit échouer,
# pas déployer une console inutilisable. Valeurs complètes par
# ailleurs : seule la garde peut faire échouer ce rendu.
run: |
if helm template open-eidas deploy/helm/open-eidas \
-f deploy/helm/open-eidas/ci/ra-console-values.yaml \
--set ca.internal.enabled=false > /dev/null 2>&1; then
echo "le chart accepte raConsole.enabled sans ca.internal.enabled" >&2
exit 1
fi
- name: Le script des droits du chart est celui du dépôt
# Helm ne lit pas de fichier hors du chart : files/ en porte une copie,
# qui ne doit jamais diverger de l'original.
run: |
diff -u crates/oe-castore/sql/ra_console_grants.sql \
deploy/helm/open-eidas/files/ra_console_grants.sql
helm-kind-smoke-test:
name: Amorçage complet + démonstration (Helm sur kind)
runs-on: ubuntu-latest
needs: [build, helm-lint]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Calcule le cache-bust journalier
id: cachebust
run: echo "value=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
- name: Construit l'image de la CA
uses: docker/build-push-action@v6
with:
context: .
file: deploy/ca-server/Dockerfile
push: false
load: true
tags: openeidas-ca:ci
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
CACHEBUST=${{ steps.cachebust.outputs.value }}
- name: Construit l'image TSA
uses: docker/build-push-action@v6
with:
context: .
file: deploy/tsa/Dockerfile
push: false
load: true
tags: openeidas-tsa:ci
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
CACHEBUST=${{ steps.cachebust.outputs.value }}
- name: Construit l'image du répondeur OCSP
uses: docker/build-push-action@v6
with:
context: .
file: deploy/ocsp-responder/Dockerfile
push: false
load: true
tags: openeidas-ocsp-responder:ci
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
CACHEBUST=${{ steps.cachebust.outputs.value }}
- name: Crée un cluster kind
uses: helm/kind-action@v1
with:
cluster_name: open-eidas
- name: Charge les images dans kind
run: |
kind load docker-image openeidas-ca:ci --name open-eidas
kind load docker-image openeidas-tsa:ci --name open-eidas
kind load docker-image openeidas-ocsp-responder:ci --name open-eidas
- name: Installe le chart
run: |
helm install open-eidas deploy/helm/open-eidas \
--namespace open-eidas --create-namespace \
--set ca.image.repository=openeidas-ca \
--set ca.image.tag=ci \
--set ca.autoApprove.enabled=true \
--set tsa.image.repository=openeidas-tsa \
--set tsa.image.tag=ci \
--set tsa.time.policy=monitor \
--set ocsp.image.repository=openeidas-ocsp-responder \
--set ocsp.image.tag=ci
- name: Attend que tous les pods soient prêts
# L'essentiel de l'attente est la génération de quatre bi-clés RSA
# dans SoftHSM (deux pour la hiérarchie de CA en 4096 bits, une par
# service signataire en 3072) : quelques minutes sur un runner
# partagé. 6 minutes laissent une marge confortable.
run: |
kubectl -n open-eidas wait --for=condition=ready pod --all --timeout=6m
- name: Horodate un fichier et vérifie le jeton (openssl ts)
run: |
kubectl -n open-eidas port-forward svc/open-eidas-tsa 8318:8318 &
sleep 3
echo "facture CI $(date -Is)" > facture.txt
openssl ts -query -data facture.txt -sha256 -cert -out facture.tsq
curl -sf -H 'Content-Type: application/timestamp-query' \
--data-binary @facture.tsq http://localhost:8318/tsa -o facture.tsr
curl -sf http://localhost:8318/api/v1/certificate -o chain.pem
awk '/BEGIN CERTIFICATE/{n++} {print > (n == 1 ? "tsu.pem" : "ca.pem")}' chain.pem
openssl ts -verify -in facture.tsr -queryfile facture.tsq -CAfile ca.pem
# Le moteur de CA est maison : il ne peut pas se valider avec ses seuls
# outils. openssl relit ici, indépendamment, ce qu'il a produit.
- name: Vérifie le certificat TSU et la CRL avec openssl
run: |
kubectl -n open-eidas port-forward svc/open-eidas-ca 8320:8320 &
sleep 3
openssl verify -CAfile ca.pem -purpose timestampsign tsu.pem
openssl x509 -in tsu.pem -noout -text | grep -q "Time Stamping"
CRL_URL="$(openssl x509 -in tsu.pem -noout -text \
| grep -A 2 "X509v3 CRL Distribution" | sed -n 's/.*URI://p' | tr -d ' ')"
curl -sf "http://localhost:8320/${CRL_URL#*://*/}" -o issuing.crl
openssl crl -inform DER -in issuing.crl -CAfile ca.pem -noout
- name: Rapport de conformité de l'instance déployée
run: |
curl -sf http://localhost:8320/api/v1/conformance \
| tee conformance.json | head -c 400
grep -q '"matrice_coherente": *true' conformance.json
- name: Interroge le répondeur OCSP pour le certificat TSU
run: |
kubectl -n open-eidas port-forward svc/open-eidas-ocsp 8319:8319 &
sleep 3
openssl ocsp -issuer ca.pem -cert tsu.pem -CAfile ca.pem -no_nonce \
-url http://localhost:8319/ocsp -resp_text | tee ocsp-response.txt
grep -q "tsu.pem: good" ocsp-response.txt
# Constat O-1 (EN 319 411-1 OVR-6.6.3-02) : un numéro de série que cette
# CA n'a jamais émis reçoit `unknown`, jamais `good` par défaut.
- name: Interroge le répondeur OCSP pour un numéro jamais émis
run: |
kubectl -n open-eidas port-forward svc/open-eidas-ocsp 8329:8319 &
sleep 3
openssl ocsp -issuer ca.pem -serial 0x7e57 -CAfile ca.pem -no_nonce \
-url http://localhost:8329/ocsp -resp_text | tee ocsp-unknown.txt
grep -q "0x7e57: unknown" ocsp-unknown.txt
- name: Vérifie la chaîne des journaux d'audit (TSA et CA)
run: |
kubectl -n open-eidas exec deploy/open-eidas-tsa -- tsa-server verify-audit
kubectl -n open-eidas exec deploy/open-eidas-ca -c ca -- ca-server verify-audit
# L'approbation est ce qu'un auditeur vient vérifier : elle doit être
# nominative et tracée, y compris lorsque l'opérateur est technique.
- name: Vérifie que chaque émission a été approuvée par un opérateur nommé
run: |
kubectl -n open-eidas exec deploy/open-eidas-ca -c ca -- \
ca-server ra list ISSUED | tee requests.txt
grep -q "ci-bootstrap" requests.txt
- name: État des pods et journaux (utile en cas d'échec)
if: always()
run: |
kubectl -n open-eidas get pods -o wide
kubectl -n open-eidas describe pods
for c in ca ra-autoapprove; do
echo "=== ca / $c ==="
kubectl -n open-eidas logs deploy/open-eidas-ca -c "$c" --tail 200 || true
done
echo "=== postgres ==="
kubectl -n open-eidas logs statefulset/open-eidas-postgres --tail 100 || true
echo "=== tsa ==="
kubectl -n open-eidas logs deploy/open-eidas-tsa --tail 200 || true
echo "=== ocsp ==="
kubectl -n open-eidas logs deploy/open-eidas-ocsp --tail 200 || true
compose-smoke-test:
name: Amorçage complet + démonstration (docker compose)
runs-on: ubuntu-latest
needs: build
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Amorce la pile (CA + TSA + répondeur OCSP)
# Les runners GitHub-hébergés ne garantissent pas de sortie UDP/123 vers
# les deux serveurs NTP externes exigés par la politique "enforce" ; le
# job helm-kind-smoke-test contourne déjà ce même aléa réseau en passant
# tsa.time.policy=monitor.
env:
OPENEIDAS_TIME_POLICY: monitor
run: ./scripts/bootstrap.sh
- name: Horodate un fichier et vérifie le jeton (openssl ts)
run: ./scripts/demo.sh
- name: Vérifie la chaîne des journaux d'audit (TSA et CA)
run: |
docker compose exec -T tsa tsa-server verify-audit
docker compose exec -T ca ca-server verify-audit
- name: Journaux (utile en cas d'échec)
if: always()
run: docker compose logs --tail 200
- name: Arrête la pile
if: always()
run: docker compose down -v