Panne d’Internet, partie 2/2 : rendre accessible son serveur derrière la connexion du voisin

Dans la partie précédente, j’ai décrit comment réinjecter la connexion Internet prêtée par un voisin sympa dans son propre réseau local. Le but était de rétablir la connectivité au niveau du LAN lui-même, sans avoir à reconfigurer tous les équipements pour qu’ils utilisent le réseau du voisin.

C’est bien, mais je n’ai toujours pas accès au serveur, y compris depuis l’intérieur du réseau local : mon nom de domaine pointe toujours vers l’IP publique de la box, qui est inaccessible. Les services qu’il héberge ne sont donc plus disponibles et je m’en sers pourtant régulièrement, en particulier :

  • Mon cloud personnel qui utilise Seafile,
  • Les interfaces Web qui me permettent de contrôler ma domotique : volets roulants, chauffage, relevé des consommation d’eau/d’électricité/de gaz, etc.
  • Mon serveur VPN, pour être virtuellement chez moi lorsque je n’y suis pas physiquement et qui me permet d’accéder à des ressources locales ou de contourner certaines limitations,
  • Et encore d’autres…

L’objectif de cette seconde partie est donc de rendre à nouveau accessible le serveur. La méthode la plus simple serait de faire pointer mon nom de domaine vers l’IP publique de la box du voisin et ensuite d’ouvrir les ports de son routeur en les redirigeant vers mon serveur. Mais cela pose d’autres problèmes :

  • Je n’ai pas envie de l’embêter avec cela.
  • Si lui aussi a des services hébergés sur les mêmes ports (au hasard : 443) cela en fonctionnera pas. Et le connaissant, c’est quasi certain que c’est le cas (coucou si tu me lis 😉 )
  • Pour terminer, il m’a laissé un accès à son réseau “invité” qui est isolé de son réseau principal et je ne suis pas sûr du tout que son routeur permette de rediriger des connexions entrantes vers une machine du réseau “invité“.

Il faut donc trouver une autre solution.

Le dépannage d’urgence : rendre le serveur à nouveau accessible depuis le réseau local

Techniquement, le serveur a toujours été accessible depuis le réseau local, dans le sens où il a toujours été joignable. Malheureusement, les interfaces Web que j’utilise au quotidien sont hébergées par le serveur Web nginx et accessibles depuis des sous-domaine de fandam.eu différents : ce sont pour lui des sites Web distincts.

De plus, nginx a forcément besoin que les requêtes passent par le nom de domaine pour savoir quel site Web est demandé parmi tous ceux qu’il héberge simultanément : impossible de taper dans un navigateur l’adresse IP locale du serveur. Dès lors, puisque fandam.eu ne pointe plus au bon endroit, je perds alors totalement l’accès à ces interfaces.

Bref, c’est la cata : je dois me lever et appuyer manuellement sur les télécommandes stockées dans le placard technique pour bouger mes volets roulants.

Pour sauver la mise, la première chose à faire est de modifier la configuration du nom de domaine pour ne plus le faire pointer vers l’IP publique de ma box, mais vers l’IP locale de mon serveur (192.168.1.250). Faire pointer un nom de domaine public vers une IP locale peut paraître assez inhabituel, mais c’est techniquement fonctionnel. Je récupère ainsi l’accès, au moins en local, aux services que j’utilise quotidiennement.

L’alternative plus pérenne : utiliser un tunnel SSH vers un autre serveur qui va faire office d’intermédiaire

Pour enfin rendre le serveur accessible à l’extérieur, la solution que j’ai retenue nécessite de posséder un autre serveur à l’extérieur du LAN, accessible lui sur Internet.

  • Depuis le serveur local, il faut alors créer un tunnel SSH vers le serveur distant. Cela est possible car cela utilise une connexion sortante à travers la connexion du voisin.
  • Mais grâce à ce tunnel, des requêtes pourront circuler en sens inverse : du serveur distant vers le serveur local !
  • En faisant pointer le nom de domaine fandam.eu vers le serveur distant et en le configurant pour qu’il redirige les requêtes concernant ce domaine vers le serveur local à travers le tunnel, on récupère donc l’accès à ce dernier, même depuis l’extérieur !

Facile, non ? Je vais utiliser comme serveur distant le système d’interphone vidéo que j’ai fabriqué chez mes parents.

La création du tunnel SSH

Par défaut, un tunnel SSH “écoute” sur un port de la machine locale et “réémet” les paquets sur un port de la machine distante. Dans notre cas, nous avons besoin de l’inverse : c’est le serveur distant qui doit être à l’écoute et les paquets qu’il reçoit doivent être réémis vers la machine locale :

Ce type de tunnel peut être créé grâce à la commande suivante :

ssh -R 443:127.0.0.1:443 utilisateur@serveur_distant.net -f –N

