Projets
DevSecOpsCybersécurité

Indian Food, Landing page DevSecOps

Un site vitrine de restaurant déployé en production avec une chaîne DevSecOps complète, analyse statique, scan de dépendances, tests dynamiques et déploiement automatisé sur Oracle Cloud ARM64.

Par Sehenonirina Elisa Randriamasinoro, M1 Cybersécurité des Systèmes Embarqués, UBS Lorient

Code source202611 stack
[DEVSECOPS][DOCKER][NGINX][CI/CD][OWASP][GITHUB ACTIONS][TRIVY][SEMGREP][GITLEAKS][SBOM][CYCLONEDX]

Contexte

Ce projet est né d'une contrainte pédagogique claire : prouver que les pratiques DevSecOps ne sont pas réservées aux grosses applications. L'idée était de prendre le cas le plus simple qui soit, un site vitrine statique pour un restaurant de cuisine indienne, et d'y appliquer une chaîne de sécurité complète, du commit 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

git push
    │
    ▼
GitHub Actions ──► Gitleaks ──► Semgrep ──► Trivy
                                              │
                                   (security gates)
                                              │ tout passe
                                              ▼
                               Build image linux/arm64
                               Push → ghcr.io/randriamasinoro/resto-site
                                              │
                                              ▼
                               SSH → Oracle Cloud VPS
                               docker compose up -d
                                              │
                                              ▼
                               OWASP ZAP (DAST post-déploiement)
Navigateur
    │ https://indian.sinoro.fr
    ▼
DNS sinoro.fr → IP Oracle Cloud
    │ port 443
    ▼
Traefik  ← TLS terminé ici (Let's Encrypt)
    │ réseau Docker interne
    ▼
Docker container (nginx:alpine + fichiers statiques)

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.

3. Trivy, SCA + SBOM

Scan des dépendances du système de fichiers pour identifier les CVE connues sur les packages utilisés. Génère un Software Bill of Materials (SBOM) au format CycloneDX, inventaire complet des composants logiciels, exigé par le CRA 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. Deploy via SSH

Le runner GitHub Actions SSH sur le VPS, tire la nouvelle image depuis GHCR et relance le container avec docker compose up -d --remove-orphans. Aucun artifact n'est transféré : 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 généré automatiquement à chaque build
Tests de sécuritéSAST (Semgrep) + DAST (OWASP ZAP) 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, bloqué par Gitleaks avant d'atteindre le dépôt public. 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 similaires

Tous les projets
DevSecOpsCybersécurité

Umami, Analytics self-hosted, dashboard VPN-only

Web analytics sans cookie auto-hébergé sur VPS ARM64, avec une architecture réseau qui sépare la collecte publique du dashboard, ce dernier étant accessible uniquement via VPN WireGuard.

2026
CybersécuritéDevSecOps

Wazuh SIEM/XDR, surveillance de sécurité sur VPS personnel

Déploiement de Wazuh sur VPS ARM64 pour comprendre comment fonctionne un SIEM/XDR en conditions réelles. Collecte de logs, détection d'intrusion et premier regard sur l'audit de conformité.

2026
CybersécuritéDevSecOps

VPN WireGuard, accès distant chiffré & frontière de confiance

Un VPN WireGuard auto-hébergé sur VPS ARM64 qui transforme un serveur isolé en réseau privé multi-appareils, et sert de frontière de confiance pour exposer des services d'administration en interne uniquement.

2026