Read in another language: English 🇬🇧, Français 🇫🇷.
- Quoi ? Pourquoi ?
- Installer Merlin sur son routeur
- Activer SSH et partition JFFS
- Installer Entware
- Utiliser le DynHost d'Ovh sur son routeur
- Installer nginx
- Configurer nginx
- Obtenir un certificat Let's Encrypt
- Conclusion
- Sources
- Bonus
Un reverse proxy ou proxy inverse est un petit serveur web qui permet d'accéder aux interfaces utilisateur situées derrière lui, par exemple : interfaces web de caméras, serveurs multimédia, Nas, calendrier ou email auto-hébergées, etc. Le but est de pouvoir accéder aux différentes ressources depuis l'extérieur, sans avoir à utiliser un VPN. VPN et reverse proxy ne s'excluent pas pour autant, le proxy n'étant vraiment utile que pour les interfaces web. De plus, le VPN permet une sécurité accrue, lors de l'utilisation de wifi gratuits par exemple.
Le reverse proxy est sécurisable. Il suffit pour cela d'utiliser un certificat, la connexion sera chiffrée entre votre ordinateur extérieur et le proxy. Et avec Let's Encrypt, il est possible d'avoir un certificat reconnu par les navigateurs et d'avoir le petit cadenas vert ! D'autre part, Let's Encrypt a lancé en 2018 le support des certificats wildcard : il est désormais possible de demander un certificat pour "*.domaine.com" plutôt que pour "pouet.domaine.com, pouet2.domaine.com, ...".
J'ai mis en place cette configuration car je possède un routeur Asus - un AC86U - derrière ma box, il est là pour combler les manques de la box fournie par mon opérateur : DNS personnalisé, firewall et DHCP plus avancés et plus fins, serveur et client VPN, dnsmasq, etc. Et ce routeur me permet également de faire tourner nginx - que j'utilise justement comme reverse proxy - et d'utiliser un nom de domaine loué chez Ovh avec mon adresse IP dynamique (DynHost).
J'ai fait ce tuto à la base pour me souvenir de ce que j'avais fait. Alors, pourquoi ne pas partager ?
Le firmware (programme intégré au matériel) Merlin est une modification du firmware officiel d'Asus. Il a l'avantage de proposer pas mal d'améliorations sans pour autant supprimer l'interface graphique bien agréable d'Asus. Il permet également d'utiliser Entware - j'y reviendrais juste un peu après.
Installer Merlin se fait très simplement, comme une mise à jour du routeur, il y a énormément de tutos en ligne, en voici un, très clair : http://tex.fr/firmware-asuswrt-merlin/
Il n'y a pas vraiment de risque à utiliser Merlin, car il est très facile de revenir en arrière, et de réinstaller le firmware officiel.
Une fois que le routeur fait tourner Merlin, il faudra se rendre dans Administration > Système, et activer la partition JFFS.
Toujours sur la même page, on active l'accès SSH, et on passe l'interface en https sur le port 8443 :
JFFS est une partition en écriture de la mémoire flash du routeur, ce qui vous permettra de stocker de petits fichiers (comme des scripts) sans avoir besoin d'avoir un disque USB branché. Cet espace survivra au redémarrage. Il sera également disponible assez tôt au démarrage (avant les disques USB). Bref, cette partition est nécessaire pour ce que l'on veut faire.
L'interface graphique du routeur, que l'on atteint avec l'adresse 192.168.1.1, utilise le port 80 par défaut. Sauf que notre reverse proxy aura besoin des ports 80 et 443, on déplace donc l'interface graphique sur le port 8443. Le routeur sera ainsi accessible via https://192.168.1.1:8443, libérant les ports 80 et 443.
Quant à l'accès SSH, il sera nécessaire par la suite, car la quasi-totalité du tuto utilisera un terminal et des lignes de commandes. A titre personnel, sous Windows j'utilise PuTTY.
Entware est un logiciel libre, c'est un gestionnaire de paquets pour les systèmes embarqués, comme les Nas ou les routeurs. Cela permet d'ajouter tout un tas de logiciels normalement indisponibles, comme l'éditeur de texte nano par exemple. L'intérêt d'Entware pour ce tuto, c'est qu'il permet d'installer nginx.
Entware nécessite une clé usb formatée en EXT2, branchée sur le port usb du routeur. Facile si vous avez un ordinateur sous linux. Moins facile sous Windows... Le mieux, est d'utiliser MiniTool Partition Wizard Home Edition si votre PC est sous Windows. Rien de bien complexe : on installe l'application, on clique droit sur sa clé, on supprime la ou les partitions déjà présentes. On re-clique droit et on crée une partition EXT2 d'au moins 2Go. On clique ok, et appliquer.
La clé branchée, on se connecte en SSH au routeur avec PuTTY, et on tape :
entware-setup.sh
Le terminal va afficher :
Info: This script will guide you through the Entware installation.
Info: Script modifies only "entware" folder on the chosen drive,
Info: no other data will be touched. Existing installation will be
Info: replaced with this one. Also some start scripts will be installed,
Info: the old ones will be saved to .entwarejffs_scripts_backup.tgz
Info: Looking for available partitions...
[1] --> /tmp/mnt/sda1
=> Please enter partition number or 0 to exit
On choisit la partition en tapant le chiffre correspondant, et hop. C'est fini.
Note : si votre routeur permet l'utilisation d'entware en version 64bits, un message supplémentaire apparaîtra avant le choix de la partition :
Info: This platform supports both 64bit and 32bit Entware installations.
Info: 64bit support is recommended, but 32bit support may be required
Info: if you are using other 32bit applications.
Info: The 64bit installation is also better optimized for newer kernels.
=> Do you wish to install the 64bit version? (y/n)
Si c'est le cas, répondez "Yes".
La clé branchée, on se connecte en SSH au routeur avec PuTTY, et on tape :
amtm
Le terminal va lancer le script amtm. Il suffit de taper "i" pour lancer le menu d'installation, et ensuite de taper "ep" pour installer entware.
Comme indiqué en introduction, je possède un nom de domaine Ovh, et je souhaite accéder aux différents services que j'héberge chez moi, via cette adresse. Problème, je n'ai pas une ip fixe : si je lie pouet.fr à mon adresse ip, au premier changement d'ip, l'adresse ne pointera plus chez moi. Je vais donc créer des enregistrements chez Ovh et utiliser mon routeur pour mettre à jour l'adresse ip liée. Pour cela, il faut faire une manipulation chez Ovh, et créer un script sur le routeur.
Dans l'espace client Ovh, on se rend sur le domaine que l'on souhaite utiliser, et on clique sur DynHost 1️⃣ , puis sur gérer les accès 2️⃣. Dans la fenêtre qui s'ouvre, on crée un accès 3️⃣
- Le suffixe sera l'identifiant que l'on utilisera dans le script après : mettez ce que vous voulez.
- Le sous-domaine permet d'indiquer quelle sera l'étendue de la mise à jour de l'adresse ip. Personnellement, j'indique "*".
- Et enfin, un mot de passe au choix qui sera utilisé pour le script.
De retour dans la fenêtre Dynhost, on clique sur ajouter un Dynhost 4️⃣ et on ajoute son ip actuelle que l'on a récupérée sur http://myip.dnsomatic.com/ par exemple. Pour le sous-domaine, je ne mets personnellement rien, mais rien n'oblige à faire comme moi.
Enfin, dernière étape, on va créer autant de redirections que ce qu'il y a de services auxquels vous souhaitez accéder. Pour ça, on va dans redirection, et on créée une redirection CNAME vers le domaine dynhost :
A noter qu'il est possible créer une redirection wildcard. Il suffit pour cela de supprimer les redirections CNAME existantes s'il y en a, puis d'ajouter une entrée CNAME dans la zone DNS de *.pouet.fr vers pouet.fr
Pour que le routeur puisse mettre à jour l'adresse ip sur laquelle pointe le domaine, il faut utiliser la fonction DDNS du routeur. Par défaut, une série de fournisseurs comme no-ip est proposée, mais pas Ovh. Il faut donc créer un script personnel. Vous avez de la chance, j'en ai testé et adapté un.
On se connecte au routeur via le terminal, et :
wget https://github.com/pedrom34/TutoAsus/raw/master/Data/asuswrt-ovh-ddns-start.sh -O /jffs/scripts/ddns-start
Puis on édite le script téléchargé.
vi /jffs/scripts/ddns-start
On met à jour les informations d'identification du DynHost Ovh (user & password) que l'on a créé à l'étape 4.1., ainsi que le domaine (pouet.fr). Dans vi, il suffit de taper "i" pour insérer du texte au niveau du curseur.
U=user
P=password
H=domain
Pour quitter vi et sauvegarder le script, on appuie Esc, et on tape "ZZ" sans les guillemets et en majuscules. On rend le script exécutable :
chmod a+x /jffs/scripts/ddns-start
Et on retourne dans l'interface du routeur, dans réseau étendu et DDNS, on active le DynHost custom :
Puis on applique et redémarre.
Tada ! Nous avons un nom de domaine qui pointe sur l'ip de notre routeur ! Et ce, même en cas de changement d'adresse IP !
A noter que le script ddns-start considère par défaut que le routeur est, comme le mien, en double Nat derrière une box. Si ça n'est pas le cas, adaptez le script en rajoutant "#" devant "IP=$(wget..." à la ligne 29 du script.
Bon, maintenant que tout est bon, on installe nginx.
opkg install nginx-ssl
Pourquoi nginx-ssl et pas nginx ? Simplement car nginx tout court n'inclus pas certains modules intéressants pour la sécurité https.
On ajoute des règles dans le firewall pour que nginx puisse écouter les ports 80 et 443 :
vi /jffs/scripts/firewall-start
On tape "i", puis on colle ça dans le script (et comme précédemment, pour quitter vi, on fait Esc, puis "ZZ") :
#!/bin/sh
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
iptables -I INPUT -p tcp --dport 443 -j ACCEPT
Chez moi, le script services-start créé par entware ne démarre pas nginx automatiquement. C'est dû au délai indiqué qui ne permet pas à la clé usb d'être montée. Alors, j'ai changé le script de ça :
#!/bin/sh
RC='/opt/etc/init.d/rc.unslung'
i=30
until [ -x "$RC" ] ; do
i=$(($i-1))
if [ "$i" -lt 1 ] ; then
logger "Could not start Entware"
exit
fi
sleep 1
done
$RC start
À ceci :
#!/bin/sh
RC='/opt/etc/init.d/rc.unslung'
i=60
until [ -x "$RC" ] ; do
i=$(($i-1))
if [ "$i" -lt 1 ] ; then
logger "Could not start Entware"
exit
fi
sleep 1
done
$RC start
Enfin, on rend le tout exécutable :
chmod a+x /jffs/scripts/*
Sans doute la partie la plus complexe car la configuration de nginx dépend complètement des services auxquels vous souhaitez accéder... Quoi qu'il arrive, il faut modifier la configuration présente dans "/opt/etc/nginx/nginx.conf". Ainsi, avec vi :
vi /opt/etc/nginx/nginx.conf
Et quitter vi comme expliqué précédemment pour sauvegarder.
Il existe des tas d'exemples sur internet. Je vous invite, par exemple, à regarder les configurations reverse-proxy de linuxserver.io ici. Un petit exemple avec https exclusivement :
user nobody;
worker_processes 1;
#error_log /opt/var/log/nginx/error.log;
#error_log /opt/var/log/nginx/error.log notice;
#error_log /opt/var/log/nginx/error.log info;
#pid /opt/var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
server_tokens off;
server_names_hash_bucket_size 64;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log /opt/var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
## Compression
gzip on;
gzip_buffers 16 8k;
gzip_comp_level 9;
gzip_http_version 1.1;
gzip_min_length 10;
gzip_types text/plain text/css application/x-javascript text/xml;
gzip_vary on;
gzip_static on; #Needs compilation with gzip_static support
gzip_proxied any;
gzip_disable "MSIE [1-6]\.";
server {
listen 80;
server_name localhost;
location / {
root /opt/share/nginx/html;
index index.html index.htm;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 80;
server_name *.domain.tld;
return 301 https://$host$request_uri;
}
server {
listen 443;
server_name localhost;
ssl on;
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Xss-Protection "1";
add_header Content-Security-Policy "default-src 'self'";
add_header Referrer-Policy strict-origin-when-cross-origin;
add_header Strict-Transport-Security "max-age=31557600; includeSubDomains";
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;
ssl_prefer_server_ciphers on;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate cert.crt;
resolver 1.1.1.1 valid=60s;
resolver_timeout 2s;
location / {
root html;
index index.html index.htm;
}
location /robots.txt {
return 200 "User-agent: *\nDisallow: /";
}
}
include /opt/etc/nginx/sites-enabled/*.conf;
}
Pour le resolver, vers la fin du fichier, il faut indiquer un résolveur DNS. J'ai indiqué ici le DNS de cloudflare, mais il est possible d'utiliser l'adresse ip locale du routeur.
Une fois cette modification faite, il faut créer un fichier .conf par service à proxifier dans le dossier /opt/etc/nginx/sites-enabled/. Par exemple :
vi /opt/etc/nginx/sites-enabled/kodi.domain.tld
Et :
server {
listen 443;
server_name kodi.domain.tld;
ssl on;
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Xss-Protection "1";
add_header Content-Security-Policy "default-src 'self'";
add_header Referrer-Policy strict-origin-when-cross-origin;
add_header Strict-Transport-Security "max-age=31557600; includeSubDomains";
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;
ssl_prefer_server_ciphers on;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate cert.crt;
resolver 1.1.1.1 valid=60s;
resolver_timeout 2s;
location / {
proxy_pass http://192.168.0.10:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $remote_addr;
}
location /robots.txt {
return 200 "User-agent: *\nDisallow: /";
}
}
Il existe de multiples façon pour obtenir un certificat gratuit Let's Encrypt. Pour les routeurs, je trouve que la méthode la plus adaptée est l'utilisation du script acme.sh.
Ce script est fabuleux : il s'adapte à énormément de situations grâce à ses nombreuses options, et il est très léger !
Sur la partie qui suit, j'utilise le script avec mon nom de domaine, via l'API d'Ovh. Si vous n'êtes pas dans cette situation, référez-vous au wiki de acme.sh.
Alors, on commence par télécharger Acme.sh
wget https://github.com/Neilpang/acme.sh/archive/master.zip
Décompresser l'archive. J'ai choisi de décompresser l'archive dans /opt (ce qui correspond à la clé usb branchée sur le routeur). Mais de toutes façons, ce dossier sera supprimé après.
unzip master.zip -d /opt
On va dans le dossier dézippé :
cd /opt/acme.sh-master/
On rend le script exécutable
chmod a+x /opt/acme.sh-master/*
Et on installe le script dans /opt/scripts/acme.sh, l'argument "--home" permet de définir l'emplacement de l'installation.
./acme.sh --install --home "/opt/scripts/acme.sh"
Je configure le script pour qu'il utilise l'api d'Ovh pour créer des champs TXT dans les enregistrements de domaine, justifiant ainsi ma propriété pour Let's Encrypt.
On crée les clés sur https://eu.api.ovh.com/createApp/
Notez bien les informations affichées, puis, dans le terminal, on se rend le dossier acme.sh
cd /opt/scripts/acme.sh
Et on installe les clés d'api Ovh que l'on a eu à l'étape précédente, en tapant dans le terminal (remplacez par vos informations) :
export OVH_AK="Ovh Application Key"
export OVH_AS="Ovh Application Secret"
Ensuite, on génère le certificat, ici, on voit que je demande un certificat wildcard *.domain.tld* ainsi que pour la racine du domaine (domain.tld). ZeroSSL est le serveur acme par défaut, mais je préfère let's encrypt. Pour plus d'information sur le paramètre "--server" cliquez ici.
./acme.sh --server letsencrypt --issue -d *.domain.tld -d domain.tld --dns dns_ovh
Quoi qu'il en soit, cela va échouer, et renvoyer un message d'erreur comme suivant :
Using Ovh endpoint: ovh-eu
Ovh consumer key is empty, Let's get one:
Please open this link to do authentication: https://eu.api.ovh.com/auth/?credentialToken=n0Qbjm6wBdBr2KiSqIuYSEnixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Here is a guide for you: https://github.com/Neilpang/acme.sh/wiki/How-to-use-Ovh-domain-api
Please retry after the authentication is done.
Error add txt for domain:_acme-challenge.*.domain.tld
En effet, il faut se rendre, la première fois uniquement, à l’adresse qu'indique le script pour pouvoir activer l'Api. Sélectionnez "Unlimited" pour la durée de validité.
Ensuite, on recommence, cette fois, ça doit fonctionner :
./acme.sh --issue -d *.domain.tld -d domain.tld --dns dns_ovh
On installe le script dans nginx.
./acme.sh --install-cert -d domain.tld \
--key-file /opt/etc/nginx/cert.key \
--fullchain-file /opt/etc/nginx/cert.crt \
--reloadcmd "/opt/etc/init.d/S80nginx reload"
A noter, que le chemin que j'indique pour la clé et le certificat est celui indiqué dans la configuration de nginx. Indiquez bien la même !
On ajoute ensuite une ligne au fichier services-start pour le renouvellement automatique des certificats, qui se lancera tous les jours à 2h du matin. Pour cela, il faut faire vi /jffs/scripts/services-start et on agjoute cette ligne (pour ça, on tape i, puis Esc et ZZ quand c’est collé) :
cru a "acme.sh" '0 2 * * * /opt/scripts/acme.sh/acme.sh --cron > /dev/null'
On active la mise à jour automatique de acme.sh via la ligne de commande suivante :
./acme.sh --upgrade --auto-upgrade
On peut supprimer le dossier acme.sh-master présent dans jffs.
rm -r /opt/acme.sh-master/
Et on peut enfin lancer nginx :
/opt/etc/init.d/S80nginx start
Note : Il peut arriver que nginx n'ait pas assez de mémoire pour se lancer. Le message d'erreur suivant s'affiche alors :
nginx: [alert] mmap(MAP_ANON|MAP_SHARED, 52428800) failed (12: Cannot allocate memory)
Il suffit de redémarrer le routeur pour régler le problème.
Chez moi, nginx fonctionne très bien, mais une mise à jour du routeur peut supprimer la totalité du travail fait ici. Pensez donc à bien sauvegarder la configuration du routeur et la partition JFFS depuis l'interface du routeur !
Si nginx ne se lance pas, essayez la commande permettant de tester la configuration pour diagnostiquer les soucis :
nginx -t
En cas de modification du fichier de configuration on recharge la configuration, sans redémarrer nginx en faisant :
nginx -s reload
N'étant pas informaticien ou administrateur réseau, si j'ai pu faire tout cela, c'est en montant sur les épaules des géants. N'hésitez pas à consulter ces sites qui m'ont énormément aidé, pour adapter ce modeste tuto à votre situation :
- Sauvageau E. asuswrt-merlin: Enhanced version of Asus’s router firmware (Asuswrt) - Wiki [En ligne]. 2018 [visité le 19 avr 2018]. Disponible sur : https://github.com/RMerl/asuswrt-merlin/wiki
- Neilpang. acme.sh: A pure Unix shell script implementing ACME client protocol - Wiki [En ligne]. 2018 [visité le 19 avr 2018]. Disponible sur : https://github.com/Neilpang/acme.sh/wiki
- Xuplus. 搞定Merlin使用DNS实现Let’s Encrpt证书,使用SSL安全访问后台 - 梅林 - KoolShare - 源于玩家 服务玩家 [En ligne]. Koolshare. 2016 [visité le 19 avr 2018]. Disponible sur : http://koolshare.cn/thread-79146-1-1.html
- HTPC Guides [En ligne]. Mike. Use Afraid Custom Dynamic DNS on Asus Routers; 17 mai 2016 [visité le 19 avr 2018]. Disponible: https://www.htpcguides.com/use-afraid-custom-dynamic-dns-asus-routers/
- Törnqvist G. nginx Reverse Proxy on Asus Merlin [En ligne]. Göran Törnqvist Website. 2015 [visité le 19 avr 2018]. Disponible sur : http://goran.tornqvist.ws/nginx-reverse-proxy-on-asus-merlin/
- jeromeadmin. Firmware Asuswrt-Merlin - T[echnical] eXpertise [En ligne]. T[echnical] eXpertise. 2014 [visité le 19 avr 2018]. Disponible: http://tex.fr/firmware-asuswrt-merlin/
- SSL Configuration Generator [En ligne]. Fondation Mozilla. Generate Mozilla Security Recommended Web Server Configuration Files; [Visité le 23 avr 2018]. Disponible: https://mozilla.github.io/server-side-tls/ssl-config-generator/
- 2018-09-11 : utiliser plus d'un DynDNS: Français 🇫🇷, English 🇬🇧
- 2018-09-18 : configurer nginx avec des liens symboliques : Français 🇫🇷, English 🇬🇧
- 2018-12-05 : utiliser logrotate pour gérer les logs nginx : Français 🇫🇷, English 🇬🇧