Docs/ Sécurité/ Accéder au panneau par un tunnel SSH

Accéder au panneau par un tunnel SSH

Un panneau que personne n'atteint depuis Internet ne peut pas être attaqué depuis Internet.

Vue d'ensemble

Vos applications sont servies par sh0 sur les ports 80 et 443. Le panneau, qui les contrôle toutes, n'a pas à l'être. En mode tunnel seul, il n'écoute que sur 127.0.0.1:9000 : la seule entrée est une connexion SSH au serveur, que votre pare-feu restreint déjà et que vous protégez déjà par une clé.

Pourquoi pas de domaine de panneau public

Chaque certificat TLS est publié dans les journaux publics de Certificate Transparency : un domaine de panneau comme panel.example.com apparaît sur crt.sh quelques minutes après l'émission de son certificat. Les scanners lisent ces journaux : un panneau public est trouvé, puis sondé, quelle que soit sa politique de mots de passe.

Installer en mode tunnel seul

Sur un serveur neuf, en root, avec un pare-feu déjà limité à SSH, 80 et 443 :

bash
curl -fsSL https://get.sh0.dev | bash -s -- --harden --panel-tunnel-only

Avant le premier démarrage de sh0, l'installeur écrit un drop-in systemd qui lie le panneau à 127.0.0.1, désactive l'enregistrement *.sh0.app (un proxy n'atteint pas un panneau en boucle locale) et, avec --harden, n'ouvre pas le port 9000 dans ufw. Il se termine en affichant la commande ssh exacte à coller sur votre propre ordinateur.

Warning
Dans ce mode, vos applications perdent leurs adresses gratuites *.sh0.app. Le proxy *.sh0.app joint le serveur par le port 9000, que le mode tunnel seul ferme : chaque application doit alors être servie sur un domaine à vous. Si vous ou vos développeurs n'avez pas de domaine, gardez l'installation par défaut : le panneau reste joignable sur le port 9000 et chaque application garde son adresse gratuite.
Note
Le premier compte propriétaire exige toujours le jeton d'amorçage, lu sur le serveur par sudo sh0 setup-token. Derrière le tunnel, personne d'autre n'atteint le panneau, mais la règle reste la même partout.

Ouvrir le tunnel

1. Sur votre propre ordinateur (pas sur le serveur), ouvrez le tunnel et laissez la commande tourner :

bash
ssh -N -L 9000:127.0.0.1:9000 root@SERVER_IP

2. Ouvrez http://localhost:9000. Le trafic circule à l'intérieur de SSH : rien ne traverse le réseau en clair, même si l'adresse commence par http://. Ctrl+C ferme le tunnel.

3. Pour ne plus la retaper, ajoutez un bloc à ~/.ssh/config sur votre ordinateur :

text
Host sh0-panel
    HostName SERVER_IP
    User root
    LocalForward 9000 127.0.0.1:9000
    SessionType none
    ExitOnForwardFailure yes

Ensuite, ssh sh0-panel ouvre le tunnel. SessionType none exige OpenSSH 8.7 ou plus récent ; avec un client plus ancien, retirez cette ligne et lancez ssh -N sh0-panel.

Pannes courantes

SymptômeCause et correction
ssh reste bloqué, puis « Connection timed out »SSH lui-même est filtré : votre IP n'est pas autorisée par le pare-feu de l'hébergeur, ou le serveur écoute sur un autre port (ajoutez -p PORT). Le tunnel n'est pas en cause.
« bind [127.0.0.1]:9000: Address already in use »Le port 9000 est déjà pris sur votre ordinateur, souvent par un autre tunnel. Prenez un autre port local : -L 9001:127.0.0.1:9000, puis http://localhost:9001.
Navigateur : « connexion refusée » sur localhost:9000Le tunnel n'est pas ouvert (la commande ssh s'est arrêtée), ou sh0 ne tourne pas sur le serveur : systemctl status sh0.
« channel 2: open failed: connect failed » dans la fenêtre sshSSH fonctionne, mais rien ne répond sur 127.0.0.1:9000 côté serveur, ou AllowTcpForwarding est désactivé dans sshd. Vérifiez ss -ltn | grep 9000 et sshd -T | grep allowtcpforwarding.

Basculer un serveur existant

Un serveur qui fait déjà tourner sh0 se bascule en relançant l'installeur avec les mêmes options. Il conserve vos applications, écrit le drop-in, retire une règle ufw existante pour 9000 (avec --harden) et redémarre sh0 une fois, ce qui retire aussi son adresse *.sh0.app. Les relances suivantes de l'installeur, pour une mise à jour, gardent le serveur en mode tunnel seul tant que le drop-in existe.

bash
curl -fsSL https://get.sh0.dev | bash -s -- --harden --panel-tunnel-only

Vos applications continuent de répondre sur leurs domaines : elles sont servies par Caddy sur 80 et 443, que la bascule ne touche pas. Comptez quelques secondes d'interruption pendant le redémarrage de sh0. Si un domaine de panneau est posé, l'installeur s'arrête avant de rien changer et explique pourquoi (section suivante).

Ce que le tunnel ne ferme pas

Warning
Le mode tunnel seul déplace l'écoute propre du panneau sur 127.0.0.1. Il ne retire pas un domaine de panneau : si sh0 panel-domain en affiche un, Caddy continue de servir le panneau sur ce domaine par le port 443, à tout Internet. Pour un vrai tunnel seul, retirez le domaine. L'installeur refuse --panel-tunnel-only tant qu'un domaine est posé par sh0 panel-domain ou par SH0_PANEL_DOMAIN dans l'environnement du service, et exige sh0 1.10.6 ou plus récent ; un domaine configuré autrement n'est pas détecté : vérifiez-le vous-même.
bash
sudo sh0 panel-domain          # show
sudo sh0 panel-domain clear    # remove the route and the setting

Accès par navigateur sans tunnel

Si le tunnel n'est pas envisageable pour votre équipe, gardez un domaine de panneau, mais placez Cloudflare Access (ou un proxy d'identité équivalent) devant, et limitez le port 443 aux plages d'adresses de Cloudflare au pare-feu de l'hébergeur. Sans cette seconde étape, Access se contourne en se connectant directement à l'IP du serveur. Cette restriction vaut pour toutes les applications du serveur : chacun de leurs domaines doit alors passer lui aussi par le proxy Cloudflare, sinon il cesse de répondre. Ne combinez pas cette méthode avec --panel-tunnel-only.

Vérifier le résultat

Sur le serveur, le panneau ne doit écouter que sur l'adresse de boucle locale, aucun domaine de panneau ne doit être posé, et sh0 doctor ne doit signaler aucun échec. Depuis un autre réseau, le port 9000 doit être fermé.

bash
ss -ltn | grep ':9000'          # 127.0.0.1:9000 only
cat /etc/systemd/system/sh0.service.d/panel-tunnel-only.conf
sudo sh0 panel-domain            # Domain: not set
sudo sh0 doctor

Revenir en arrière

Pour rendre le panneau de nouveau joignable sur l'adresse du serveur, supprimez le drop-in, redémarrez sh0 et rouvrez le port :

bash
rm /etc/systemd/system/sh0.service.d/panel-tunnel-only.conf
rm /etc/systemd/system/sh0.service.d/cloud-registration.conf   # only to get *.sh0.app back
systemctl daemon-reload && systemctl restart sh0
ufw allow 9000/tcp                                              # if the firewall is ufw

Le reste de l'hôte (clés SSH, pare-feu, fail2ban) est traité dans Sécuriser son serveur