Tous les projets

Indian Food : pipeline DevSecOps pour un site vitrine

Site vitrine statique d'un restaurant, déployé sur Oracle Cloud ARM64 derrière une chaîne DevSecOps : analyse statique, scan de dépendances, tests dynamiques et déploiement automatisé.

L'objectif
Montrer qu'une chaîne DevSecOps complète s'applique même au plus petit projet : un simple site vitrine.
Ma démarche
Pipeline GitHub Actions : Gitleaks et Trivy bloquants, SAST Semgrep, build ARM64, déploiement, puis scan dynamique OWASP ZAP.
Le résultat
Site déployé derrière des portes de sécurité, en-têtes HTTP corrigés, analyse de risques STRIDE.

Contexte

C'est un projet pédagogique. Le but était de montrer que les pratiques DevSecOps s'appliquent aussi à une petite application. J'ai pris le cas le plus simple possible, un site vitrine statique pour un restaurant de cuisine indienne, et je lui ai appliqué une chaîne de sécurité du commit jusqu'au déploiement en production.

Le contenu est volontairement trivial. Ce qui est mis en avant, c'est l'infrastructure et le pipeline.

Aperçu de la landing page Indian Food en production
Ouvrir
indian.sinoro.fr, cliquer pour ouvrir le site en ligne

Objectifs

  • Déployer un site en production avec HTTPS sur un serveur personnel (Oracle Cloud Free Tier)
  • Implémenter une chaîne CI/CD avec des security gates bloquants
  • Couvrir les quatre vecteurs d'analyse : secrets, code statique, dépendances, tests dynamiques
  • Comprendre le Cyber Resilience Act (CRA) en pratique, pas seulement lire le texte, mais traduire ses exigences en livrables concrets sur un vrai projet déployé

Stack technique

CoucheChoixRaison
SiteHTML / CSS / Vanilla JSZéro dépendance, surface d'attaque minimale
Conteneurnginx:alpineImage légère (~7 Mo), durcissement possible
OrchestrationDocker ComposeSimple, suffisant pour un seul service
RegistryGitHub Container Registry (GHCR)Intégration native avec GitHub Actions, gratuit
CI/CDGitHub ActionsÉcosystème riche en actions de sécurité
InfraOracle Cloud ARM64 (Ampere A1)Free Tier permanent, architecture ARM64
DNSSous-domaine sinoro.frEnregistrement A statique vers le VPS
SSLLet's Encrypt (via Traefik)Certificat automatisé, renouvellement géré par le proxy
Reverse proxyTraefik v2.11Terminaison TLS + routing par labels Docker

Architecture de déploiement

Pipeline DevSecOps : portes de sécurité, build, déploiement, puis scan OWASP ZAP
Pipeline DevSecOps : portes de sécurité, build, déploiement, puis scan OWASP ZAP
Chemin d'une requête, du navigateur au conteneur nginx sur le VPS
Chemin d'une requête, du navigateur au conteneur nginx sur le VPS

Pipeline DevSecOps détaillé

1. Gitleaks, Détection de secrets

Scan de l'ensemble du dépôt à la recherche de credentials oubliés (tokens, clés SSH, mots de passe). Le déploiement est bloqué si un secret est détecté.

2. Semgrep, SAST (Static Application Security Testing)

Analyse statique du code source (public/) pour détecter des patterns dangereux : injections, mauvaise gestion des entrées, fonctions obsolètes. Les règles auto couvrent HTML, CSS et JavaScript. Semgrep est lancé sans --error : il produit un rapport mais ne bloque pas le déploiement, contrairement à Gitleaks et Trivy.

3. Trivy, SCA

Scan du système de fichiers pour identifier les CVE connues sur les composants utilisés. Le job échoue sur toute CVE critique corrigeable (severity: CRITICAL, exit-code: 1, ignore-unfixed).

En complément, un Software Bill of Materials (SBOM) au format CycloneDX 1.6 a été généré avec Trivy, en dehors du pipeline, ainsi qu'un rapport SCA. C'est l'inventaire des composants logiciels que le CRA exige pour la traçabilité.

4. Build ARM64

L'image Docker est compilée pour linux/arm64 via QEMU + Docker Buildx, adapté à l'architecture Ampere d'Oracle Cloud. Le build multi-plateforme sur runner ubuntu-latest (x86_64) est transparent grâce à l'émulation.

