DEV Community

Dinh Doan Van Bien
Dinh Doan Van Bien

Posted on

Partie 3 — Traefik et HTTPS

Partie 3 sur 7 — Supabase en auto-hébergement : retour d'expérience

Version française de Part 3 — Traefik and SSL.

Nous avons besoin d'un proxy inverse. Il se place devant tous nos conteneurs, effectue la terminaison TLS et achemine les requêtes entrantes vers le bon service en fonction du nom d'hôte. Traefik est le choix naturel pour les setups basés sur Docker : il lit les labels des conteneurs et se configure automatiquement.

Ajoutez un label à un conteneur pour indiquer "je veux une route sur ce domaine" et Traefik la crée. C'est élégant. Mais il y a un problème de compatibilité que j'ai rencontré dès le départ, et qui m'a coûté un bon moment.


Le problème de version

Les versions récentes de Docker Engine ont modifié la façon dont l'API négocie les versions avec les clients. Traefik v2, la version que l'on trouve dans la plupart des tutoriels, ne gère pas cela correctement. Il se connecte au daemon Docker, semble démarrer sans problème, mais échoue silencieusement à détecter les services.

Vos conteneurs tournent. Traefik tourne. Rien n'est routé. Aucun message d'erreur ne pointe clairement vers la cause.

La solution, c'est Traefik v3, la version majeure actuelle. Traefik v3 gère correctement la négociation de version.

Si vous cherchez "Traefik Supabase Docker", la majorité des résultats montre encore une configuration Traefik v2. Gardez ça à l'esprit avant de copier quoi que ce soit.


Configuration DNS

Avant de configurer Traefik, vous devez créer des enregistrements DNS pointant vers votre serveur. Chez votre fournisseur DNS, créez des enregistrements A pour chaque sous-domaine que vous comptez utiliser :

kong.project1.yourdomain.com    A    YOUR_VPS_IP
studio.project1.yourdomain.com  A    YOUR_VPS_IP
kong.project2.yourdomain.com    A    YOUR_VPS_IP
studio.project2.yourdomain.com  A    YOUR_VPS_IP
Enter fullscreen mode Exit fullscreen mode

Let's Encrypt vérifie ces enregistrements lors de l'émission des certificats. La propagation DNS prend généralement quelques minutes, mais peut aller jusqu'à quelques heures selon votre fournisseur. Avant de déployer, vérifiez que la propagation est complète. Si dig est disponible (apt install dnsutils -y), exécutez dig kong.project1.yourdomain.com +short et confirmez que votre IP VPS est bien retournée. Sinon, attendez et recommencez.


Le stack Traefik

Créez traefik/docker-compose.yml :

networks:
  traefik_default:
    driver: overlay
    attachable: true

services:
  traefik:
    image: traefik:v3
    command:
      - "--providers.swarm.network=traefik_default"
      - "--providers.swarm.exposedByDefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.le.acme.email=your@email.com"
      - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.le.acme.tlschallenge=true"
      - "--log.level=INFO"
      - "--api.dashboard=false"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - letsencrypt:/letsencrypt
    networks:
      - traefik_default
    deploy:
      placement:
        constraints:
          - node.role == manager

volumes:
  letsencrypt:
Enter fullscreen mode Exit fullscreen mode

Quelques explications s'imposent.

exposedByDefault=false signifie que Traefik ignore tout conteneur qui ne s'inscrit pas explicitement avec traefik.enable=true. Sans ça, chaque conteneur sur le réseau Traefik serait accessible publiquement. Ce n'est pas ce que vous voulez.

api.dashboard=false désactive l'interface web de Traefik. Le tableau de bord expose l'intégralité de votre configuration de routage : tous les noms de services, tous les domaines, tous les middlewares. Aucune raison de rendre ça public.

Le volume letsencrypt stocke les certificats. Ne le supprimez pas entre les déploiements. Let's Encrypt applique une limite de demandes de certificats par domaine et par semaine. Si vous videz le volume et redéployez à répétition, vous finirez par atteindre cette limite et vous ne pourrez plus obtenir de nouveau certificat pendant plusieurs jours.

Le trafic HTTP sur le port 80 est redirigé automatiquement vers HTTPS par les règles de redirection des entrypoints.


Un problème de placement de labels

Dans un Docker Compose classique, les labels de service se placent au niveau du service :

services:
  myapp:
    labels:
      traefik.enable: 'true'
Enter fullscreen mode Exit fullscreen mode

Avec docker stack deploy en Docker Swarm, ils doivent aller à l'intérieur du bloc deploy :

services:
  myapp:
    deploy:
      labels:
        traefik.enable: 'true'
Enter fullscreen mode Exit fullscreen mode

Les labels placés au mauvais niveau sont ignorés silencieusement. Traefik ne routera pas le service et ne donnera aucune indication sur la raison. J'ai découvert ça en fixant une configuration d'apparence correcte pendant un long moment avant de trouver l'erreur.


En-têtes de sécurité

Traefik supporte les middlewares : des composants réutilisables qui traitent les requêtes avant qu'elles n'atteignent un service. Nous définissons un middleware security-headers qui ajoute HSTS, supprime l'en-tête Server, définit une politique de type de contenu, et ainsi de suite.

