All checks were successful
CD Preprod / Security gate (push) Successful in 29s
CD Preprod / Backend — Lint (push) Successful in 1m2s
CD Preprod / Backend — Unit Tests (push) Successful in 1m7s
CD Preprod / Frontend — Lint & Type-check (push) Successful in 1m11s
CD Preprod / Frontend — Unit Tests (push) Successful in 41s
CD Preprod / Backend — Integration Tests (push) Successful in 46s
CD Preprod / Build Frontend (push) Successful in 32s
CD Preprod / Build Backend (push) Successful in 53s
CD Preprod / Build Log Exporter (push) Successful in 32s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (amd64, frontend) (push) Successful in 23s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (amd64, backend) (push) Successful in 25s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (amd64, log-exporter) (push) Successful in 21s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (arm64, backend) (push) Successful in 24s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (arm64, frontend) (push) Successful in 23s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (arm64, log-exporter) (push) Successful in 22s
CD Preprod / Deploy to Preprod (push) Successful in 44s
CD Preprod / Notify Failure (push) Has been skipped
CD Preprod / Notify Success (push) Successful in 2s
278 lines
17 KiB
Markdown
278 lines
17 KiB
Markdown
# CI/CD sécurisée — Gitea 1.22.6
|
||
|
||
La version du serveur `https://gitea.ops.xpeditis.com` a été vérifiée via
|
||
`/api/v1/version` : **1.22.6**. Les pipelines ciblent Gitea Actions / act_runner,
|
||
avec des jobs exécutés dans des conteneurs Linux AMD64 ou ARM64.
|
||
|
||
## Workflows et contrôles
|
||
|
||
Les quatre workflows actifs se trouvent dans `.gitea/workflows/`. Les anciennes
|
||
copies `.github/workflows/` sont déplacées pour éviter une double exécution.
|
||
Les étapes de sécurité sont exposées directement dans chaque workflow pour que
|
||
Gitea affiche leurs journaux ; les scripts restent communs. Aucun workflow
|
||
réutilisable GitHub ni client `gh` n'est nécessaire.
|
||
|
||
| Pipeline | Déclenchement | Contrôles |
|
||
| --- | --- | --- |
|
||
| Dev CI | Push/PR vers `dev` | Lint, types, tests unitaires, sécurité |
|
||
| PR Checks | PR vers `preprod` ou `main` | Idem + tests d'intégration PostgreSQL/Redis |
|
||
| CD Preprod | Push vers `preprod` | Idem + build, scan AMD64/ARM64, déploiement, contrôles HTTP |
|
||
| CD Production | Push vers `main` | Qualité/tests/sécurité, provenance préprod, build frontend, scan AMD64, déploiement SSH, tests de fumée |
|
||
|
||
Le job **Security gate** exécute :
|
||
|
||
- `npm audit --package-lock-only --audit-level=high` sur la racine, le backend,
|
||
le frontend et le log-exporter, sans installer de dépendances ni exécuter leurs
|
||
scripts. Les dépendances de développement sont incluses.
|
||
- Trivy sur les fichiers suivis du commit : détection de secrets et de mauvaises
|
||
configurations Docker/Kubernetes/Terraform, seuil HIGH/CRITICAL.
|
||
- La validation statique des workflows et les tests des scripts de promotion/santé.
|
||
|
||
Les images sont analysées par digest, avec HIGH/CRITICAL bloquants, y compris
|
||
lorsqu'aucun correctif n'est disponible. Les erreurs de scanner ou de registre
|
||
échouent aussi : aucune exclusion générale ni échec masqué n'est ajouté.
|
||
Les installations applicatives utilisent `npm ci`, le runtime est Node 22 et les
|
||
actions sont épinglées par SHA. Trivy, Actionlint et Hetzner CLI sont téléchargés
|
||
avec détection native AMD64/ARM64 et vérification SHA256. Le lint backend ne réécrit plus les fichiers.
|
||
|
||
Les rapports sont joints aux exécutions **Gitea Actions** avec
|
||
`actions/upload-artifact` **v3**, compatible avec le protocole de cette version
|
||
Gitea. `security-reports` est conservé 7 jours, `image-security-*` 14 jours selon
|
||
la politique de rétention du serveur. Les valeurs et extraits de code des secrets
|
||
sont retirés du rapport téléchargeable. Les URLs GitHub servent uniquement à
|
||
récupérer les actions publiques épinglées, pas à exécuter les pipelines.
|
||
|
||
Ces scans ne remplacent pas un audit du code métier ni un test d'intrusion ; la
|
||
recherche de secrets porte sur le commit courant, pas tout l'historique.
|
||
|
||
## Promotion sans API GitHub
|
||
|
||
Gitea 1.22.6 n'expose pas d'API de consultation des runs Actions dans son Swagger.
|
||
La provenance utilise donc le registre Scaleway existant : après scans, webhooks
|
||
et contrôles HTTP backend/frontend réussis, le job préprod publie des marqueurs
|
||
`validated-preprod-<SHA complet>` pour le backend et le log-exporter, à partir des
|
||
digests construits et scannés. Le log-exporter est construit/scanné par cette
|
||
chaîne ; son déploiement n'a pas de webhook ni de contrôle HTTP dédié existant.
|
||
|
||
La production accepte seulement un commit de main ou un de ses parents dont
|
||
l'arbre Git est identique, avec les deux marqueurs présents. Elle récupère leurs
|
||
digests, les rescane et les promeut sans reconstruire le backend/log-exporter.
|
||
Le frontend est reconstruit avec les URLs prod puis scanné par digest. Les tags
|
||
`prod-SHA` et `latest` ne sont publiés que dans le job de déploiement, après scans.
|
||
|
||
Utiliser un merge classique ou fast-forward de préprod vers main ; un squash ou
|
||
rebase qui supprime cette provenance sera refusé. Il faut une première préprod
|
||
réussie avec ces nouveaux workflows avant de promouvoir en production. La preuve
|
||
repose sur les droits du registre : réserver l'écriture des marqueurs au compte CI
|
||
et ne pas les créer manuellement. Ce n'est pas une attestation cryptographique.
|
||
|
||
Les images candidates peuvent rester dans le registre après un échec de scan,
|
||
sans être publiées sous les alias d'environnement par le pipeline.
|
||
|
||
## Configuration nécessaire sur ton instance
|
||
|
||
Gitea 1.22 ignore notamment `concurrency`, `permissions`, `environment`, les délais
|
||
YAML de jobs et `workflow_dispatch`. Ces paramètres ne sont donc pas présentés
|
||
comme des protections effectives dans les workflows adaptés.
|
||
|
||
1. Activer Actions dans le dépôt et disposer d'un runner Docker Linux AMD64 ou ARM64 avec
|
||
le label `ubuntu-latest`, Git, Bash, Python 3, curl, tar, timeout et les outils
|
||
Docker. Les builds multiarchitecture ont besoin du support QEMU/binfmt.
|
||
Les services d'intégration sont joints via `postgres` et `redis`, sans ports
|
||
hôte fixes ; un runner en mode host n'est pas la cible de ces workflows.
|
||
2. Les déploiements préprod et production utilisent le runner existant avec le
|
||
label **`ubuntu-latest`**, comme les builds et les audits. Aucun runner dédié
|
||
`xpeditis-deploy` n'est nécessaire. Ce runner doit aussi disposer de SSH et
|
||
rsync pour le déploiement de production. Gitea 1.22 ne prenant pas en charge
|
||
`concurrency`, les workflows ne garantissent pas la sérialisation des
|
||
déploiements si les runners autorisent plusieurs jobs simultanés.
|
||
Pour éviter les chevauchements, attendre la fin d'un déploiement avant de
|
||
déclencher le suivant sur le même environnement.
|
||
3. Protéger `dev`, `preprod`, `main` dans les réglages de branches Gitea : interdire
|
||
les push directs/force-push et exiger **Security gate**, les deux contrôles
|
||
qualité et les deux tests unitaires ; ajouter l'intégration pour preprod/main.
|
||
Choisir les noms exacts proposés après la première exécution. Les approbations
|
||
humaines doivent être imposées sur les PR, pas via `environment`.
|
||
4. Renseigner les secrets/variables dans **Settings → Actions** du dépôt Gitea :
|
||
registre, webhooks Portainer, URLs de build et identifiants
|
||
SSH/Hetzner déjà référencés. Les secrets ne sont pas des secrets d'environnement
|
||
GitHub. Les clés SSH temporaires sont écrites dans le répertoire du job.
|
||
Les contrôles de santé préprod utilisent par défaut
|
||
`https://api.preprod.xpeditis.com/api/v1/health` et
|
||
`https://app.preprod.xpeditis.com/api/health`, conformément aux routes Traefik
|
||
de la stack. Le contrôle frontend cible son endpoint de santé pour éviter
|
||
la redirection HTTP 307 de la page d’accueil.
|
||
`PREPROD_BACKEND_URL` et `PREPROD_FRONTEND_URL` sont des surcharges facultatives
|
||
(URL de base sans chemin API), lues dans les secrets puis les variables du dépôt.
|
||
Une réponse HTTP 200 reste obligatoire pour valider le déploiement.
|
||
5. Les fonctions manuelles `workflow_dispatch` ne sont pas disponibles sur 1.22.
|
||
L'ancien workflow de rollback est conservé hors du dossier actif dans
|
||
`.gitea/manual/rollback.reference.yml` comme référence, sans prétendre qu'il est
|
||
exécutable sur cette version. Le rollback SSH automatique de production reste
|
||
actif en cas d'échec du déploiement ou des tests de fumée. Pour une intervention
|
||
manuelle, utiliser la procédure serveur existante ; ne pas ajouter un faux
|
||
bouton de lancement Gitea.
|
||
|
||
Ces réglages serveur/runners n'ont pas été modifiés depuis cette session. Aucun
|
||
pipeline distant ni déploiement n'a été lancé.
|
||
|
||
## Mises à jour de dépendances
|
||
|
||
Dependabot a été remplacé par `renovate.json`. La configuration propose des PR
|
||
vers `dev` pour npm, Docker et les actions, sans fusion automatique. Elle n'installe
|
||
ni ne démarre un bot. Un service Renovate doit être configuré séparément avec
|
||
`RENOVATE_PLATFORM=gitea`, `RENOVATE_ENDPOINT=https://gitea.ops.xpeditis.com/api/v1`,
|
||
un compte bot et son token, et le dépôt `David/xpeditis2.0`. Conserver le token hors
|
||
du dépôt. Les hashes des outils téléchargés dans les scripts doivent être mis à
|
||
jour en même temps que leurs versions.
|
||
|
||
## Alertes déjà constatées
|
||
|
||
Audits npm des lockfiles au 22 septembre 2026 :
|
||
|
||
| Projet | HIGH | CRITICAL |
|
||
| --- | ---: | ---: |
|
||
| Racine | 0 | 0 |
|
||
| Backend | 50 | 0 |
|
||
| Frontend | 13 | 1 |
|
||
| Log-exporter | 0 | 0 |
|
||
|
||
Ce sont des entrées de dépendances signalées, pas nécessairement autant de CVE
|
||
distinctes. L'entrée critique frontend concerne `next` ; npm propose une migration
|
||
majeure. Aucune mise à jour applicative forcée n'a été faite pour masquer ces résultats.
|
||
|
||
Le scan local des configurations a relevé 11 alertes Kubernetes HIGH : systèmes
|
||
de fichiers inscriptibles, droits de monitoring et accès hôte. Elles restent à
|
||
examiner et bloquantes. Certains accès peuvent être nécessaires au monitoring.
|
||
|
||
Les secrets Stripe en dur de la stack préprod ont été remplacés par les variables
|
||
Portainer obligatoires `STRIPE_SECRET_KEY` et `STRIPE_WEBHOOK_SECRET`. Renseigner
|
||
ces variables avant redéploiement. Si les anciennes valeurs sont actives, les
|
||
révoquer/renouveler dans Stripe : elles restent dans l'historique Git. Le scan de
|
||
ces configurations ne signale plus ce secret après modification.
|
||
|
||
## Validation
|
||
|
||
Les 27 tests offline de promotion et santé passent : arbre différent, marqueurs
|
||
manquants, panne du registre, digest invalide, mauvaise branche, saisie hostile et
|
||
échec HTTP sont refusés. Les tests vérifient aussi que les échecs et délais des
|
||
audits restent bloquants et que le rapport masque les valeurs de secrets. Actionlint valide la syntaxe après normalisation des URLs
|
||
d'actions Gitea ; ce contrôle ne remplace pas une exécution avec act_runner.
|
||
|
||
```bash
|
||
ACTIONLINT_BIN=/chemin/vers/actionlint bash scripts/ci/validate-workflows.sh
|
||
```
|
||
|
||
La correction du 23 septembre 2026 a aussi été exécutée dans un conteneur Linux
|
||
ARM64 : Trivy 0.74.0, Actionlint 1.7.12 et Hetzner CLI 1.49.0 démarrent avec leurs
|
||
archives officielles vérifiées. `activate-node.sh` place le Node 22 natif du cache
|
||
en tête du PATH et vérifie son exécution ; un Node 18 préinstallé ne peut plus
|
||
être sélectionné silencieusement. Node 22.23.2 a été vérifié dans l'étape suivante.
|
||
Le téléchargement/affichage de version dans `setup-node` ne suffit pas à confirmer
|
||
le runtime utilisé par les étapes shell ; la nouvelle étape affiche le chemin
|
||
absolu et la version réellement activés.
|
||
|
||
L'installateur commun est `scripts/ci/install-tool.sh`. Chaque architecture a sa
|
||
propre archive et son empreinte. QEMU couvre AMD64 et ARM64 dans les builds préprod
|
||
et frontend prod, dont la cible reste AMD64 même si le runner est ARM64.
|
||
|
||
Les installations ont été vérifiées avec `npm ci --dry-run --offline
|
||
--ignore-scripts --legacy-peer-deps`. Les builds Docker, les tests applicatifs sous
|
||
Node 22, le transport réel des artefacts et les déploiements sont encore à valider
|
||
sur les runners de l'instance.
|
||
|
||
Références : [différences Gitea 1.22](https://docs.gitea.com/1.22/usage/actions/comparison/),
|
||
[Renovate sur Gitea](https://docs.renovatebot.com/modules/platform/gitea/).
|
||
|
||
## Diagnostic du run Gitea 146
|
||
|
||
Le [journal complet du job sécurité](https://gitea.ops.xpeditis.com/David/xpeditis2.0/actions/runs/146/jobs/0/logs)
|
||
confirme que Node 22 et Trivy démarrent, que les validations passent et que
|
||
l'artefact `security-reports` est bien téléversé. L'affichage de l'action composite
|
||
s'arrête pourtant visuellement au lancement de Trivy. Les quatre workflows
|
||
exposent désormais installation, validation, audit, résumé et téléversement comme
|
||
étapes distinctes. Le résumé s'exécute même si l'audit échoue, sans afficher de
|
||
valeurs de secrets. Un rapport absent ou invalide est signalé comme indisponible.
|
||
|
||
Ce run échoue réellement sur les audits : 50 vulnérabilités élevées backend,
|
||
13 élevées et une critique frontend, quatre détections de secrets et douze
|
||
alertes d'infrastructure. Ces nombres décrivent ce run, pas un audit futur.
|
||
Les seuils restent bloquants ; cette correction d'affichage ne corrige pas les
|
||
vulnérabilités. Les douze erreurs Prettier du job backend ont été corrigées et
|
||
le lint sans correction automatique passe localement.
|
||
|
||
## Correction des audits du 24 septembre 2026
|
||
|
||
Les dépendances ont été actualisées sans suppression de l'audit : Next 15.5.26,
|
||
React 19, Nest 11.2.6, MJML 5 (rendu attendu de façon asynchrone) et Nodemailer 10.
|
||
PostCSS 8.5.28 est imposé uniquement sous Next pour corriger sa dépendance épinglée.
|
||
Les modules Tiptap sont alignés. SheetJS utilise la distribution officielle 0.20.3,
|
||
avec intégrité dans le lockfile, en conservant l'API des exports Excel.
|
||
|
||
Les audits actuels passent au seuil HIGH/CRITICAL : zéro vulnérabilité frontend,
|
||
trois alertes modérées backend (csv-parse, uuid et la dépendance d'exceljs).
|
||
Ces résultats sont datés et ne garantissent pas l'absence de nouvelles alertes.
|
||
|
||
Les clés Stripe embarquées dans le script de configuration et les deux stacks
|
||
préprod sont retirées. Renseigner `STRIPE_SECRET_KEY` et `STRIPE_WEBHOOK_SECRET`
|
||
dans Portainer avant de redéployer ces stacks, puis renouveler les valeurs qui
|
||
étaient dans Git. Le placeholder de l'adaptateur n'est pas une véritable clé.
|
||
|
||
Les exceptions approuvées sont dans `.trivyignore.yaml`, chargées explicitement
|
||
par l'audit source : quatre règles pour node-exporter et une pour le proxy
|
||
cAdvisor de Prometheus, uniquement sur leurs chemins précis, jusqu'au
|
||
**24 octobre 2026**. Aucune exclusion npm ou de secret. Le test réel Trivy vérifie
|
||
qu'une copie dans un autre fichier et une exception expirée échouent toujours.
|
||
Le droit nodes/proxy inutile de Promtail est retiré.
|
||
|
||
Les racines des conteneurs backend, migration, Loki, Prometheus et Grafana sont
|
||
en lecture seule, avec volumes d'écriture dédiés. Le backend initialise son
|
||
volume CSV à partir de la même image que l'application ; le script Kubernetes
|
||
applique le manifeste complet pour prendre en charge les déploiements existants.
|
||
Les données temporaires des volumes emptyDir restent éphémères, comme les anciens
|
||
fichiers écrits dans les pods. PostgreSQL démarre en UID 999 : le provisionnement
|
||
du nœud attribue déjà les volumes et certificats à cet utilisateur. L'image WAL-G
|
||
configurée reste AMD64 ; son archive ARM64 n'est pas publiée à cette URL.
|
||
|
||
Validation locale : lint backend/frontend, types frontend, 27 tests de scripts
|
||
plus un test d'intégration Trivy (trois scénarios), builds Docker Node 22 backend
|
||
et frontend, rendu email et copie CSV sur système en lecture seule, démarrage
|
||
PostgreSQL AMD64 sans root et scan source Trivy. Les tests applicatifs couvrent
|
||
les exports, emails, contrôles d'accès et TLS. Aucun déploiement réel ni nouvelle
|
||
exécution Gitea n'est effectué par cette correction locale.
|
||
|
||
Sources : [migration Next 15](https://nextjs.org/docs/app/guides/upgrading/version-15),
|
||
[distribution SheetJS](https://docs.sheetjs.com/docs/getting-started/installation/nodejs/),
|
||
[exceptions Trivy](https://trivy.dev/docs/dev/configuration/filtering/).
|
||
|
||
## Images de déploiement : npm global (25 septembre 2026)
|
||
|
||
Les rapports du run Gitea 150 attribuent huit alertes HIGH identiques, sur AMD64
|
||
et ARM64, aux dépendances du npm global livré dans `node:22-alpine` :
|
||
`usr/local/lib/node_modules/npm/node_modules/...`. Les audits npm du projet ne
|
||
couvrent pas ce gestionnaire global ; un audit applicatif vert ne suffit donc
|
||
pas à valider l'image finale.
|
||
|
||
Les trois Dockerfiles désinstallent le npm global avec `npm uninstall --global
|
||
npm` uniquement après les installations nécessaires, dans l'image d'exécution.
|
||
Le backend et le frontend gardent npm dans leurs étages de construction.
|
||
Les services démarrent avec Node, et les migrations backend utilisent directement
|
||
TypeORM via `startup.js`. La procédure manuelle Portainer utilise également Node.
|
||
Aucune CVE n'est ignorée et les scans par digest restent bloquants en préprod/prod.
|
||
|
||
Chaque job de scan affiche maintenant un résumé indiquant CVE, paquet, version,
|
||
correctif disponible et chemin, même après échec. Le rapport JSON complet reste
|
||
joint en artefact. Les barres de progression sont désactivées pour éviter les
|
||
journaux illisibles. L'avertissement de Trivy 0.74 sur la liste EOL d'Alpine 3.24
|
||
n'est pas la cause du code de sortie 1 observé : il est aussi présent sur les
|
||
scans corrigés qui retournent 0.
|
||
|
||
Validation du correctif : reconstruction des trois images ARM64 et du log-exporter
|
||
AMD64 ; scans Trivy au seuil HIGH/CRITICAL sans exclusions ; HTTP 200 sur les
|
||
routes de santé frontend et log-exporter ; chargement du démarrage backend et de
|
||
la CLI TypeORM sans npm/npx ; validation des workflows et 30 tests de scripts.
|
||
Les images backend/frontend AMD64 et le déploiement restent à confirmer par la
|
||
prochaine exécution Gitea après publication du correctif. Relancer uniquement les
|
||
anciens jobs de scan réanalyse les anciens digests vulnérables : il faut rebâtir
|
||
les images à partir du commit corrigé.
|