Aller au contenu

Tuto · pour qui héberge chez soi

Cloudflare pour les nuls

Si tu as ouvert cette page, c'est probablement parce que tu as un serveur à la maison, des applis à montrer au monde, et qu'à chaque passage dans le dashboard Cloudflare tu cliques un peu au hasard en espérant que ça marche. Cette page explique ce que sont réellement un tunnel, un hostname publié, un enregistrement DNS et une policy Access — en s'appuyant sur une vraie panne de 3 semaines (la fameuse erreur 1033) et sa résolution sur le homelab qui héberge AetherWX. Lecture ~15 min. Complément naturel de la bible /argocd-k8s-pour-les-nuls côté cluster.

0. Le problème de départ

Tu as une appli qui tourne chez toi — sur un NAS, un vieux PC, un cluster K8s de salon. Elle marche très bien sur http://192.168.1.38. Maintenant tu veux que monapp.mondomaine.dev marche depuis n'importe où. Instinctivement, tu penses : « j'ouvre le port 443 sur ma box et je redirige vers le serveur ».

C'est la méthode historique, et elle a trois problèmes :

  • Ton IP publique change. Les box résidentielles ont une IP dynamique. Ton DNS pointe dans le vide au premier renouvellement de bail.
  • Tu exposes ta maison. Un port ouvert, c'est une porte que les scanners automatiques du monde entier testent en continu. La moindre CVE sur ton reverse proxy et c'est ton réseau domestique qui est en jeu.
  • Le TLS, c'est toi qui le gères. Certificats, renouvellements, la totale.

Le tunnel Cloudflare renverse le problème : au lieu de laisser Internet entrer chez toi, c'est chez toi qui sort vers Internet.

1. Le tunnel : une connexion à l'envers

Un petit programme, cloudflared, tourne dans ton réseau (chez nous : un pod K8s de 64 Mo). Au démarrage, il ouvre 4 connexions sortantes vers les serveurs Cloudflare les plus proches (à Paris : les datacenters cdg) et les garde ouvertes en permanence.

Quand un visiteur demande aetherwx.sladoire.dev, sa requête arrive chez Cloudflare — jamais chez toi directement. Cloudflare la pousse dans le tuyau déjà ouvert, cloudflared la reçoit et la transmet au service interne. La réponse fait le chemin inverse.

Architecture
Loading diagram…
Aucune flèche n'entre dans la maison : cloudflared a appelé Cloudflare en premier, et la connexion reste ouverte pour faire passer les requêtes à contresens.

Conséquences immédiates, toutes agréables :

  • Zéro port ouvert sur la box. Un scanner ne voit rien.
  • L'IP dynamique n'est plus un sujet : c'est cloudflared qui appelle, peu importe d'où.
  • TLS géré par Cloudflare : ton service interne peut parler HTTP tout nu, le visiteur voit un cadenas.

Le tunnel est identifié par un token (le long eyJhbGci... que le dashboard te fait copier à la création). Ce token est la seule chose dont cloudflared a besoin pour se présenter : « je suis le connecteur du tunnel dark-blue ». Chez nous il vit dans un Secret K8s, jamais dans Git.

2. Les 4 objets du dashboard

Tout ce qu'on fait dans Cloudflare pour un homelab tient en 4 objets. Si tu sais lesquels tu touches, tu ne cliques plus au hasard :

  • Le tunnel (Zero Trust → Networks → Tunnels) — le tuyau lui-même. Un par site physique suffit : le nôtre s'appelle dark-blue, comme le serveur. Son statut (HEALTHY / DOWN) reflète l'état des connecteurs cloudflared.
  • Les applications publiées (« Published applications », onglet du tunnel) — la table de routage : tel sous-domaine public → tel service interne. C'est ici qu'on décide ce qui est visible d'Internet.
  • Les enregistrements DNS (dashboard classique → ta zone → DNS) — pour chaque application publiée, Cloudflare crée automatiquement un CNAME monapp → <id-du-tunnel>.cfargotunnel.com. Tu n'y touches quasiment jamais… sauf pour faire le ménage (on y revient, c'est du vécu).
  • Les applications Access (Zero Trust → Access → Applications) — des portiers optionnels devant un hostname : « avant de servir cette app, vérifie l'identité du visiteur ». Chez nous : un code par email, réservé à une seule adresse, devant l'interface d'administration ArgoCD.
