1. Le Constat et les Objectifs Architecturaux
Dans le cadre de l’évolution de mes compétences en administration système et réseau, j’ai entrepris la conception d’une infrastructure d’hébergement “Enterprise Grade” sur un serveur physique local (On-Premise).
L’objectif initial était de dépasser la simple conteneurisation pour construire une architecture hautement résiliente, respectant les standards stricts de l’industrie :
- Sécurité absolue (Zero Trust) : Refus catégorique de l’ouverture des ports sur le routeur de bordure (NAT/PAT) afin d’éliminer le risque d’attaques par force brute ou de balayage de ports (scanning).
- Automatisation (IaC) : Toute l’infrastructure doit être décrite sous forme de code et reproductible.
- Immutabilité (CI/CD) : Le code applicatif ne doit jamais être monté via des volumes locaux, mais scellé au sein d’artefacts (images Docker) versionnés et distribués via un registre privé.
- Routage Dynamique (Layer 7) : L’exposition des services doit être gérée à la volée par un proxy inverse intelligent.
2. L’Infrastructure de Base : Réseau SDN (Software Defined Network)
La première étape de la sécurisation consiste à isoler les flux réseau. Au lieu d’utiliser le réseau bridge par défaut de Docker, un réseau virtuel dédié a été créé. Ce SDN permet à nos futurs conteneurs de communiquer entre eux via une résolution DNS interne stricte, tout en restant invisibles depuis l’extérieur.
# Création du sous-réseau isolé pour le reverse proxy
docker network create proxy_net
3. Le Cœur du Routage : Déploiement de Traefik
Pour aiguiller le trafic entrant vers les bons conteneurs applicatifs (le registre, le site web, etc.), j’ai déployé Traefik, un reverse proxy et load-balancer cloud-native.
L’avantage majeur de Traefik réside dans sa capacité à écouter l’API de Docker. Il détecte automatiquement l’apparition de nouveaux conteneurs (via leurs labels) et reconfigure ses routes (Layer 7) en temps réel, sans nécessiter de redémarrage.
Voici le manifeste docker-compose.yml définissant le routeur principal, configuré pour accepter le trafic HTTP pur depuis le tunnel de sécurité (que nous déploierons plus tard).
services:
traefik:
image: traefik:latest
container_name: traefik_proxy
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy_net
ports:
- "80:80"
- "443:443"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./dynamic_conf:/dynamic_conf:ro
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.file.directory=/dynamic_conf"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--api.dashboard=true"
- "--api.insecure=true"
- "--serverstransport.insecureskipverify=true"
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`traefik.local`)"
- "traefik.http.routers.api.service=api@internal"
- "traefik.http.routers.api.entrypoints=web"
networks:
proxy_net:
external: true

4. Le Pipeline CI/CD : Déploiement d’un Registre Privé
Dans une approche DevOps stricte, la compilation du code et son exécution sont deux étapes distinctes. Pour éviter de dépendre du Docker Hub public ou de compiler du code en direct sur le serveur de production, l’architecture intègre son propre Registre Docker Privé.
L’objectif est d’avoir un coffre-fort local où sont stockées de manière chiffrée les images immuables (Artefacts) générées par les pipelines CI. C’est le conteneur final (Nginx) qui ira tirer (Pull) cette image depuis ce registre.
Pour autoriser les transferts sécurisés entre le moteur Docker (hôte) et ce registre, un ensemble de certificats cryptographiques locaux a été généré via mkcert et injecté dans le dossier d’approbation système /etc/docker/certs.d/.
Voici le manifeste docker-compose.yml du registre, routé par Traefik sur un domaine interne virtuel :
services:
registry:
image: registry:2
container_name: docker_registry
restart: unless-stopped
networks:
- proxy_net
security_opt:
- no-new-privileges:true
volumes:
- ./registry-data:/var/lib/registry
- ./auth:/auth
# Configuration TLS
- /opt/docker/certs:/certs:ro
environment:
- REGISTRY_AUTH=htpasswd
- REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm
- REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
- REGISTRY_HTTP_ADDR=0.0.0.0:5000
# Force le registre à générer des URL de redirection HTTPS pour les blobs
- REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.local.pem
- REGISTRY_HTTP_TLS_KEY=/certs/registry.local-key.pem
labels:
- "traefik.enable=true"
# Accès exclusif sur le domaine virtuel local
- "traefik.http.routers.registry.rule=Host(`registry.local`)"
- "traefik.http.routers.registry.entrypoints=websecure"
# Délégation de la terminaison TLS au registre (Passthough)
- "traefik.tcp.routers.registry.rule=HostSNI(`registry.local`)"
- "traefik.tcp.routers.registry.tls.passthrough=true"
- "traefik.tcp.services.registry.loadbalancer.server.port=5000"
networks:
proxy_net:
external: true
Mécanique de l’Artefact (Build & Push)
Grâce à ce composant critique, le workflow de déploiement d’une nouvelle version applicative (ex: v2.3) se résume à une séquence rigoureuse sans aucune altération de la production en cours :
# 1. Compilation de l'image (Scellement du code source dans Nginx)
docker build -t registry.local/portfolio_web:v2.3 .
# 2. Transfert sécurisé de la nouvelle couche vers le registre local
docker push registry.local/portfolio_web:v2.3