5. Déploiement via runner auto-hébergé

Le job de déploiement s'exécute sur un runner GitHub Actions auto-hébergé, installé directement sur le VPS : il tire la nouvelle image depuis GHCR et relance le container avec docker compose up -d --remove-orphans, puis nettoie les anciennes images. Aucune clé SSH n'est stockée dans GitHub, et seule l'image Docker circule.

6. OWASP ZAP, DAST (Dynamic Application Security Testing)

Scan dynamique du site en production après déploiement. ZAP simule un attaquant en testant les en-têtes HTTP, les redirections, les éléments HTML contrôlables, les politiques de cache, etc. Le rapport HTML est archivé comme artifact du workflow.


Ce que ZAP a identifié, et corrigé

C'est ici que le DAST a eu le plus de valeur concrète. La première exécution de ZAP a remonté une série de WARN sur des en-têtes de sécurité absents ou mal configurés. Chacun a été corrigé directement dans la configuration Nginx (docker/nginx.conf), puis re-vérifié au scan suivant.

Vulnérabilité (ZAP)CorrectionRisque si non corrigé
HSTS absent [10035]Strict-Transport-Security: max-age=31536000SSL stripping : un attaquant en MITM force la connexion en HTTP clair et intercepte/modifie le trafic
CSP absente [10038]Content-Security-Policy strict ajoutéAucune barrière contre l'injection de scripts/contenu : un XSS s'exécute librement dans le navigateur du visiteur
CSP style-src 'unsafe-inline' [10055]Styles inline externalisés en classe CSS, 'unsafe-inline' retiréUn CSP avec unsafe-inline n'arrête pas l'injection de styles malveillants (défacement, exfiltration de données via CSS)
CSP directives sans fallback [10055]Ajout de frame-ancestors, form-action, base-uri, object-srcClickjacking (page embarquée dans une iframe pirate), détournement de soumission de formulaire, injection d'objets
COOP / COEP / CORP manquants [90004]Cross-Origin-Opener-Policy, -Embedder-Policy, -Resource-PolicyAttaques cross-origin (fenêtres détournées, fuites mémoire type Spectre)
Permissions-Policy absente [10063]Header désactivant géoloc/micro/caméra/paiementUne page tierce embarquant le site pourrait solliciter des API sensibles du navigateur
Fuite de version serveur [10036] / banner [10009]server_tokens offLa version exacte de Nginx exposée facilite le ciblage d'exploits CVE connus

Le tri des faux positifs

Tout aussi formateur : ZAP a aussi remonté des alertes qui n'étaient pas des vulnérabilités dans ce contexte. Les écarter à la légère est une erreur ; les justifier l'est tout autant. J'ai documenté chaque exclusion dans .zap/rules.tsv :

  • Absence of Anti-CSRF Tokens [10202], le site est 100 % statique, aucun formulaire POST : pas de surface CSRF.
  • Storable and Cacheable Content [10049], la mise en cache est souhaitable pour un site statique, pas un défaut.
  • Modern Web Application [10109], alerte purement informative, aucune action requise.

Analyse de sécurité (méthode STRIDE)

RisqueScore initialMesuresRésiduel
Interception HTTP4HTTPS TLS 1.3 + HSTSFaible ✅
Accès SSH non autorisé12Clé ED25519 + Fail2ban + UFWFaible ✅
Défacement du site6Container éphémère + TrivyFaible ✅
Secrets dans le code8Gitleaks + GitHub SecretsFaible ✅
Déni de service6Fail2ban + cgroups DockerMoyen ⚠️
CVE dépendances6Trivy + Security GateFaible ✅
Attaques navigateur4Headers HTTP + OWASP ZAPFaible ✅

Le seul risque résiduel accepté est le déni de service : une protection complète nécessiterait un WAF ou un CDN (Cloudflare), hors scope pour ce projet.


Limites

  • Pas de WAF : le site est directement exposé sans filtrage applicatif avancé.
  • Nœud unique : pas de haute disponibilité. Si le VPS tombe, le site est indisponible.
  • DAST détectif, pas préventif : ZAP scanne après déploiement. Une vulnérabilité n'est donc visible qu'une fois le site déjà en ligne, un WAF en frontal comblerait ce délai.
  • Site statique pur : aucune interaction serveur, donc le périmètre de test de sécurité reste limité.