La table de routage réelle du tunnel dark-blue text
Sous-domaine public          →  Service interne (DNS du cluster K8s)
aetherwx.sladoire.dev        →  http://maritime-frontend.maritime.svc.cluster.local:80
geoserver.sladoire.dev       →  http://geoserver.maritime.svc.cluster.local:8080
argocd.sladoire.dev          →  http://argocd-server.argocd.svc.cluster.local:80   (+ Access)
ol.sladoire.dev              →  http://ol-frontend.preprod.svc.cluster.local:80
warhammer.sladoire.dev       →  http://warhammer-frontend.preprod.svc.cluster.local:80

finance.sladoire.dev         →  (RIEN. Volontairement.)

La dernière ligne est la plus importante de cette page. L'appli finance manipule de vraies données bancaires : elle n'a ni route dans le tunnel, ni enregistrement DNS. Résultat : finance.sladoire.dev ne se résout même pas. Ce n'est pas « protégé par un mot de passe », c'est inexistant vu d'Internet — la meilleure sécurité est celle qui n'a rien à défendre. L'appli reste accessible en LAN, et son PIN devient un simple deuxième rideau.

3. Le chemin complet d'une requête

Architecture
Loading diagram…
Ce qui se passe entre « je tape l'URL » et « la carte s'affiche ». Les étapes 2 et 3 sont chez Cloudflare, tout le reste chez toi.

Note bien où le TLS s'arrête : au bord de Cloudflare. Entre cloudflared et ton service, on est dans le réseau privé du cluster, en HTTP simple. C'est pour ça que la colonne « Service » d'une application publiée commence par http:// sans que personne ne hurle.

4. Erreur 1033 : autopsie d'une panne de 3 semaines

Maintenant qu'on a le modèle en tête, décodons la panne qui a motivé cette page. Pendant 3 semaines, ol.sladoire.dev affichait :

Ce que voyaient les visiteurs text
Error 1033 — Cloudflare Tunnel error
The host (ol.sladoire.dev) is configured as a Cloudflare Tunnel,
and Cloudflare is currently unable to resolve it.

Traduction avec notre vocabulaire : le DNS est bon (l'objet 3 pointe vers le tunnel), la route existe (objet 2), mais aucun connecteur cloudflared n'est branché au tuyau (objet 1 : tunnel DOWN). Cloudflare reçoit la requête, se tourne vers le tunnel… et il n'y a personne au bout.

Chez nous, le cloudflared tournait dans un cluster K8s sous WSL qui gelait régulièrement — puis dont la machine a fini débranchée. L'appli elle-même était en pleine forme sur une autre machine : 1033 ne dit rien de ton appli, il parle uniquement du connecteur. C'est le piège classique : on debug le service alors que c'est le tuyau.

Le tableau de diagnostic à garder sous le coude :

Symptôme → coupable → réflexe text
Erreur 1033 / 530        → connecteur mort            → kubectl -n infra get pods
                            (tunnel DOWN)                kubectl -n infra logs deploy/cloudflared
Erreur 502                → tunnel OK, service interne → l'URL "Service" de la route est-elle
                            injoignable                  bonne ? le pod visé tourne-t-il ?
NXDOMAIN                  → pas d'enregistrement DNS   → la route/le CNAME existe-t-il ?
Page de login exposée     → il manque le portier       → Zero Trust → Access → Applications
  (200 au lieu de 302)      Access
"DNS record already       → un CNAME fantôme d'un      → zone DNS : supprimer les records
  exists" à la création     ancien tunnel traîne          pointant sur l'ancien tunnel

Vécu, épisode 2 : à la création du nouveau tunnel, impossible de publier aetherwx — « A DNS record with this name already exists ». Les CNAME de l'ancien tunnel mort étaient encore là. Un tunnel supprimé ou remplacé ne nettoie pas ses enregistrements DNS : c'est à toi de passer dans la zone et de supprimer les records *.cfargotunnel.com orphelins. En le faisant, on a même retrouvé un fantôme d'avant un renommage de projet, mort depuis des mois. Attention en revanche à ne pas emporter les CNAME légitimes — chez nous, deux sites statiques sur Cloudflare Pages (*.pages.dev) vivent dans la même zone.

5. Access : le portier devant l'admin

Dernier objet, le plus « Zero Trust » de tous. Certaines URLs méritent d'exister publiquement (pratique pour administrer depuis n'importe où) sans être ouvertes à n'importe qui. L'interface ArgoCD, par exemple : elle a son propre login, mais on préfère que même sa page de login ne soit pas servie au premier scanner venu.