Voyons un peu l’utilité de chacun des paramètres :

  • -R indique que le tunnel doit écouter du côté distant et réémettre du côté local. L’inverse serait réalisé avec le paramètre -L : dans ce cas, l’écoute serait réalisée côté local et les paquets réémis côté distant. Autrement dit :
    • Avec -R, l’entrée du tunnel est le serveur distant et la sortie, le serveur local.
    • Avec -L, l’entrée est le serveur local et la sortie, le serveur distant.
  • 443:127.0.0.1:443 est le paramètre qui indique les ports d’entrée et de sortie du tunnel, ainsi que le serveur à contacter en sortie de tunnel. Ce paramètre est sous la forme A:B:C où :
    • A est le port sur lequel doit écouter l’entrée du tunnel.
      • Si -R est utilisé, ce port est donc côté distant.
      • Si -L est utilisé, il est côté local.
        • Dans notre cas, on veut que notre serveur intermédiaire écoute sur le port 443, qui est le port standard pour le protocole HTTPS.
    • B est la machine sur laquelle la sortie du tunnel doit retransmettre les paquets. En effet, contrairement à ce que je pensais initialement, la sortie du tunnel peut tout-à-fait envoyer les paquets sur une autre machine, différente de celle sur laquelle le tunnel débouche !
      • Dans notre cas, le tunnel débouche sur notre serveur local, celui-là même qui héberge les sites Web. On saisit donc 127.0.0.1.
    • Pour terminer, C est le port de la machine B sur lequel les paquets doivent être envoyées.
      • Comme le serveur local met à disposition les sites Web sur le port 443, c’est ce port que l’on entre ici.
  • -f permet de laisser le tunnel actif en tâche de fond.
  • Enfin, -N demande à ssh de ne pas exécuter de commande sur le serveur distant.

Bref, voilà la théorie. En pratique, je ne peux pas utiliser cette solution car elle implique que le serveur distant ait son port 443 libre, autrement dit, qu’il n’héberge pas déjà de site Web. Or, ce n’est bien évidemment pas le cas : il faut trouver une astuce.

Je n’ai donc pas testé cette solution, mais d’après-moi, elle devrait fonctionner sans opération supplémentaire. Dites-moi dans les commentaires si je me trompe !

Je ne l’ai pas fait car je peux m’en passer pendant une semaine, mais j’aurais pu utiliser cette méthode pour rendre à nouveau accessible un service de mon serveur qui n’est pas fourni par le serveur distant, par exemple le VPN.

Le proxy_pass de nginx à la rescousse

Le serveur distant héberge déjà un site Web grâce à nginx, ce qui explique que les ports 80 et 443 soient déjà occupés.

On va alors utiliser la directive proxy_pass qui permet de créer un nouveau serveur Web qui redirige toutes les données (entrantes et sortantes) vers un serveur existant. Ce serveur existant ne sera rien d’autre que l’entrée du tunnel, déplacé sur un port autre que 443, par exemple 50443 :

Compris ? Les requêtes entrantes sur le port 443 du serveur distant sont toutes récupérées par nginx. Ensuite, il regarde quel est le nom de domaine concerné par la requête :

  • Si la requête demande les domaines habituels qui le concernent, cela ne change rien : le serveur renvoie les sites Web qu’il héberge.
  • Par contre, si la requête demande un sous-domaine de fandam.eu, alors le serveur utilise la règle proxy_pass et retransmet la requête à 127.0.0.1 sur le port 50443.
    • Il y un bien un serveur qui écoute sur 127.0.0.1 sur le port 50443 : c’est l’entrée du tunnel.
    • La requête traverse donc le tunnel jusqu’à arriver… sur notre serveur local ! Enfin !
    • Ensuite, la réponse du serveur reprend le chemin en sens inverse.

Il reste donc juste à ajouter cette directive proxy_pass à la configuration de nginx existante. Pour cela, le mieux est certainement de créer un nouveau fichier dans /etc/nginx/sites-enabled/ contenant le texte suivant :

server {
     listen 443 ssl;
     ssl on;
     ssl_certificate /etc/letsencrypt/live/fandam.eu/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/fandam.eu/privkey.pem;
     server_name  ~^*.fandam.eu$;
     ssl_session_timeout 1440m;
     ssl_session_cache shared:SSL:5m;
     # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
 ssl_dhparam /etc/nginx/dhparam.pem;
 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers
 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:EC$
     ssl_prefer_server_ciphers on;
     add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
     proxy_set_header X-Forwarded-For $remote_addr;
 location / {     
     proxy_pass https://127.0.0.1:50443;
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header X-Forwarded-Host $server_name;
     proxy_read_timeout 1200s;
     proxy_set_header X-Forwarded-Proto https;
     client_max_body_size 0;
     access_log /dev/null;
     error_log /dev/null; 
     }
 }

Avant de redémarrer le nginx pour qu’il prenne en compte la nouvelle configuration, il faut mieux tester le fichier de config pour ne pas avoir de surprise en cas d’erreur :

nginx -c /etc/nginx/nginx.conf –t

Ah oui, une dernière chose à faire : la directive proxy_pass de nginx crée un nouveau site Web sur le serveur distant, lequel doit donc posséder son propre certificat SSL. Il ne me semble pas qu’il soit possible d’utiliser le certificat du serveur local.

C’est donc un peu lourd, mais il n’y a à ma connaissance pas d’autre possibilité que de créer un nouveau certificat. Toutefois, on peut créer un certificat multi-domaine, qui couvre tous les sous-domaines de fandam.eu pour limiter le nombre d’opérations à effectuer :

service nginx stop
certbot certonly --standalone -d www.fandam.eu -d blog.fandam.eu -d blabla.fandam.eu ... 
service nginx start

Note : il faut bien penser à arrêter nginx pour que certbot puisse démarrer son propre serveur Web pour obtenir le certificat…

Il n’y a plus qu’à faire pointer le domaine fandam.eu vers le nouveau serveur et lieu de l’ancien et voilà ! Les interfaces Web sont à nouveau accessibles ;-).

C’est la fin de l’épopée “panne de connexion Internet” ! Merci de m’avoir lu.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *