Brahim Bousnguar

Notes · Article

Tailscale Serve : HTTPS pour 50 applis auto-hébergées, aucune sur Internet

Par · · 3 min de lecture

Je fais tourner une cinquantaine de petites applis sur un Mac mini chez moi : tableaux de bord, PWA, API pour mes applis mobiles, un Ollama local. Aucune n’est joignable depuis Internet, et aucune n’écoute non plus sur mon réseau domestique. Chaque appli n’écoute que sur 127.0.0.1, et tailscale serve la publie sur mon tailnet à l’adresse https://<machine>.<tailnet>.ts.net:<port>, avec un vrai certificat, joignable uniquement depuis mes propres appareils, par le tunnel WireGuard chiffré de Tailscale.

Une commande par appli, rien d’autre à faire tourner :

tailscale serve --bg --https 5174 http://127.0.0.1:5174

Le chemin d’une requête

Internet

Tous les autresaucune route d’entrée

Mon tailnet

Mes appareils

Ouvrent l’URL ts.net

Mac mini

Funnel désactivé

tailscale serve

Le certificat s’arrête ici

127.0.0.1

51 entrées serve, toutes en tailnet only

Des zones dans des zones. La requête est chiffrée jusqu’à serve, et seule la dernière étape, à l’intérieur de la machine, passe en HTTP simple. Chaque carré est l’une des 51 entrées serve.

En ce moment, tailscale serve status liste 51 entrées, toutes marquées tailnet only. Aucune n’utilise Funnel, la fonctionnalité qui publierait un port sur l’Internet public.

Pourquoi HTTPS, alors que le tunnel est déjà chiffré

WireGuard chiffre déjà le trafic entre mes appareils, donc du HTTP simple serait assez privé. HTTPS, c’est pour le navigateur. iOS n’exécute le service worker d’une PWA que sur une origine sécurisée : sans HTTPS, mes applis mobiles perdent le mode hors ligne et l’installation sur l’écran d’accueil. Tailscale émet le certificat pour le nom ts.net de la machine, donc il n’y a rien à renouveler à la main.

Un détail : utilisez le nom complet. https://<machine>:5174 échoue, parce que le certificat est émis pour le nom ts.net, pas pour le nom court.

Le tunnel, sans certificat

HTTP

Assez privé : WireGuard le chiffre

iOS refuse de lancer le service worker

Ni hors ligne, ni installation sur l’écran d’accueil

Le même tunnel, plus un certificat

HTTPS

Une origine sécurisée : le navigateur lui fait confiance

Service worker, hors ligne, installation

Tailscale émet le certificat : rien à renouveler

https://<machine>:5174✗ le certificat est émis pour le nom ts.net

https://<machine>.<tailnet>.ts.net:5174✓ correspond au certificat

Utilisez le nom completLe nom court ne correspond pas au certificat.

Les deux téléphones sont sur le même tunnel chiffré, la ligne bleue. Ce qui change, c’est ce que le navigateur laisse faire à mes applis mobiles, et ça dépend du certificat, pas du réseau.

Pourquoi la loopback, et pas le LAN

Lier l’appli à 127.0.0.1, c’est faire de tailscale serve la seule entrée. Un invité sur mon Wi-Fi ne peut pas la joindre, un routeur mal configuré non plus. Dans Docker, c’est la partie hôte du mappage de ports :

ports:
  - "127.0.0.1:5174:80"   # loopback only; tailscale serve proxies to it
  1. Un invité sur mon Wi-Fi

    Adresse LAN ✗

    rien n’écoute

  2. Un routeur mal configuré

    Adresse LAN ✗

    rien n’écoute

  3. Mon téléphone, sur le tailnet

    tailscale serve

    127.0.0.1 → l’appli

Le mappage de ports Docker

  • 127.0.0.1adresse hôte : loopback uniquement
  • 5174port hôte : tailscale serve y relaie les requêtes
  • 80le port à l’intérieur du conteneur

Une seule entréePar tailscale serve, ou pas du tout.

Trois façons de frapper, une seule porte qui s’ouvre. La partie surlignée du mappage est ce qui tient les deux autres dehors.

Quatre pièges où je suis tombé

PiègeCe que j’ai vuCorrectif
Entrée serve perdueL’appli répond sur 127.0.0.1, l’URL du tailnet ne se connecte pasL’entrée avait disparu après un redémarrage de Tailscale. Vérifier tailscale serve status, la rajouter
Appli liée à l’IP du LANServe renvoie des erreurs, la loopback est videUn changement avait lié Docker à l’adresse LAN. Retour à 127.0.0.1:port
Course au port (Ollama)Après un redémarrage, Ollama plante en boucle sur address already in useLe réglage « Expose to network » d’Ollama prenait 0.0.0.0:11434 alors que serve tenait le même port sur l’adresse du tailnet. J’ai désactivé Expose
Vérification du Host (Ollama)403 via serve, tout va bien en localServe transmet l’en-tête Host ts.net et Ollama sur la loopback le rejette. Un petit nginx intermédiaire réécrit le Host

Les deux derniers n’ont touché qu’Ollama, mais la leçon est générale : serve relaie la requête telle quelle, donc une appli qui vérifie l’en-tête Host ou veut le port pour elle seule mérite un coup d’œil.

  1. 1Entrée serve perdue

    Correctifla rajouter ; l’appli allait bien depuis le début

  2. 2Appli liée à l’IP du LAN

    Correctifrevenir à 127.0.0.1

  3. 3Course au portOllama

    Correctifdésactiver Expose to network

  4. 4Vérification du HostOllama

    Correctifun nginx intermédiaire réécrit le Host

La leçonServe transmet la requête telle quelle.

Le même chemin quatre fois, avec l’étape où chaque piège a arrêté ma requête. Le carré jaune est la requête ; les deux derniers pièges ne touchaient qu’Ollama.

Ce que je dirais à quelqu’un qui démarre

  • Liez chaque appli à la loopback et laissez tailscale serve être la seule porte.
  • Passez en HTTPS dès le premier jour si un téléphone doit l’ouvrir. Les PWA en ont besoin.
  • Tenez une liste des ports. Avec 50 applis, un registre de ports évite que deux applis se disputent le même numéro.
  • Vérifiez d’abord tailscale serve status quand une URL ne répond plus. La plupart de mes pannes venaient d’une entrée manquante, pas d’une appli cassée.
  1. 1Loopback uniquement

    Serve est la seule porte

  2. 2HTTPS dès le premier jour

    Si un téléphone l’ouvre, sa PWA en a besoin

  3. 3Une liste des ports

    Deux applis ne se disputent jamais un numéro

  4. 4Le statut d’abord

    La plupart des pannes : une entrée manquante, pas une appli cassée

Les quatre mêmes habitudes, en dessin. La quatrième a le cadre bleu, parce que c’est là que se trouvaient la plupart de mes pannes.

Texte brut: tailscale-serve-self-hosted-apps.md · Flux Atom · Toutes les notes

Contact

Écrivez-moi.

b.bousnguar@gmail.com

Intégration SAP Commerce Cloud, MuleSoft et Salesforce · Nantes · FR / EN