5. L’Exposition Sécurisée : Cloudflare Tunnels (Zero Trust)
Exposer un serveur local directement sur Internet via une redirection de ports (NAT/PAT) sur un routeur expose l’infrastructure à des attaques par balayage (scanning) et par force brute. Lors des premiers tests d’exposition sur IP publique, la couche réseau physique a montré des signes de saturation (lags SSH, perte de paquets) liés au bruit de fond d’Internet.
Pour pallier cette vulnérabilité, le paradigme Zero Trust a été implémenté via Cloudflare Tunnels.
Le principe : Aucun port entrant n’est ouvert sur le pare-feu. Un démon local (cloudflared) initie une connexion sortante vers le réseau mondial de Cloudflare (Edge) via le protocole QUIC (UDP). Le trafic des visiteurs est chiffré par Cloudflare, envoyé dans ce tunnel, puis déchargé en HTTP pur vers Traefik. Le serveur devient littéralement invisible sur Internet.
Voici le manifeste docker-compose.yml du connecteur, placé sur le même réseau proxy_net pour qu’il puisse résoudre le nom de Traefik :
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared_tunnel
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy_net
environment:
# Injection propre du secret de liaison via variable d'environnement
- TUNNEL_TOKEN=VOTRE_TOKEN_CLOUDFLARE
command: tunnel --no-autoupdate run
networks:
proxy_net:
external: true

6. Hébergement Final : L’Artefact Immuable (Hugo + Nginx)
Le portfolio est rédigé en Markdown et compilé via Hugo, un générateur de site statique ultra-rapide. Plutôt que de monter les fichiers HTML générés via un volume (Bind Mount) - ce qui briserait l’immutabilité du déploiement - les fichiers sont scellés directement à l’intérieur d’une image Nginx.
Voici la recette (Dockerfile) utilisée pour construire l’artefact :
FROM nginx:alpine
# Suppression de la page par défaut de Nginx
RUN rm -rf /usr/share/nginx/html/*
# Copie du site généré par Hugo à l'intérieur de l'image Nginx
COPY ./public /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Une fois compilée et poussée dans le registre privé (via l’étape 4), l’application est instanciée via son manifeste de production. Traefik lit les labels dynamiquement et route le trafic issu du tunnel vers ce conteneur Nginx.
services:
web:
# L'image est tirée du registre privé local
image: registry.local/portfolio_web:v2.3
container_name: portfolio_web
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy_net
labels:
- "traefik.enable=true"
# Routage dynamique basé sur le domaine public intercepté par Cloudflare
- "traefik.http.routers.portfolio.rule=Host(`aghilesportfolio.com`)"
- "traefik.http.routers.portfolio.entrypoints=web"
- "traefik.http.services.portfolio.loadbalancer.server.port=80"
networks:
proxy_net:
external: true

Conclusion
Cette infrastructure valide la mise en œuvre d’une architecture robuste, Enterprise-Grade. Le couplage du SDN de Docker, du routage Layer 7 de Traefik et des tunnels Cloudflare permet d’obtenir :
- Une sécurité maximale (Aucun port exposé, IP publique masquée, terminaison SSL gérée par un CDN mondial).
- Une agilité de déploiement digne d’une chaîne CI/CD (Build/Tag/Push locaux).
- Une capacité de reprise sur sinistre immédiate : en cas de régression, il suffit de modifier le tag de l’image dans le fichier compose (ex: passage de
v2.3àv2.2) et de redéployer pour effectuer un rollback instantané.