Post

Cloudflare Tunnel : expose tes services sans ouvrir un port

Cloudflare Tunnel : expose tes services sans ouvrir un port

Tu veux exposer un service self-hosted au monde sans ouvrir un seul port sur ton routeur ? Cloudflare Tunnel (cloudflared) crée un tunnel chiffré outbound depuis ta machine vers l’edge Cloudflare. Zéro port ouvert, zéro IP exposée, HTTPS automatique. C’est la méthode la plus propre pour exposer tes services depuis un homelab ou un VPS derrière un CGNAT.

Pourquoi Cloudflare Tunnel ?

Le modèle classique : ouvrir le port 443, pointer un reverse proxy, gérer les certificats TLS. Ça marche, mais ça implique d’exposer ton IP publique et de gérer la surface d’attaque (Reverse Proxy Nginx).

Cloudflare Tunnel inverse le problème : ta machine initie la connexion vers Cloudflare (outbound). Aucun port entrant n’est nécessaire. Avantages concrets :

ModèlePort ouvertIP exposéeTLSCGNAT compatible
Reverse proxy classiqueOui (443)OuiManuel/certbotNon
WireGuard + reverse proxyOui (51820)OuiVia proxyOui (si port ouvert)
Cloudflare TunnelAucunNonAutomatiqueOui

Pour le contexte, compare avec l’approche WireGuard qui nécessite au moins un port UDP ouvert.

Prérequis

  • Un nom de domaine géré par Cloudflare (gratuit suffit)
  • Un compte Cloudflare
  • Une machine locale ou un VPS avec accès root

Installation de cloudflared

1
2
3
4
# Debian/Ubuntu
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install -y cloudflared
1
2
# Vérifier la version
cloudflared --version

Authentification

1
cloudflared tunnel login

Ça ouvre un navigateur. Tu choisis le domaine à utiliser. Un certificat est stocké dans ~/.cloudflare/cert.pem.

Créer le tunnel

1
2
3
4
5
# Créer le tunnel
cloudflared tunnel create mon-tunnel

# Vérifier
cloudflared tunnel list

Note l’UUID du tunnel. Il sera nécessaire pour la suite.

Configuration

Crée le fichier ~/.cloudflare/config.yml :

1
2
3
4
5
6
7
8
9
10
11
tunnel: <UUID-de-ton-tunnel>
credentials-file: /root/.cloudflare/<UUID>.json

ingress:
  - hostname: vault.example.com
    service: http://localhost:8080
  - hostname: grafana.example.com
    service: http://localhost:3000
  - hostname: git.example.com
    service: http://localhost:3001
  - service: http_status:404

La dernière règle (404) est obligatoire — c’est le catch-all.

Associer le tunnel au DNS

1
2
3
cloudflared tunnel route dns mon-tunnel vault.example.com
cloudflared tunnel route dns mon-tunnel grafana.example.com
cloudflared tunnel route dns mon-tunnel git.example.com

Cloudflare crée automatiquement les enregistrements CNAME.

Lancer le tunnel

1
2
# Test direct
cloudflared tunnel run mon-tunnel

Pour un service permanent, crée un service systemd :

1
2
3
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared

Vérifie que tout fonctionne :

1
2
sudo systemctl status cloudflared
curl -I https://vault.example.com

Docker Compose

Si tes services tournent déjà dans Docker (Sécurité Docker) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: "3.8"

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: tunnel run
    environment:
      - TUNNEL_TOKEN=<ton-token>
    networks:
      - internal

  vaultwarden:
    image: vaultwarden/server:latest
    restart: unless-stopped
    volumes:
      - ./vw-data:/data
    networks:
      - internal

networks:
  internal:
    driver: bridge

Avec cette approche, cloudflared accède à vaultwarden:80 via le réseau Docker interne. Aucun port n’est publié sur l’hôte.

Pour obtenir le token :

1
cloudflared tunnel token create mon-tunnel

Politiques d’accès Zero Trust

Cloudflare Access permet de protéger tes services avec une couche d’authentification supplémentaire. Dans le dashboard Cloudflare > Zero Trust > Access > Applications :

1
2
3
4
5
6
7
# Exemple de politique : accès restreint à ton email
policy:
  name: "Admin only"
  decision: allow
  include:
    - email:
        value: "ton-email@example.com"

Options d’authentification supportées :

  • Email (one-time pin)
  • GitHub, Google, OIDC
  • Clés d’accès matériel (passkeys — voir Passkeys & FIDO2)
  • Service tokens (pour les appels API machine-to-machine)

Tunnel vs Reverse Proxy classique

CritèreCloudflare TunnelNginx + Let’s Encrypt
Port ouvertAucun443 minimum
IP publique exposéeNonOui
CGNAT/NAT strictFonctionneBloqué
Certificats TLSAutomatiqueCertbot/cron
Dépendance CloudflareOuiNon
Latence ajoutée~5-20ms (edge CF)Aucune
GratuitéPlan gratuit suffisantGratuit
Contrôle totalLimité (Cloudflare edge)Total

Le revers de la médaille : tu dépends de Cloudflare. Si leur edge est down, tes services sont inaccessibles. Pour un homelab critique, combine avec WireGuard comme accès de secours.

Durcissement

Même sans port ouvert, les bonnes pratiques s’appliquent :

  • UFW : laisse le firewall actif pour bloquer les scans locaux (UFW & iptables)
  • Fail2ban : protège les services exposés contre le brute-force (Fail2ban avancé)
  • Docker security : ne publie aucun port sur l’hôte, utilise les réseaux internes
  • Monitoring : surveille les logs de cloudflared dans Grafana (Monitoring Prometheus & Grafana)

Checklist de déploiement

  • Domaine ajouté à Cloudflare
  • cloudflared installé et authentifié
  • Tunnel créé et configuré
  • Enregistrements DNS routés
  • Service systemd ou Docker actif
  • Accès HTTPS fonctionnel
  • Politique Access configurée (si exposé publiquement)
  • Logs monitorés
  • UFW actif malgré l’absence de port ouvert
  • Backup de la config tunnel (config.yml + credentials)

Références :

Besoin d’un audit de ton infrastructure ou d’un coup de main sur le déploiement ? Contacte-moi.

This post is licensed under CC BY 4.0 by the author.