aller au contenu
FRFlo
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 conditions depends_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 cloudflared
ingress:
- 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:404

Grâce à cette approche :

  • Absence de port public d’écoute : netstat ou ss ne montrent aucune écoute sur 0.0.0.0:80 ou 0.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) :

  1. Pocket ID : IdP principal auto-hébergé avec clés de sécurité FIDO2 et WebAuthn.
  2. Discord OIDC : IdP pour les applications liées aux agents communautaires.
  3. 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-Id et CF-Access-Client-Secret pour 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.com qui 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
  1. applications : Regroupe les frontaux web. Ces conteneurs ne peuvent pas communiquer avec le réseau resources sauf s’ils y sont explicitement rattachés.
  2. 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.
  3. 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.yml
postfix:
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é :

Terminal window
# Activation du transfert de paquets au niveau du noyau
sysctl -w net.ipv4.ip_forward=1
# Autorisation du flux entrant depuis l'interface WARP vers les sous-réseaux Docker
iptables -I FORWARD 1 -i CloudflareWARP -d 172.18.0.0/16 -j ACCEPT
# Autorisation du trafic retour via la machine à états conntrack
iptables -I FORWARD 2 -o CloudflareWARP -s 172.18.0.0/16 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Lorsqu’un administrateur interroge une base de données interne via DBeaver sur le tunnel WARP :

  1. Le paquet TCP SYN pénètre par l’interface CloudflareWARP.
  2. Le module nf_conntrack enregistre le nouveau flux dans sa table d’états.
  3. Le paquet est transféré directement vers le bridge Docker correspondant.
  4. La réponse TCP SYN-ACK émise par le service est automatiquement autorisée par la règle RELATED,ESTABLISHED sans 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.