Dans Swarm, un middleware défini dans un stack est accessible aux autres stacks via le suffixe @swarm. Nous définissons le middleware des en-têtes dans les labels du stack project1 (présentés dans le billet suivant), et project2 y fait référence sous le nom security-headers@swarm. Traefik le détecte automatiquement dès que project1 est déployé.

Le middleware des en-têtes ressemble à ceci dans les labels du service :

traefik.http.middlewares.security-headers.headers.stsSeconds: '63072000'
traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains: 'true'
traefik.http.middlewares.security-headers.headers.stsPreload: 'true'
traefik.http.middlewares.security-headers.headers.forceSTSHeader: 'true'
traefik.http.middlewares.security-headers.headers.contentTypeNosniff: 'true'
traefik.http.middlewares.security-headers.headers.referrerPolicy: strict-origin-when-cross-origin
traefik.http.middlewares.security-headers.headers.customFrameOptionsValue: SAMEORIGIN
traefik.http.middlewares.security-headers.headers.customResponseHeaders.Server: ''
Enter fullscreen mode Exit fullscreen mode

Cette dernière ligne fixe l'en-tête de réponse Server à une chaîne vide. Par défaut, Kong annonce sa version dans chaque réponse. Il n'y a aucune bonne raison de faire ça.

Une remarque sur la dépendance inter-stacks : comme le middleware security-headers est défini dans le stack de project1, il disparaît si project1 est complètement arrêté. Project2 se retrouverait alors sans son middleware d'en-têtes. Pour un setup d'apprentissage, c'est acceptable. La solution plus propre consiste à définir les middlewares partagés directement dans le stack Traefik.


Comment Kong obtient sa route

Seuls deux services de notre stack Supabase ont besoin d'une route publique : Kong (la passerelle API) et Studio (le tableau de bord). Tout le reste est interne.

Les labels du service Kong dans notre fichier Compose ressemblent à ceci :

services:
  kong:
    networks:
      - internal
      - traefik_default
    deploy:
      labels:
        traefik.enable: 'true'
        traefik.http.routers.p1-kong.entrypoints: websecure
        traefik.http.routers.p1-kong.rule: Host(`kong.project1.yourdomain.com`)
        traefik.http.routers.p1-kong.tls.certresolver: le
        traefik.http.routers.p1-kong.middlewares: security-headers@swarm
        traefik.http.services.p1-kong.loadbalancer.server.port: '8000'
        traefik.swarm.network: traefik_default
Enter fullscreen mode Exit fullscreen mode

Trois points méritent attention.

Le nom du routeur est p1-kong. Chaque nom de routeur doit être unique dans l'ensemble des stacks qui tournent dans Swarm. Si deux services enregistrent tous les deux un routeur nommé kong, Traefik en choisit un et ignore l'autre sans avertissement. Nous préfixons avec l'identifiant du projet. C'est important dans le billet 6, quand nous ajoutons la deuxième instance.

Le label traefik.swarm.network indique à Traefik quel réseau utiliser pour acheminer les requêtes. Kong est connecté à deux réseaux : le réseau Supabase interne et le réseau Traefik. Sans ce label, Traefik pourrait essayer de passer par le réseau interne, qu'il ne peut pas atteindre.

Le loadbalancer.server.port indique à Traefik vers quel port transférer les requêtes à l'intérieur du conteneur. Comme nous ne publions pas le port de Kong vers l'hôte, Traefik a besoin de connaître directement le port du conteneur.


Déployer Traefik

docker stack deploy -c traefik/docker-compose.yml traefik
Enter fullscreen mode Exit fullscreen mode

Vérifiez que le service a démarré :

docker service ls
# NAME              MODE         REPLICAS
# traefik_traefik   replicated   1/1
Enter fullscreen mode Exit fullscreen mode

Vérifier SSL plus tard

Après avoir déployé un projet Supabase dans le billet suivant, vérifiez que le certificat a bien été émis :

curl -I https://kong.project1.yourdomain.com/health
# HTTP/2 200
# strict-transport-security: max-age=63072000; includeSubDomains; preload
Enter fullscreen mode Exit fullscreen mode

Et que l'en-tête Server a disparu de la réponse.

Alternatives à Traefik

Cette série utilise Traefik parce qu'il s'intègre naturellement à Docker Swarm via les labels de conteneurs. Supabase documente désormais officiellement Caddy et nginx comme alternatives plus simples — à consulter si vous préférez une configuration moins orientée labels.

Un point à noter sur l'approche décrite ici\u00a0: le guide proxy officiel de Supabase recommande de router le trafic Storage directement vers le conteneur storage, en contournant Kong. Kong ajoute une latence inutile pour les uploads et downloads de fichiers volumineux. Si Storage est important pour votre cas d'usage, consultez le guide officiel pour la configuration de proxy direct.


Partie 4 — La première instance Supabase →


La série complète

  1. Pourquoi auto-héberger
  2. Le serveur
  3. Traefik et SSL, vous êtes ici
  4. La première instance Supabase
  5. Vault
  6. Deux instances
  7. Sécurité et test de charge

Top comments (0)