Une application Access intercepte tout le trafic d'un hostname avant qu'il n'entre dans le tunnel. Le visiteur tombe sur une page Cloudflare qui exige une preuve d'identité — chez nous, la plus simple : un code à usage unique envoyé par email, avec une policy « Allow » qui ne liste qu'une seule adresse. Pas de VPN, pas de client à installer, et la charge de gérer l'authentification est chez Cloudflare.

Vérifier qu'un portier est bien en place text
$ curl -sI https://argocd.sladoire.dev/ | head -2
HTTP/2 302
location: https://sladoire.cloudflareaccess.com/cdn-cgi/access/login/...

# 302 vers cloudflareaccess.com = le portier fait son travail.
# Un 200 direct ici = la page d'admin est servie à tout Internet.

Anecdote honnête : en refaisant l'infra, on a découvert que ce portier ne protégeait… que le mauvais service (un outil interne décommissionné depuis). ArgoCD, lui, était exposé nu depuis le début. On a recyclé l'application Access existante — renommée, re-pointée — en 2 minutes. Morale : teste avec curl, pas de mémoire. Un portier, ça se vérifie.

6. Côté cluster : 40 lignes de YAML, et Git commande

Tout ce qui précède se passe côté Cloudflare. Côté cluster, la partie est minuscule — un Deployment d'un container, et un Secret pour le token :

charts/cloudflared/ — l'essentiel du chart yaml
# Le secret (créé à la main, JAMAIS commité — le repo est public) :
#   kubectl -n infra create secret generic cloudflared-token \
#     --from-literal=token='eyJhbGci...'

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cloudflared
  namespace: infra
spec:
  replicas: 1            # Cloudflare gère la HA côté edge
  template:
    spec:
      containers:
        - name: cloudflared
          image: cloudflare/cloudflared:latest
          args: ["tunnel", "run", "--token", "$(TUNNEL_TOKEN)"]
          env:
            - name: TUNNEL_TOKEN
              valueFrom:
                secretKeyRef: { name: cloudflared-token, key: token }
          resources:
            requests: { cpu: 50m,  memory: 64Mi }
            limits:   { cpu: 200m, memory: 256Mi }

Le chart est déployé par ArgoCD comme tout le reste (cf. la bible ArgoCD). Le jour où on a migré de l'ancien serveur au nouveau, la procédure complète côté Cloudflare a tenu en : créer un tunnel neuf, coller le token dans un Secret, nettoyer les CNAME fantômes, re-saisir 5 routes, re-poser 1 portier Access. Trente minutes, en comprenant chaque clic — c'est tout l'enjeu de cette page.

Piège d'interface (2026) : dans le nouveau dashboard « Cloudflare One », les Public Hostnames s'appellent désormais Published applications. L'onglet voisin « Hostname routes » ressemble beaucoup mais fait tout autre chose : du routage réseau privé, qui exige d'installer le client Cloudflare One sur chaque appareil visiteur. Si ta page parle de « Gateway » et de « on-ramp », tu es au mauvais endroit. Le plus court chemin vers n'importe quel écran : la loupe 🔍 en haut de la barre latérale.

7. Ce qu'on n'a pas couvert (et qui existe)

  • Cloudflare Pages — l'hébergement statique : les deux CNAME *.pages.dev épargnés pendant le ménage sont des sites Astro déployés à chaque push Git, sans serveur du tout.
  • Les Cache Rules — le CDN devant les tuiles cartographiques : une règle qui force la mise en cache des GetMap GeoServer a divisé la charge du serveur maison sans dépenser un euro.
  • WAF, rate limiting, analytics — tout le trafic passant par Cloudflare, on peut filtrer et observer au même endroit.

Mais l'essentiel tient dans le modèle mental : un tuyau sortant (tunnel), une table de routage (published applications), des CNAME automatiques (DNS), des portiers optionnels (Access). Une fois ces quatre objets identifiés, le dashboard cesse d'être un labyrinthe — et l'erreur 1033 cesse d'être un mystère : c'est juste un tuyau dont personne ne tient l'autre bout.