From cfc194a3fbe9075497bb55ba8e88ba0821f9efb9 Mon Sep 17 00:00:00 2001 From: Dorian Date: Wed, 23 Sep 2026 09:36:10 +0200 Subject: [PATCH 1/3] fix(backend): ajoute l'en-tete Cross-Origin-Resource-Policy sur toutes les reponses --- apps/backend/app/api/middleware.py | 3 +++ apps/backend/tests/api/test_hardening.py | 3 ++- docs/architecture/20-backend.md | 7 +++++-- docs/architecture/owasp-traceabilite.md | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/backend/app/api/middleware.py b/apps/backend/app/api/middleware.py index 0a01192..5ef8e9e 100644 --- a/apps/backend/app/api/middleware.py +++ b/apps/backend/app/api/middleware.py @@ -16,6 +16,9 @@ EN_TETES: Final[dict[str, str]] = { "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", "Referrer-Policy": "no-referrer", + # same-origin : le frontend ne consomme l'API qu'au travers d'un proxy same-origin + # (proxy.conf.json en dev, ingress /api en prod, cf. docs/architecture/20-backend.md). + "Cross-Origin-Resource-Policy": "same-origin", } PREFIXE_AUTHENTIFICATION: Final = "/auth" diff --git a/apps/backend/tests/api/test_hardening.py b/apps/backend/tests/api/test_hardening.py index 3ee1170..069bcdc 100644 --- a/apps/backend/tests/api/test_hardening.py +++ b/apps/backend/tests/api/test_hardening.py @@ -23,8 +23,9 @@ async def interroge( ("x-content-type-options", "nosniff"), ("x-frame-options", "DENY"), ("referrer-policy", "no-referrer"), + ("cross-origin-resource-policy", "same-origin"), ], - ids=["nosniff", "anti_iframe", "referrer"], + ids=["nosniff", "anti_iframe", "referrer", "corp"], ) async def test_every_response_carries_the_security_headers( client: AsyncClient, entete: str, valeur: str diff --git a/docs/architecture/20-backend.md b/docs/architecture/20-backend.md index 1b6f9fc..4bc0647 100644 --- a/docs/architecture/20-backend.md +++ b/docs/architecture/20-backend.md @@ -423,8 +423,11 @@ Le reste, par ordre de surface : écriture des journaux. C'est la troisième ligne de défense : la première est de ne rien passer de secret au logger, la deuxième de ne jamais mettre un jeton dans une URL. - En-têtes posés par l'application : `X-Content-Type-Options`, `X-Frame-Options`, - `Referrer-Policy`, plus `Cache-Control: no-store` sur `/auth/*`. HSTS et CSP appartiennent au - terminateur TLS, que l'application ne connaît pas : le reverse proxy les pose + `Referrer-Policy`, `Cross-Origin-Resource-Policy: same-origin`, plus `Cache-Control: no-store` + sur `/auth/*`. Le CORP est fixé à `same-origin` parce que le frontend ne consomme l'API qu'au + travers d'un proxy same-origin (`proxy.conf.json` en dev, ingress `/api` en prod), jamais + directement cross-origin depuis le navigateur. HSTS et CSP appartiennent au terminateur TLS, que + l'application ne connaît pas : le reverse proxy les pose ([ADR 0007](../adr/0007-terminaison-tls-et-reverse-proxy-nginx.md)). - Le conteneur tourne en utilisateur non-root, avec un `HEALTHCHECK` sur `/api/v1/health/live`. - TLS, limitation de débit au frontal et journal d'accès sont portés par le reverse proxy. diff --git a/docs/architecture/owasp-traceabilite.md b/docs/architecture/owasp-traceabilite.md index 7202cfa..15618ad 100644 --- a/docs/architecture/owasp-traceabilite.md +++ b/docs/architecture/owasp-traceabilite.md @@ -39,7 +39,7 @@ lecture seule ; plusieurs lignes resteront à compléter une fois les endpoints | Cinq gardes de configuration qui refusent le démarrage plutôt que de dégrader silencieusement | `app/core/config.py` | A05 | | Documentation interactive fermée hors développement, `/metrics` derrière un jeton, sonde qui ne publie plus de version | `app/main.py`, `app/api/security.py` | A05 | | Scan dynamique OWASP ZAP de l'API authentifiée (compte `lecteur` jetable), non bloquant, configuration par défaut du backend uniquement (ni TLS ni en-têtes du reverse proxy) | `.github/workflows/dast.yml`, `scripts/dast-token.sh` | A05, API8 Security Misconfiguration | -| En-têtes `nosniff`, `DENY`, `no-referrer`, et `no-store` sur les routes d'authentification | `app/api/middleware.py` | A05 | +| En-têtes `nosniff`, `DENY`, `no-referrer`, `Cross-Origin-Resource-Policy: same-origin`, et `no-store` sur les routes d'authentification | `app/api/middleware.py` | A05 | | Refus de rétrograder ou désactiver le dernier administrateur actif | `app/services/user.py` | A04 Insecure Design | | Amorçage du premier administrateur hors dépôt, mot de passe jamais dans `argv` ni dans Git | `app/cli.py` | A02, A05 | | Réponse de l'API Mock bornée avant écriture : timeout, plafond de sites et de mesures, bornes physiques par grandeur, recopie des seuls champs attendus | `app/etl/mock_api_import.py` | API10 Unsafe Consumption of APIs | From ac05da700163d29158d683cbfd204668e02d494b Mon Sep 17 00:00:00 2001 From: Dorian Date: Wed, 23 Sep 2026 10:30:06 +0200 Subject: [PATCH 2/3] docs(backend): corrige la justification du CORP same-origin (no-cors, pas d'ingress) --- apps/backend/app/api/middleware.py | 4 ++-- docs/architecture/20-backend.md | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/backend/app/api/middleware.py b/apps/backend/app/api/middleware.py index 5ef8e9e..1908e76 100644 --- a/apps/backend/app/api/middleware.py +++ b/apps/backend/app/api/middleware.py @@ -16,8 +16,8 @@ EN_TETES: Final[dict[str, str]] = { "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", "Referrer-Policy": "no-referrer", - # same-origin : le frontend ne consomme l'API qu'au travers d'un proxy same-origin - # (proxy.conf.json en dev, ingress /api en prod, cf. docs/architecture/20-backend.md). + # same-origin : aucun client ne charge l'API en no-cors depuis une autre origine + # (proxy.conf.json en dev, reverse proxy nginx ensuite, cf. docs/architecture/20-backend.md). "Cross-Origin-Resource-Policy": "same-origin", } diff --git a/docs/architecture/20-backend.md b/docs/architecture/20-backend.md index 4bc0647..cc4411b 100644 --- a/docs/architecture/20-backend.md +++ b/docs/architecture/20-backend.md @@ -424,9 +424,11 @@ Le reste, par ordre de surface : de secret au logger, la deuxième de ne jamais mettre un jeton dans une URL. - En-têtes posés par l'application : `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Cross-Origin-Resource-Policy: same-origin`, plus `Cache-Control: no-store` - sur `/auth/*`. Le CORP est fixé à `same-origin` parce que le frontend ne consomme l'API qu'au - travers d'un proxy same-origin (`proxy.conf.json` en dev, ingress `/api` en prod), jamais - directement cross-origin depuis le navigateur. HSTS et CSP appartiennent au terminateur TLS, que + sur `/auth/*`. Le CORP est fixé à `same-origin` parce qu'aucun client légitime ne charge l'API + en `no-cors` (image, script, média) depuis une autre origine : le frontend l'appelle en relatif + (`/api/v1`), sur sa propre origine, via `proxy.conf.json` en dev et le reverse proxy nginx + (`infra/proxy/conf.d/enervision.conf`) en recette et en production. Les appels `HttpClient`, en + mode `cors`, n'y sont de toute façon pas soumis. HSTS et CSP appartiennent au terminateur TLS, que l'application ne connaît pas : le reverse proxy les pose ([ADR 0007](../adr/0007-terminaison-tls-et-reverse-proxy-nginx.md)). - Le conteneur tourne en utilisateur non-root, avec un `HEALTHCHECK` sur `/api/v1/health/live`. From 10cc408b03d8bb3b2b27244a3db98d1f2ca698b0 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Wed, 23 Sep 2026 10:43:48 +0200 Subject: [PATCH 3/3] =?UTF-8?q?feat(deploy):=20ajoute=20un=20environnement?= =?UTF-8?q?=20dev=20d=C3=A9ploy=C3=A9=20=C3=A0=20la=20demande?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Troisième projet Compose sur la VM ENI, /srv/enervision/dev, alimenté par workflow_dispatch de n'importe quelle branche autre que dev et main (https://dev.enervision.local:9443). La recette suit toujours dev, la production main. - deploy.yml : routage main -> prod, dev -> rec, autre -> dev ; groupe de concurrence par environnement et non plus par branche. - provision-host.sh : prépare le dossier dev (ports 9443, 5435, 8027, 8084) ; passe safe.directory à git, faute de quoi un second passage en root, celui de terraform apply, échoue sur les clones déjà remis au runner. - ADR 0017, 10-infra.md, 50-cicd.md, infra/README.md à jour. --- .github/workflows/deploy.yml | 14 +++-- .../0017-environnement-dev-a-la-demande.md | 56 +++++++++++++++++++ docs/architecture/10-infra.md | 35 ++++++------ docs/architecture/50-cicd.md | 5 +- infra/README.md | 12 ++-- infra/terraform/environments/vm-eni/main.tf | 2 +- .../environments/vm-eni/variables.tf | 2 +- scripts/provision-host.sh | 22 +++++--- 8 files changed, 109 insertions(+), 39 deletions(-) create mode 100644 docs/adr/0017-environnement-dev-a-la-demande.md diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 411e164..63ffcd9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,6 +3,8 @@ # espace de travail : `.env`, certificats et volumes y survivent d'un déploiement à l'autre. # Piège : jamais de déclencheur `pull_request` ici. Sur un dépôt public, une PR de fork # exécuterait son code sur la machine de production (ADR 0009) - job deploy. +# Pourquoi : `main` va en prod, `dev` en recette, et toute autre branche lancée à la main +# (workflow_dispatch) va dans `dev`, la vitrine d'une branche de travail (ADR 0017). name: Déploiement @@ -14,8 +16,10 @@ on: permissions: contents: read +# Par environnement et non par branche : deux branches lancées dans `dev` écriraient sinon +# dans le même dossier en même temps. concurrency: - group: deploy-${{ github.ref_name }} + group: deploy-${{ github.ref_name == 'main' && 'prod' || github.ref_name == 'dev' && 'rec' || 'dev' }} cancel-in-progress: false jobs: @@ -23,11 +27,11 @@ jobs: runs-on: [self-hosted, linux, eni-g3] timeout-minutes: 30 environment: - name: ${{ github.ref_name == 'main' && 'prod' || 'rec' }} - url: ${{ github.ref_name == 'main' && 'https://enervision.local' || 'https://rec.enervision.local:8443' }} + name: ${{ github.ref_name == 'main' && 'prod' || github.ref_name == 'dev' && 'rec' || 'dev' }} + url: ${{ github.ref_name == 'main' && 'https://enervision.local' || github.ref_name == 'dev' && 'https://rec.enervision.local:8443' || 'https://dev.enervision.local:9443' }} env: - ENVIRONNEMENT: ${{ github.ref_name == 'main' && 'prod' || 'rec' }} - PORT_HTTPS: ${{ github.ref_name == 'main' && '443' || '8443' }} + ENVIRONNEMENT: ${{ github.ref_name == 'main' && 'prod' || github.ref_name == 'dev' && 'rec' || 'dev' }} + PORT_HTTPS: ${{ github.ref_name == 'main' && '443' || github.ref_name == 'dev' && '8443' || '9443' }} steps: - name: Aligner le dossier de l'environnement sur la branche poussée run: | diff --git a/docs/adr/0017-environnement-dev-a-la-demande.md b/docs/adr/0017-environnement-dev-a-la-demande.md new file mode 100644 index 0000000..549d551 --- /dev/null +++ b/docs/adr/0017-environnement-dev-a-la-demande.md @@ -0,0 +1,56 @@ +# 0017 - Un troisième environnement, `dev`, déployé à la demande depuis n'importe quelle branche + +- Statut : accepté +- Date : 2026-09-23 + +## Contexte + +L'[ADR 0009](0009-deux-environnements-compose-sur-la-vm-eni.md) a posé deux environnements sur +la VM ENI : la recette suit `dev`, la production suit `main`. Les environnements GitHub en +comptent trois, `dev`, `rec` et `prod`, et le troisième ne déployait rien. + +Il manque un endroit où montrer une branche de travail avant son merge : la recette ne doit +porter que ce qui est intégré à `dev`, sinon elle cesse d'être une recette. Un +`workflow_dispatch` sur une branche de travail envoyait d'ailleurs cette branche dans la +recette, puisque tout ce qui n'était pas `main` y partait. + +La VM est passée à 32 Go : une troisième TimescaleDB, réglée à 2 Go comme les deux autres, +tient sans peine. + +## Décision + +**Un troisième projet Compose, `enervision-dev`, dans `/srv/enervision/dev`**, bâti exactement +comme les deux autres : son clone, son `.env`, son certificat, préparés par +`scripts/provision-host.sh`. + +**Déployé à la demande, jamais sur un push.** `deploy.yml` envoie `main` en prod, `dev` en +recette, et toute autre branche lancée depuis l'onglet Actions dans `dev`. Seul un membre ayant +le droit d'écriture sur le dépôt peut lancer un workflow. + +**Ports décalés d'un cran de plus** : HTTPS `9443`, et sur `127.0.0.1` la redirection HTTP +`8083`, PostgreSQL `5435`, Mailpit `8027`, Airflow `8084`. Nom d'hôte `dev.enervision.local`, +pour la même raison de cookie que la recette. + +**Le groupe de concurrence suit l'environnement**, et non plus la branche : deux branches lancées +coup sur coup écriraient sinon dans le même dossier en même temps. + +## Alternatives écartées + +- **`dev` suit la branche `dev` à chaque push, la recette devient manuelle** : la recette + offrirait une version figée au jury, mais la doc CI/CD, l'ADR 0009 et l'habitude de l'équipe + basculeraient à deux jours du rendu. +- **Un environnement par branche de travail** : un projet Compose et une TimescaleDB par + branche, sans mécanisme de nettoyage. La machine ne le porterait pas longtemps. +- **Garder `dev` sur les postes seulement** : rien à montrer d'une branche non mergée sans + passer par la recette. + +## Conséquences + +- Une branche de travail créée avant ce changement porte l'ancien `deploy.yml` : lancée à la + main, elle part encore dans la recette. Limiter l'environnement GitHub `rec` à la branche + `dev` ferme ce chemin, réglage que seul un administrateur du dépôt peut poser. +- `dev` ne garde aucune donnée d'une branche à l'autre au-delà de ce que ses migrations + acceptent : une branche dont les migrations divergent de `dev` peut laisser la base dans un + état que la suivante refuse. Recréer le volume, `docker compose down -v`, est alors le remède. +- Trois environnements construisent leurs images séparément : l'écart de l'ADR 0009, un même + commit construit deux fois, reste ouvert jusqu'au passage à GHCR. diff --git a/docs/architecture/10-infra.md b/docs/architecture/10-infra.md index 1fbabda..3e912da 100644 --- a/docs/architecture/10-infra.md +++ b/docs/architecture/10-infra.md @@ -227,26 +227,27 @@ Deux conséquences se propagent jusqu'à l'application, et elles ne se devinent - `APP_TRUST_PROXY_HEADERS` passe à vrai en même temps, sinon la limitation de débit par IP compte sur l'IP du proxy et devient globale. -### Deux environnements sur la même machine +### Trois environnements sur la même machine -Statut : `En cours`, la machine n'étant pas encore provisionnée. Décision et motifs dans -l'[ADR 0009](../adr/0009-deux-environnements-compose-sur-la-vm-eni.md). -La VM `eadl-2025-nantes-g3` portera la recette et la production, chacune dans son clone du dépôt, -son `.env` et son projet Compose. Le nom de projet préfixe volumes, réseau et conteneurs : rien -n'est partagé. `scripts/provision-host.sh` prépare les deux dossiers, génère les secrets et les -certificats, et ne démarre rien. +Statut : `En cours`. Décision et motifs dans +l'[ADR 0009](../adr/0009-deux-environnements-compose-sur-la-vm-eni.md), étendue à un troisième +environnement par l'[ADR 0017](../adr/0017-environnement-dev-a-la-demande.md). +La VM `eadl-2025-nantes-g3` porte le développement, la recette et la production, chacun dans son +clone du dépôt, son `.env` et son projet Compose. Le nom de projet préfixe volumes, réseau et +conteneurs : rien n'est partagé. `scripts/provision-host.sh` prépare les trois dossiers, génère +les secrets et les certificats, et ne démarre rien. -| | Recette | Production | -|---|---|---| -| Branche, environnement GitHub | `dev`, `rec` | `main`, `prod` | -| Dossier, projet Compose | `/srv/enervision/rec`, `enervision-rec` | `/srv/enervision/prod`, `enervision-prod` | -| URL | `https://rec.enervision.local:8443` | `https://enervision.local` | -| Proxy HTTP, HTTPS | `127.0.0.1:8081`, `8443` | `80`, `443` | -| PostgreSQL, Mailpit, Airflow, sur `127.0.0.1` | `5434`, `8026`, `8082` | `5433`, `8025`, `8080` | +| | Développement | Recette | Production | +|---|---|---|---| +| Branche, environnement GitHub | toute branche lancée à la main, `dev` | `dev`, `rec` | `main`, `prod` | +| Dossier, projet Compose | `/srv/enervision/dev`, `enervision-dev` | `/srv/enervision/rec`, `enervision-rec` | `/srv/enervision/prod`, `enervision-prod` | +| URL | `https://dev.enervision.local:9443` | `https://rec.enervision.local:8443` | `https://enervision.local` | +| Proxy HTTP, HTTPS | `127.0.0.1:8083`, `9443` | `127.0.0.1:8081`, `8443` | `80`, `443` | +| PostgreSQL, Mailpit, Airflow, sur `127.0.0.1` | `5435`, `8027`, `8084` | `5434`, `8026`, `8082` | `5433`, `8025`, `8080` | -Les deux noms d'hôte visent la même IP, à déclarer dans le `/etc/hosts` des postes. Deux noms +Les trois noms d'hôte visent la même IP, à déclarer dans le `/etc/hosts` des postes. Deux noms distincts sont nécessaires : le cookie `__Secure-ev_refresh` est posé par hôte, pas par port. -La redirection HTTP de la recette est ramenée sur la boucle locale parce que la configuration +La redirection HTTP de la recette et du développement est ramenée sur la boucle locale parce que la configuration Nginx renvoie vers `https://$host` sans port, c'est-à-dire vers la production. Le déploiement est décrit dans [50-cicd.md](50-cicd.md) : un runner GitHub Actions installé sur @@ -267,7 +268,7 @@ sequenceDiagram TF->>VM: SSH, get.docker.com puis docker compose version TF->>VM: copie et exécute scripts/provision-host.sh - VM->>VM: deux clones, deux .env, deux certificats + VM->>VM: trois clones, trois .env, trois certificats TF->>VM: installe actions-runner, config.sh, svc.sh VM->>GH: le runner s'enregistre avec le label eni-g3 ``` diff --git a/docs/architecture/50-cicd.md b/docs/architecture/50-cicd.md index 9a80102..2cd4f7a 100644 --- a/docs/architecture/50-cicd.md +++ b/docs/architecture/50-cicd.md @@ -131,13 +131,14 @@ entrant n'est ouvert. |---|---|---|---| | `push` sur `dev` | `rec` | `/srv/enervision/rec` | aucune : la recette suit `dev` | | `push` sur `main` | `prod` | `/srv/enervision/prod` | approbation d'un relecteur dans l'environnement `prod`, branche `main` seule autorisée | +| `workflow_dispatch` sur toute autre branche | `dev` | `/srv/enervision/dev` | droit d'écriture sur le dépôt, seul à pouvoir lancer un workflow ([ADR 0017](../adr/0017-environnement-dev-a-la-demande.md)) | Le job aligne le clone sur la branche (`fetch`, `checkout`, `reset --hard`), lance `make stack-up`, qui reconstruit les images, redémarre les conteneurs puis applique les migrations Alembic dans le conteneur backend, et attend jusqu'à trois minutes que `/api/v1/health/ready` réponde derrière le proxy. Cette sonde ne vérifie que la connexion à la base et la présence de TimescaleDB : sans la migration, le déploiement serait vert sur une base -sans schéma, et c'est pourquoi `make stack-up` la porte. Un groupe de concurrence par branche, +sans schéma, et c'est pourquoi `make stack-up` la porte. Un groupe de concurrence par environnement, sans annulation, empêche deux déploiements simultanés du même environnement. Le job ne fait pas de `actions/checkout` dans son espace de travail, et c'est voulu : le dossier @@ -157,7 +158,7 @@ passé à `scripts/provision-host.sh` fixe ce propriétaire. La machine se prépare avec `scripts/provision-host.sh`, qui vérifie Docker et Compose 2.24.4 ou plus, clone les deux branches, génère les secrets de chaque `.env` et les certificats -auto-signés, et ne démarre rien. Le détail des deux environnements, ports et noms d'hôte, est +auto-signés, et ne démarre rien. Le détail des trois environnements, ports et noms d'hôte, est dans [10-infra.md](10-infra.md). ## Ce qui bloque un merge diff --git a/infra/README.md b/infra/README.md index 5a4776e..da74e09 100644 --- a/infra/README.md +++ b/infra/README.md @@ -8,8 +8,9 @@ Rien ici ne construit d'image ni ne lance de conteneur. - `k3s` : installe un cluster k3s single-node sur une machine distante via SSH (script officiel `get.k3s.io`) et rapatrie le kubeconfig en local. - `terraform/environments/` : une racine par machine provisionnee. - - `vm-eni` : la VM `eadl-2025-nantes-g3`, qui porte les environnements `rec` et `prod` - ([ADR 0009](../docs/adr/0009-deux-environnements-compose-sur-la-vm-eni.md)). Installe Docker, + - `vm-eni` : la VM `eadl-2025-nantes-g3`, qui porte les environnements `dev`, `rec` et `prod` + ([ADR 0009](../docs/adr/0009-deux-environnements-compose-sur-la-vm-eni.md), + [ADR 0017](../docs/adr/0017-environnement-dev-a-la-demande.md)). Installe Docker, execute `scripts/provision-host.sh`, enregistre le runner GitHub Actions. - `k3s-cible` : le cluster k3s, cible a terme de `docs/architecture/10-infra.md`. Jamais applique. @@ -32,9 +33,10 @@ terraform apply Parametres du depot, Actions, Runners, New self-hosted runner. Seul un administrateur du depot peut le creer. -Apres l'apply, la machine porte `/srv/enervision/rec` et `/srv/enervision/prod`, chacun avec son -`.env` et son certificat. Le premier demarrage reste manuel, `make stack-up` dans chaque dossier ; -les suivants sont joues par le runner a chaque push sur `dev` et sur `main`. +Apres l'apply, la machine porte `/srv/enervision/dev`, `/srv/enervision/rec` et +`/srv/enervision/prod`, chacun avec son `.env` et son certificat. Le premier demarrage reste +manuel, `make stack-up` dans chaque dossier ; les suivants sont joues par le runner a chaque push +sur `dev` et sur `main`, et a chaque lancement manuel d'une autre branche pour `dev`. Retirer le runner se fait a la main, depuis les parametres du depot : `terraform destroy` ne le desinscrit pas. diff --git a/infra/terraform/environments/vm-eni/main.tf b/infra/terraform/environments/vm-eni/main.tf index 44a9241..2641282 100644 --- a/infra/terraform/environments/vm-eni/main.tf +++ b/infra/terraform/environments/vm-eni/main.tf @@ -6,7 +6,7 @@ # Contrainte : pas de provisioner `destroy` sur le runner. Il imposerait une connexion ne lisant # que `self`, donc le chemin de la cle SSH dans le state, et `svc.sh uninstall` ne desinscrit pas # le runner cote GitHub : le retrait reste manuel, depuis les parametres du depot. -# Ref : ADR 0009 pour les deux environnements, `scripts/provision-host.sh` pour leur contenu. +# Ref : ADR 0009 et 0017 pour les trois environnements, `scripts/provision-host.sh` pour leur contenu. locals { sudo = var.ssh_user == "root" ? "" : "sudo " diff --git a/infra/terraform/environments/vm-eni/variables.tf b/infra/terraform/environments/vm-eni/variables.tf index 29be228..9608717 100644 --- a/infra/terraform/environments/vm-eni/variables.tf +++ b/infra/terraform/environments/vm-eni/variables.tf @@ -1,6 +1,6 @@ variable "ssh_host" { type = string - description = "Adresse de la VM ENI qui porte les deux environnements (ADR 0009)." + description = "Adresse de la VM ENI qui porte les trois environnements (ADR 0009, ADR 0017)." } variable "ssh_port" { diff --git a/scripts/provision-host.sh b/scripts/provision-host.sh index 6bad530..21262a5 100755 --- a/scripts/provision-host.sh +++ b/scripts/provision-host.sh @@ -1,7 +1,9 @@ #!/usr/bin/env bash -# Pourquoi : la machine porte deux environnements, chacun un clone du dépôt, un `.env` et un -# projet Compose (ADR 0009). Ce script prépare la machine et les deux dossiers sans rien -# démarrer : construction des images et démarrage restent à l'opérateur, puis au runner GitHub. +# Pourquoi : la machine porte trois environnements, chacun un clone du dépôt, un `.env` et un +# projet Compose (ADR 0009, ADR 0017). Ce script prépare la machine et les trois dossiers sans +# rien démarrer : construction des images et démarrage restent à l'opérateur, puis au runner. +# Piège : lancé en root, git refuse un clone déjà chowné au runner (propriété douteuse). D'où +# `safe.directory` passé en ligne de commande, seule portée où git l'accepte - preparer(). # Rejouable : un dossier déjà cloné est réaligné sur sa branche, un `.env` existant n'est jamais # réécrit, un certificat présent n'est jamais régénéré. @@ -39,9 +41,10 @@ preparer() { local dossier="$RACINE/$env" if [[ -d "$dossier/.git" ]]; then - git -C "$dossier" fetch --quiet origin "$branche" - git -C "$dossier" checkout --quiet "$branche" - git -C "$dossier" reset --quiet --hard "origin/$branche" + local git=(git -c "safe.directory=$dossier" -C "$dossier") + "${git[@]}" fetch --quiet origin "$branche" + "${git[@]}" checkout --quiet "$branche" + "${git[@]}" reset --quiet --hard "origin/$branche" else git clone --quiet --branch "$branche" "$DEPOT" "$dossier" fi @@ -93,6 +96,7 @@ mkdir -p "$RACINE" # env branche hôte origine https http pg mailpit airflow preparer prod main enervision.local https://enervision.local 443 80 5433 8025 8080 preparer rec dev rec.enervision.local https://rec.enervision.local:8443 8443 127.0.0.1:8081 5434 8026 8082 +preparer dev dev dev.enervision.local https://dev.enervision.local:9443 9443 127.0.0.1:8083 5435 8027 8084 if [[ -n "$PROPRIETAIRE" && "$(id -u)" -eq 0 ]]; then chown -R "$PROPRIETAIRE" "$RACINE" @@ -104,10 +108,12 @@ Démarrage, dans chaque dossier : make stack-up, qui applique aussi les migratio Premier administrateur, stack démarrée, dans chaque dossier : docker compose -f docker-compose.yml -f docker-compose.prod.yml exec backend \\ python -m app.cli create-admin --email -Le runner GitHub Actions (label eni-g3) rejouera le déploiement à chaque push sur dev et main. +Le runner GitHub Actions (label eni-g3) rejouera le déploiement à chaque push sur dev et main, +et déploiera dans dev toute autre branche lancée à la main depuis l'onglet Actions. L'installer sous le propriétaire de $RACINE, sinon git refuse ces dépôts et le .env en 600 lui échappe : relancer au besoin ce script avec PROPRIETAIRE=. Données historiques : git ne porte pas data/raw, déposer les fichiers dans chaque dossier avant de déclencher le DAG historical_import. -Depuis un poste : ajouter « $ADRESSE enervision.local rec.enervision.local » à /etc/hosts. +Depuis un poste : ajouter « $ADRESSE enervision.local rec.enervision.local dev.enervision.local » +à /etc/hosts. FIN