Ma Stack VPS & Architecture Zero Trust
/ 11 min de lecture
Table des matières
Gérer sa propre infrastructure en auto-hébergement (self-hosting) implique souvent un compromis difficile entre commodité, autonomie et sécurité. Dans le modèle traditionnel, multiplier les services implique d’ouvrir des dizaines de ports publics, de configurer un reverse proxy exposé aux attaques, et de sécuriser manuellement chaque brique.
Mon infrastructure de production adopte une approche radicalement différente : une stack complète de plus de 20 conteneurs Docker (développement, productivité, IA, gaming, bases de données) pilotée par une architecture Zero Trust.
Le principe fondamental ? Aucun socket applicatif n’écoute sur une interface publique. Les flux entrants transitent par des tunnels QUIC sortants, les accès sont conditionnés par une fédération d’identité multi-IdP, et l’administration des services internes (bases de données, serveurs MCP) se fait via un réseau privé maillé sous protocole MASQUE.
Voici le deep dive technique complet de cette stack VPS et de sa couche de sécurité.
Vue d’ensemble de l’infrastructure
L’ensemble de la stack tourne sur un serveur dédié Linux (Debian 12) hébergé chez OVH.
flowchart TB
subgraph Clients["Clients & Postes d'Administration"]
Browser["Navigateur / Client Web"]
AdminDev["Poste Admin (Client WARP)"]
DiscordClient["Discord (Bots & Agents)"]
end
subgraph Edge["Cloudflare Edge & Zero Trust"]
Access["Cloudflare Access (IAP / IdP)"]
Gateway["Cloudflare Gateway (Filtrage L4/L7/DNS)"]
VNet["Virtual Network (Teamnet Mesh)"]
CFMail["smtp.mx.cloudflare.net:465"]
end
subgraph Host["Serveur Dédié OVH (Debian 12)"]
subgraph Daemons["Démons Système"]
CFTunnel["cloudflared (Tunnel Ingress QUIC)"]
WARPConn["WARP Connector (Tunnel Mesh MASQUE)"]
end
subgraph DockerEngine["Docker Engine (Monorepo vps)"]
subgraph NetApp["Bridge: applications"]
AppWeb["Services Web Frontaux<br>Dockhand, Pocket ID, Gitea<br>Kaneo, Agents IA, Beszel Hub"]
end
subgraph NetRes["Bridge: resources"]
DBs["Bases de données<br>PostgreSQL, Redis, MariaDB, MongoDB"]
InternalServ["Services Internes<br>Postfix SMTP Relay, Serveurs MCP"]
end
subgraph NetWings["Bridge: wings"]
WingsDaemon["Pelican Wings Daemon"]
GameContainers["Serveurs de jeux isolés"]
end
end
end
Browser -->|HTTPS et HTTP3| Access
Access -->|Tunnel QUIC sortant UDP 7844| CFTunnel
CFTunnel -->|Loopback 127.0.0.1| NetApp
AdminDev -->|Tunnel MASQUE| Gateway
Gateway --> VNet
VNet -->|Routage Prive| WARPConn
WARPConn -->|Netfilter conntrack| NetRes
WARPConn -->|Netfilter conntrack| NetApp
InternalServ -->|TLS Wrappermode port 465| CFMail
1. Orchestration & Gestion de la Stack : Dockhand
La gestion d’une vingtaine de conteneurs avec leurs volumes, secrets et variables d’environnement peut vite devenir ingérable en ligne de commande pure.
Pour centraliser cette administration sans jamais exposer le socket Docker (/var/run/docker.sock) sur le web, la stack est orchestrée par Dockhand, une solution d’administration Docker moderne et réactive.
flowchart LR
subgraph HostEnv["Environnement Serveur"]
Socket["/var/run/docker.sock"]
Compose["docker-compose.yml (Monorepo vps)"]
EnvFiles[".env (Variables chiffrées)"]
end
subgraph DockhandUI["Dockhand Engine"]
UI["Interface Web d'administration"]
Metrics["Collecte métriques & Activity"]
AutoPrune["Image Prune & Auto-Updates"]
end
UI --> Socket
UI --> Compose
UI --> EnvFiles
Fonctionnalités clés de l’orchestration :
- Stack unique déclarative (
vps) : Tous les services, réseaux et volumes sont décrits dans un fichier Docker Compose structuré et versionné. - Healthchecks systématiques : Chaque service critique (PostgreSQL, MariaDB, MongoDB, Redis, Gitea, Kaneo, Pelican, etc.) intègre des sondes de santé (
CMD-SHELL,pg_isready,mongosh ping) assurant un ordonnancement de démarrage parfait via les conditionsdepends_on: service_healthy. - Politique de mise à jour automatisée : Suivi des digests d’images (
latest,lts) et nettoyage automatique des images orphelines (image pruning). - Isolation de l’agent : Dockhand s’exécute lui-même dans un conteneur dédié, avec son propre volume persistant.
2. Le Catalogue Applicatif : Panorama des Services
La stack répond à plusieurs besoins quotidiens : développement, gestion d’identité, productivité, écosystème IA, gaming et multimédia.
A. Identité, Authentification & Sécurité
- Pocket ID : Fournisseur d’identité OIDC moderne et ultra-léger développé en Go/Svelte. Il prend en charge nativement les Passkeys (FIDO2 / WebAuthn) pour une connexion biométrique sécurisée sans mot de passe.
- phpMyAdmin : Console d’administration web pour les bases de données MariaDB.
B. Développement, GitOps & CI/CD
- Gitea : Forge logicielle Git auto-hébergée, adossée à PostgreSQL pour la persistance des données et Postfix pour les e-mails de notification.
- Gitea Mirror : Service automatisé qui synchronise en continu les dépôts GitHub distants vers mon instance Gitea locale pour garantir des sauvegardes autonomes et pérennes.
- act-runner : Runner CI/CD natif pour exécuter des workflows GitHub Actions directement sur le VPS sans dépendance envers l’infrastructure externe.
C. Productivité & Outils Métier
- Kaneo : Application moderne de gestion de projets et ticketing (alternative fluide à Linear/Jira), directement intégrée à Pocket ID en SSO OAuth/OIDC avec flux PKCE.
- Pingvin Share : Plateforme de partage de fichiers temporaires avec chiffrement de bout en bout et liens sécurisés expirables.
- BentoPDF : Suite complète d’outils de manipulation et traitement de fichiers PDF.
D. Écosystème IA, Agents & MCP
- Executor : Runtime d’exécution de code sécurisé et serveur MCP (Model Context Protocol), exposant des endpoints sécurisés pour l’exécution d’outils d’IA.
- Hermes Agent : Agent IA autonome (basé sur NousResearch Hermes) opérant comme un bot Discord d’administration et d’interaction, connecté à la passerelle CLI Proxy API.
- Pi Bridge : Passerelle de communication et d’exécution pour l’agent pi, synchronisé avec Discord.
- CLI Proxy API : Proxy d’API unifiant les requêtes vers les modèles d’IA avec authentification par Service Token.
- GTFS MCP : Serveur MCP privé fournissant des outils d’analyse et de requêtage sur les flux de transports en commun.
E. Gaming & Virtualisation de Jeux
- Pelican Panel : Panel de gestion de serveurs de jeux nouvelle génération (successeur moderne de Pterodactyl), connecté à PostgreSQL et Postfix.
- Pelican Wings : Démon de contrôle gérant le cycle de vie des conteneurs de jeux (Minecraft Bedrock, serveurs SteamCMD) sur un réseau bridge dédié.
F. Multimédia & Utilitaires
- Cobalt : Instance privée de téléchargement et traitement de contenus multimédias, respectueuse de la vie privée.
- VERT : Moteur de conversion vidéo haute performance accéléré par CPU.
- Your Spotify : Dashboard analytique complet traquant les écoutes et statistiques Spotify personnelles, couplé à une base MongoDB.
G. Observabilité & Monitoring
- Beszel : Hub central de monitoring système pour le suivi en temps réel du CPU, de la RAM, de l’I/O disque et de l’état des conteneurs.
- Beszel Agent : Agent léger déployé avec montage en lecture seule du socket Docker (
/var/run/docker.sock:ro) pour collecter les métriques matérielles en continu.
3. Ingress Layer 7 : Cloudflare Tunnel & QUIC
Pour rendre ces applications accessibles depuis le web sans ouvrir aucun port HTTP/HTTPS sur le pare-feu du serveur, le flux Ingress repose sur Cloudflare Tunnel (cloudflared).
sequenceDiagram
participant Edge as Cloudflare Edge (PoPs)
participant Cloudflared as Démon cloudflared (Serveur)
participant LocalApp as Application Docker (127.0.0.1:local_port)
Note over Cloudflared,Edge: Démarrage : 4 tunnels QUIC sortants vers l'Edge
Cloudflared->>Edge: Connexion sortante persistante (UDP/7844)
Edge-->>Cloudflared: Tunnels actifs établis
Note over Edge: Requête utilisateur vers un service
Edge->>Cloudflared: Multiplexage de la requête dans le flux QUIC
Cloudflared->>LocalApp: Proxy HTTP local sur 127.0.0.1
LocalApp-->>Cloudflared: Réponse HTTP 200 OK
Cloudflared-->>Edge: Retour du paquet dans le stream QUIC
Mécanisme d’Ingress sécurisé
Chaque conteneur web est lié exclusivement sur la boucle locale 127.0.0.1 sur un port dédié non exposé au réseau externe :
# Exemple de configuration Ingress cloudflaredingress: - hostname: auth.example.com service: http://127.0.0.1:8001 - hostname: git.example.com service: http://127.0.0.1:8007 - hostname: tasks.example.com service: http://127.0.0.1:8012 - service: http_status:404Grâce à cette approche :
- Absence de port public d’écoute :
netstatoussne montrent aucune écoute sur0.0.0.0:80ou0.0.0.0:443. - DDoS Shield natif : Le trafic passe d’abord par le réseau Anycast mondial de Cloudflare avant d’être injecté dans le tunnel QUIC sortant.
4. Authentification & IAP : Identity Broker multi-IdP
L’exposition des applications est verrouillée par Cloudflare Access (Identity Aware Proxy).
sequenceDiagram
participant Client as Navigateur Web
participant Access as Cloudflare Access (IAP)
participant OIDCProvider as Fournisseur OIDC
participant Service as Application Web
Client->>Access: Requête vers l'application
Access->>Client: Redirection vers l'IdP Broker
Client->>OIDCProvider: Authentification
OIDCProvider-->>Access: Assertion OIDC signée
Note over Access: Évaluation politiques + Signature JWT RS256
Access->>Client: Cookie de session chiffré
Access->>Service: Requête avec Header Cf-Access-Jwt-Assertion
Service-->>Client: Réponse 200 OK
Fédération multi-fournisseurs (IdPs) :
- Pocket ID : IdP principal auto-hébergé avec clés de sécurité FIDO2 et WebAuthn.
- Discord OIDC : IdP pour les applications liées aux agents communautaires.
- GitHub & Google OAuth : Fournisseurs secondaires pour secours et administration.
Règles d’accès et Service Tokens :
- Politique Admin (
allow) : Accès complet pour l’administrateur authentifié. - Posture Appareil WARP (
non_identity) : Accès direct transparent pour les machines enrolées avec le client WARP et certificat actif. - Service Tokens M2M : Authentification machine-to-machine via headers
CF-Access-Client-IdetCF-Access-Client-Secretpour les scripts et automatisations. - Règles de Bypass ciblées : Accès public sélectif pour les points d’entrée nécessaires aux intégrations externes (ex: métadonnées OAuth d’un serveur MCP).
- SSH sans port 22 ouvert : La connexion SSH nécessite la commande
cloudflared access ssh --hostname ssh.example.comqui encapsule le protocole SSH dans un flux HTTPS authentifié par Access.
5. Layer 4 Mesh : WARP Connector & Protocole MASQUE
Pour administrer directement les bases de données (PostgreSQL, MariaDB, Redis, MongoDB) ou les serveurs MCP depuis un poste local avec des outils bureautiques (DBeaver, TablePlus, VS Code), le serveur exécute un WARP Connector rattaché au réseau virtuel Cloudflare (Teamnet).
flowchart LR
subgraph DevMachine["Poste Développeur (Client WARP)"]
DBeaver["DBeaver / TablePlus (SQL)"]
MongoCompass["MongoDB Compass"]
MCPClient["Client MCP / VS Code"]
end
subgraph CFMesh["Cloudflare Virtual Network"]
VNet["Réseau Virtuel Privé"]
CGNAT["Plage CGNAT : 100.64.0.0/10"]
end
subgraph VPS["Serveur Dédié (WARP Connector)"]
WARPInt["Interface CloudflareWARP (MASQUE)"]
Netfilter["Netfilter / conntrack"]
subgraph Subnets["Sous-réseaux Docker Annoncés"]
SubRes["Réseau resources (Bases de données)"]
SubApp["Réseau applications (Outils & MCP)"]
SubWings["Réseau wings (Gaming)"]
end
end
DBeaver -->|TCP SQL| WARPInt
MongoCompass -->|TCP NoSQL| WARPInt
MCPClient -->|HTTP MCP| WARPInt
WARPInt --> Netfilter
Netfilter --> SubRes
Netfilter --> SubApp
Le protocole MASQUE (RFC 9298 / RFC 9484)
Le tunnel utilise le protocole MASQUE (Multiplexed Application Substrate over QUIC Encryption), qui encapsule les paquets IP (TCP/UDP) dans des flux HTTP/3 sur QUIC.
Par rapport à un VPN WireGuard traditionnel :
- Le trafic traverse n’importe quel pare-feu d’entreprise sans être bloqué (apparence de trafic HTTPS standard sur le port 443).
- Le multiplexage QUIC élimine les blocages de tête de ligne pour les flux d’administration simultanés.
Split Tunneling (Mode Include) :
Côté client, le trafic routé dans le tunnel est strictement limité aux ressources nécessaires :
- Sous-réseaux Docker privés du serveur
- Espace CGNAT WARP :
100.64.0.0/10(RFC 6598) - Noms de domaine de production spécifiques
6. Micro-segmentation Docker : 3 Réseaux Bridge Isolés
Pour limiter drastiquement le rayon d’impact (blast radius) en cas de faille applicative, les conteneurs sont cloisonnés en 3 réseaux virtuels indépendants :
flowchart TD
subgraph NetApp["Bridge: applications"]
A1["Dockhand"]
A2["Pocket ID"]
A3["Gitea"]
A4["Kaneo"]
A5["Executor MCP"]
end
subgraph NetRes["Bridge: resources"]
R1[("PostgreSQL")]
R2[("Redis")]
R3[("MariaDB")]
R4[("MongoDB")]
R5["Postfix SMTP Relay"]
end
subgraph NetWings["Bridge: wings"]
W1["Pelican Wings Daemon"]
W2["Serveurs de jeux dédiés"]
end
A3 -->|Requetes SQL| R1
A4 -->|Requetes SQL| R1
A2 -->|Requetes SQL| R1
A3 -->|Envoi SMTP| R5
A2 -->|Envoi SMTP| R5
W1 -->|Stockage SQL| R3
applications: Regroupe les frontaux web. Ces conteneurs ne peuvent pas communiquer avec le réseauresourcessauf s’ils y sont explicitement rattachés.resources: Héberge les bases de données et outils internes. Aucun port n’est exposé sur l’extérieur ; les connexions proviennent uniquement des applications autorisées ou du tunnel WARP.wings: Dédié aux instances de serveurs de jeux instanciées par Pelican Wings, totalement étanches par rapport aux bases de données principales et aux outils de production.
7. Egress SMTP Sécurisé via Cloudflare Email Routing
L’envoi des e-mails transactionnels (confirmations de compte, notifications, alertes système) évite les relais SMTP publics ou non chiffrés :
sequenceDiagram
participant App as Service (Gitea / Pocket ID)
participant Postfix as Conteneur Postfix Relay (Interne)
participant CFMail as Cloudflare Email (smtp.mx.cloudflare.net:465)
participant Recipient as Destinataire final
App->>Postfix: SMTP non chiffré interne (port 25)
Note over Postfix: Chiffrement TLS + Auth SASL (API Token)
Postfix->>CFMail: Connexion TLS Wrappermode (port 465)
CFMail-->>Recipient: Distribution avec signatures SPF / DKIM / DMARC
Le conteneur postfix agit comme relais interne sur le réseau resources et transfère tous les flux sortants vers [smtp.mx.cloudflare.net]:465 en mode TLS implicite avec un Token API Cloudflare dédié.
# Extrait du service Postfix dans docker-compose.ymlpostfix: container_name: postfix image: mwader/postfix-relay:latest networks: - resources environment: - POSTFIX_mynetworks=10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, 100.64.0.0/10 - POSTFIX_relayhost=[smtp.mx.cloudflare.net]:465 - POSTFIX_smtp_tls_security_level=encrypt - POSTFIX_smtp_tls_wrappermode=yes - POSTFIX_smtp_sasl_auth_enable=yes - POSTFIX_smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd - POSTMAP_sasl_passwd=[smtp.mx.cloudflare.net]:465 api_token:${CF_SMTP_API_TOKEN}8. Interaction Linux Kernel : Netfilter, Conntrack et Routage
Pour orchestrer le routage entre l’interface réseau virtuelle du WARP Connector (CloudflareWARP) et les ponts Docker internes, le noyau Linux exploite la table de suivi des connexions (nf_conntrack) avec le routage IPv4 activé :
# Activation du transfert de paquets au niveau du noyausysctl -w net.ipv4.ip_forward=1
# Autorisation du flux entrant depuis l'interface WARP vers les sous-réseaux Dockeriptables -I FORWARD 1 -i CloudflareWARP -d 172.18.0.0/16 -j ACCEPT
# Autorisation du trafic retour via la machine à états conntrackiptables -I FORWARD 2 -o CloudflareWARP -s 172.18.0.0/16 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPTLorsqu’un administrateur interroge une base de données interne via DBeaver sur le tunnel WARP :
- Le paquet TCP SYN pénètre par l’interface
CloudflareWARP. - Le module
nf_conntrackenregistre le nouveau flux dans sa table d’états. - Le paquet est transféré directement vers le bridge Docker correspondant.
- La réponse TCP SYN-ACK émise par le service est automatiquement autorisée par la règle
RELATED,ESTABLISHEDsans nécessiter d’ouverture de port public.
9. Filtrage Gateway : Protection DNS, HTTP et L4
En amont du serveur, l’ensemble du trafic bénéficie des règles d’inspection et de sécurité Cloudflare Gateway :
| Filtre | Règle appliquée | Action | Effet |
|---|---|---|---|
| DNS | Anti-Malware | Block |
Neutralise la résolution des domaines malveillants |
| DNS | Anti-Phishing | Block |
Bloque les tentatives d’hameçonnage |
| DNS | Whitelist / Domaines de confiance | Allow |
Priorité DNSSEC et exemptions de confiance |
| HTTP | Inspection des menaces | Block |
Détection et blocage des URLs compromises |
| L4 / Network | Filtrage réseau Malwares & Phishing | Block |
Drop immédiat au niveau paquet sur les FQDNs ciblés |
Conclusion
Cette infrastructure prouve qu’il est tout à fait possible d’exploiter une stack auto-hébergée riche, moderne et productive sans faire de compromis sur la sécurité :
- Productivité maximale : Plus de 20 services conteneurisés couvrant l’ensemble des besoins (Dev, IA, Projets, Médias, Gaming), orchestrés de manière fluide avec Dockhand.
- Surface d’attaque publique nulle : Aucun port web ou SSH n’est ouvert sur Internet. Les scans automatisés ne voient qu’un hôte muet.
- Identité continue : Chaque interaction est validée par Pocket ID (Passkeys) et Cloudflare Access avec signature cryptographique JWT.
- Administration transparente : L’accès direct aux bases de données et services internes s’effectue via le protocole MASQUE sans configuration de VPN complexe ni exposition de bastions.
Une architecture robuste, moderne, et pensée pour durer.