De git push à CloudFront, sans aucune clé AWS
Un portfolio statique coûte quelques centimes à héberger. Le déployer avec la discipline d’une production coûte à peine plus : infrastructure en Terraform, chaîne d’intégration gatée, identités fédérées, budget surveillé. C’est précisément ce que ce site veut montrer. Voici comment il arrive jusqu’à vous.
TerraformGitHub ActionsS3 · CloudFrontIAM
Le problème à résoudre
Héberger un site statique sur AWS peut être fait en 10 minutes sur la console manuellement. Le faire proprement, sans bucket public, sans clé d’accès longue durée, sans écart entre ce qui est écrit et ce qui tourne, demande un peu plus : une infrastructure entièrement écrite en IaC, une CI/CD qui ne peut agir que dans un périmètre étroit, et des garde-fous sur le coût.
Ce site est le terrain d’application. Il n’a rien de critique, et c’est voulu : il permet de montrer des pratiques d’exploitation sur un système assez petit pour être lu de bout en bout.
Deux dépôts, deux responsabilités
Le code du site et l’infrastructure vivent dans deux dépôts distincts. Le premier produit un artefact, le build Vite, et le publie ; le second, en Terraform, provisionne tout ce qui le reçoit : bucket S3, distribution CloudFront, certificat TLS, enregistrements DNS, rôles IAM et budget. Aucun des deux ne dispose des droits pour faire le travail de l’autre.
Une chaîne de livraison sans clé
La chaîne d’intégration ne détient aucune clé AWS. À chaque push sur main, GitHub Actions lint, teste, vérifie les types et build le site, puis présente à AWS un token OIDC (OpenID Connect) signé par GitHub. AWS l’échange contre des identifiants temporaires pour un rôle IAM dont la politique de confiance n’accepte que ce dépôt et cette branche. Ces identifiants expirent avec le job : il n’y a rien à faire tourner et rien à faire fuiter.
Ce rôle sait faire trois choses : lister le bucket du site, le synchroniser, et invalider le cache CloudFront. Il ne peut ni modifier l’infrastructure ni lire les autres buckets du compte. Le dépôt d’infrastructure et le dépôt RAG ont chacun le leur : un commit malveillant sur l’un ne peut pas réécrire les artefacts de l’autre.
Cette règle a été éprouvée. Quand l’application RAG a été extraite vers son propre dépôt, aucune identité fédérée ne le couvrait encore : son bucket est resté vide et son sous-domaine a répondu 403. Le diagnostic a conduit à créer un troisième rôle plutôt qu’à élargir un rôle existant : l’alternative, plus rapide, aurait donné à chaque dépôt l’accès aux artefacts de l’autre. La décision est consignée dans un ADR (Architecture Decision Record).
- C’est l’artefact validé par la CI qui est déployé, pas un second build : ce qui a été testé est ce qui part en production.
- Les assets portent un hash dans leur nom : ils sont servis avec un cache d’un an et marqués immuables, sans invalidation à chaque release.
- Les pages HTML sont poussées en dernier, en no-cache : elles ne référencent de nouveaux hashs qu’une fois ceux-ci en ligne, et chaque déploiement est visible immédiatement.
Le Terraform passe aussi par la CI
Chaque pull request sur le dépôt d’infrastructure déclenche cinq contrôles avant même de parler à AWS : terraform fmt, terraform validate, tflint, puis deux analyses de sécurité, trivy et checkov, qui refusent un bucket sans chiffrement ou une politique trop permissive. Le plan n’est calculé qu’ensuite ; l’apply se fait au merge sur main, sous un rôle dédié, un seul à la fois.
Ce rôle d’apply est nécessairement large, puisqu’il gère IAM, CloudFront ou Cognito, mais il est borné : il ne peut créer ou modifier que des rôles préfixés par le nom du projet, et sa politique de confiance n’accepte que ce dépôt. Le compromis, et ce qu’il laisse ouvert, est écrit dans un ADR (Architecture Decision Record).
Rien de public, tout chiffré
- Le bucket est privé : blocage total de l’accès public, ACL désactivées, chiffrement côté serveur, versioning, et une politique qui refuse toute requête non chiffrée.
- Seule la distribution CloudFront peut le lire, via un Origin Access Control signé ; le bucket ne répond jamais en direct.
- CloudFront impose TLS 1.2 au minimum, redirige HTTP vers HTTPS et ajoute les en-têtes de sécurité : HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy.
- Le certificat ACM est validé par DNS et renouvelé sans coupure ; Route 53 pointe sur la distribution en IPv4 et en IPv6.
- Pas de WAF : à cinq dollars par mois, il coûterait plus que tout le reste. Le choix est consigné, à revoir si le site devient critique.
Des modules par intention, pas par ressource
Le Terraform est découpé en modules composites (ci-oidc, static-site, serverless-api) qui correspondent chacun à une boîte du schéma d’architecture, plus une seule primitive, secure-bucket, extraite parce qu’elle a réellement plusieurs appelants. Pas de wrapper autour de chaque ressource, pas de module communautaire : l’IAM et l’accès à l’origine sont écrits à la main et se lisent ligne à ligne, ce qui est précisément l’objet du portfolio.
Le coût, tenu
Le budget AWS du compte est plafonné à 10 $ par mois, avec une alerte à 80 %. Le site lui-même se chiffre en centimes : S3 et CloudFront facturent à l’usage, la distribution est limitée aux points de présence d’Amérique du Nord et d’Europe, et le premier poste fixe est la zone DNS Route 53, 0,50 $ par mois. Le coût réel, relevé sur la facture, sera publié ici.
Ce qui manque encore
- Une approbation manuelle avant l’apply Terraform : l’environnement GitHub existe, la règle de protection reste à activer.
- infracost sur chaque pull request, pour lire le coût d’un changement avant de le merger.
- Le plan Terraform posté en commentaire de la pull request.