Évolutions envisagées

  • Cloudflare en frontal : proxy gratuit qui apporte DDoS protection, CDN et WAF basique, résoudrait le risque DoS.
  • Wazuh (SIEM) : monitorer les logs Nginx pour détecter en temps réel les tentatives d'attaque (SQLi, XSS, scans) et déclencher des alertes, complète le DAST par de la détection en continu.
  • Watchtower : mise à jour automatique de l'image Docker dès qu'une nouvelle version est publiée sur GHCR.
  • Notifications Slack/Discord : alertes en cas d'échec d'un security gate en CI.
  • Tests de charge (k6 ou Locust) : ajouter un job de performance au pipeline pour détecter les régressions.

Le CRA en pratique

Le Cyber Resilience Act est un règlement européen qui impose des exigences de cybersécurité aux produits numériques mis sur le marché. Première précision importante : ce site ne tombe pas dans le champ d'application du CRA. Le règlement cible les produits avec des éléments numériques (PDE) commercialisés, un site vitrine personnel n'est pas mis sur le marché au sens du règlement.

L'objectif n'était donc pas de "se conformer au CRA", mais de s'en servir comme cadre méthodologique pour apprendre à structurer une démarche de sécurité : qu'est-ce qu'une évaluation des risques formelle ? Comment documenter un SBOM ? Que met-on dans une déclaration de conformité ?

Exigence CRATraduction concrète dans ce projet
Évaluation des risquesAnalyse STRIDE sur 7 vecteurs, score et risque résiduel documentés
Gestion des vulnérabilitésTrivy SCA + Security Gate bloquant en CI
Traçabilité des composantsSBOM CycloneDX 1.6 généré avec Trivy (hors pipeline)
Tests de sécuritéSAST (Semgrep, non bloquant) + DAST (OWASP ZAP post-déploiement) dans le pipeline
Sécurité par conceptionSite 100% statique, container sans privilèges, surface d'attaque minimale
Documentation techniqueArchitecture, analyse des risques, déclaration de conformité

L'exercice a mis en évidence un écart entre la théorie et la pratique : le CRA parle d'"évaluation des risques" et de "mesures appropriées" de façon abstraite. Appliquer ça sur un projet réel, même hors scope réglementaire, force à prendre des décisions tranchées : quel risque on accepte, pourquoi, et comment on le documente. Le risque DoS accepté faute de WAF en est un exemple direct.


Ce que ce projet m'a appris

Appliquer DevSecOps à un site statique est intentionnellement disproportionné, et c'est le point. L'objectif était de maîtriser la mécanique du pipeline (ordering des jobs, needs, permissions, artifacts) et les outils (Gitleaks, Semgrep, Trivy, ZAP) dans un contexte où une erreur ne coûte rien.

Le vrai enseignement est venu d'un incident réel : un token GitHub commité par erreur, détecté par Gitleaks, qui a stoppé le pipeline avant la construction et la publication de l'image. Le pipeline a fonctionné exactement comme prévu, c'est plus formateur que n'importe quel cours.

Sur le CRA : comprendre un règlement en lisant ses articles est trompeur. C'est en cherchant à générer un SBOM conforme ou à documenter un risque résiduel accepté, même sur un projet hors scope, qu'on mesure ce que le texte exige vraiment des fabricants qui, eux, y sont soumis.

Projets proches

CybersécuritéDevSecOps

Mon budget : une application de prévision de solde

Application web qui répond à "combien j'aurai à telle date ?" et "quand est-ce que ça coince ?", avec un moteur de calcul testé, plusieurs budgets étanches et une authentification à double vérification. Déployée sur mon VPS ARM64.

2026Next.jsTypeScriptSQLiteargon2id

DevSecOpsCybersécurité

Umami : statistiques de visite auto-hébergées

Statistiques de visite sans cookie, auto-hébergées sur VPS ARM64. La collecte est publique, le dashboard n'est accessible que par le VPN WireGuard.

2026UmamiDockerTraefikWireGuard

CybersécuritéDevSecOps

Wazuh : un SIEM/XDR sur mon VPS

Wazuh installé sur mon VPS ARM64 pour voir comment un SIEM/XDR fonctionne sur une vraie machine : collecte de logs, détection d'intrusion et premier audit de conformité CIS.

2026WazuhSIEMXDRSCA