Some checks failed
CD Preprod / Security gate (push) Successful in 31s
CD Preprod / Backend — Lint (push) Successful in 1m4s
CD Preprod / Frontend — Lint & Type-check (push) Successful in 1m10s
CD Preprod / Backend — Unit Tests (push) Successful in 1m5s
CD Preprod / Frontend — Unit Tests (push) Successful in 43s
CD Preprod / Backend — Integration Tests (push) Successful in 46s
CD Preprod / Build Frontend (push) Successful in 33s
CD Preprod / Build Backend (push) Successful in 54s
CD Preprod / Build Log Exporter (push) Successful in 30s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (amd64, frontend) (push) Successful in 24s
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 22s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (arm64, backend) (push) Successful in 24s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (arm64, log-exporter) (push) Successful in 22s
CD Preprod / Image security (${{ matrix.service }}, ${{ matrix.arch }}) (arm64, frontend) (push) Successful in 1m31s
CD Preprod / Deploy to Preprod (push) Failing after 42s
CD Preprod / Notify Success (push) Has been skipped
CD Preprod / Notify Failure (push) Has been skipped
270 lines
16 KiB
Markdown
270 lines
16 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 préprod, 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.
|
|
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é.
|