Un tunnel low cost

Besoin de tunnel

La dernière mode chez les développeurs, c’est d’exposer publiquement son environnement local, généralement une application web en cours d’écriture. De mon côté, il m’a été demandé de travailler sur un système de paiement dont la configuration nécessitait que ma machine de développement soit accessible au service de paiement.

Détail d’une maison alsacienne. Cliquer pour voir l’image en plus grand

Il m’a été proposé d’utiliser les tunnels Cloudflare, ou bien d’installer ngrok. Mais ces deux solutions, ainsi que leurs multiples alternatives, me semblaient «/overkill/» pour mon simple besoin. J’ai donc préféré m’amuser à implémenter une solution plus modeste et sur mesure, que je vais présenter ci-après.

Solution maison

Je contrôle déjà un serveur web qui est accessible publiquement sur internet. C’est le serveur web qui héberge le blog sur lequel vous êtes certainement en train de lire cet article. Mon objectif va être d’utiliser ce serveur comme proxy web sur un nom de domaine dédié, qui transmettra toutes les requêtes vers mon serveur de développement, qui lui tournera sur mon portable dans des containers configurés avec docker-compose.

Configuration DNS

La première étape consiste donc à faire pointer le nom de domaine sous lequel on veut rendre accessible notre environnement de développement vers ce serveur.

Par convention je vais utiliser les noms suivants:

  • my-web-server.net: le nom de domaine de mon serveur web public;
  • dev1.devs.my-web-server.net: le nom de domaine sous lequel l’environnement de développement sera accessible.

L’enregistrement DNS à créer ressemblera à quelque chose comme: *.devs.my-web-server.net CNAME my-web-server.net

Configuration Caddy

Mon serveur web utilise Caddy. Ici je vais utiliser deux fonctionnalités de cet outil particulièrement pratiques:

  1. en déclarant un nom de domaine ouvert sur le port 443, Caddy récupère automatiquement le certificat Letsencrypt!
  2. Un reverse proxy se configure en une seule ligne.

La section de ma configuration Caddy dédiée au tunnel ressemble à ceci:

dev1.devs.my-web-server.net:80,
dev1.devs.my-web-server.net:443 {
    log {
        output file /var/log/caddy/dev1-devs.log
    }
    reverse_proxy {
        to 127.0.0.1:8888
   }
}

Configuration Docker avec le certificat

Lorsque mon application web sera accessible depuis l’extérieur, Caddy va utiliser le certificat généré par Letsencrypt pour sécuriser la connexion. Mais lorsque j’accède à cette même application en local depuis mon portable, c’est au serveur web qui tourne dans Docker de s’en occuper. Pour cela ce serveur a aussi besoin des fichiers du certificat.

Ces fichiers (la clé publique et la clé privée) se trouvent dans le sous répertoire dev1.devs.my-web-server.net du répertoire /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/ Il faut les copier en local, puis les insérer dans le container qui fera tourner le serveur web.

Dans le Dockerfile du serveur web on trouvera donc quelque chose qui ressemble à:

COPY certs/dev1.devs.my-web-server.net.crt /usr/local/apache2/conf/dev1.devs.my-web-server.net.crt
COPY certs/dev1.devs.my-web-server.net.key /usr/local/apache2/conf/dev1.devs.my-web-server.net.key

Dans mon cas particulier l’application en local est servie par un serveur apache. La configuration de ce serveur ressemble à ceci:

<VirtualHost _default_:443>
    DocumentRoot "/var/www/my-app/"
    ServerName dev1.devs.my-web-server.net

    SSLEngine on
    <Directory "/var/www/my-app/">
        AllowOverride All
        Require all granted
    </Directory>

    SSLCertificateFile "/usr/local/apache2/conf/dev1.devs.my-web-server.net.crt"
    SSLCertificateKeyFile "/usr/local/apache2/conf/dev1.devs.my-web-server.net.key"
    Protocols h2 http/1.1
</VirtualHost>

Modification du fichier /etc/hosts

Pour pouvoir travailler localement, sans passer par le proxy, il faut associer l’adresse IP du container apache au nom de domaine dev1.devs.my-web-server.net. On récupère cette adresse avec la commande suivante:

docker inspect \
     --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
    $(docker compose ps -q apache)

Connexion ssh

La dernière étape consiste à créer le tunnel, avec SSH, entre notre machine locale et le serveur web:

ssh -R 8888:127.0.0.1:8351 my-web-server.net

Ici on a:

  • 8351 le port ouvert de notre serveur web dans votre docker-compose;
  • 8888 le port qui est dans la configuration reverse proxy de Caddy.

Et voilà!

Aller plus loin

Il ne semble pas trop difficile de bricoler une solution qui automatise un peu toutes ces étapes. D’ailleurs c’est pour cela que j’ai déclaré le domaine *.devs.my-web-server.net dans ma configuration dns, au lieu de dev1.my-web-server.net. Ainsi je peux ajouter plus facilement d’autres domaines sans modifier mon serveur DNS. Je n’aurais ensuite que les modifications de la configuration Caddy et de docker à programmer. Pour le moment, avec un seul environnement à rendre accessible, je n‘ai pas besoin d’automatiser plus.

Post Scriptum

Après avoir écrit cet article, je découvre un projet qui met en œuvre exactement le même procédé que moi, avec un début d’automatisation.