SSH : Créer un tunnel pour rediriger des ports d'une machine à l'autre
Table des matières
Dans cet article, nous allons voir comment créer un tunnel SSH en redirigeant des ports d'une machine sur une autre.
Pour expliquer le contexte :
Le but est, pour le serveur web, de se connecter à la base de données de manière sécurisée.
Nous allons donc créer, avec un tunnel SSH, qui va mapper le port 3306 du serveur MariaDB sur le port 3306 du serveur Web.
Ainsi, le serveur web pourra se connecter à une base de données distante de manière sécurisée.
Sur le serveur Web, on se connectera à la base de données MariaDB avec la commande suivante :
Le port 3306 (par défaut de MySQL) écoutant sur le serveur Web ira contacter le port 3306 du serveur de base de données à travers le tunnel sécurisé !
Évidement, ce que je montre ici peut s'appliquer à n'importe quel service écoutant sur un port TCP.
C'est plus léger que de mettre en oeuvre un OpenVPN ou autre, puisque la partie certificat et tout le bazar n'est pas à faire et seul SSH est nécessaire !
Sur la machine de destination :
Sur la machine source :
Plus d'infos sur l'article SSH Linuxtricks :
- SSH : https://www.linuxtricks.fr/wiki/ssh-installer-et-configurer-un-serveur-ssh
- Screen : https://www.linuxtricks.fr/wiki/screen-plusieurs-terminaux-virtuels-dans-un-seul
Si votre serveur SSH n'écoute pas sur le port 22, par exemple sur le port 2222, modifiez les commandes en ajoutant l'option -p et le numéro du port :
Tout d'abord, sur la machine de destination, nous allons créer un utilisateur "tunnel".
Cet utilisateur n'aura pas de droits d'administration, et n'aura aucun droit concernant l'ouverture d'un shell).
On définit un mot de passe (nous permettra d'envoyer la clé plus tard) :
Sur la machine source, on va créer une clé sans passphrase, pour ouvrir le tunnel sans demande de mot de passe (on arrête avec le RSA SVP !!!) :
On a nos 2 clés :
La clé publique qu'on va envoyer sur le serveur distant : /root/.ssh/id_ecdsa_tunnel.pub
La clé privée qu'on va bien garder : /root/.ssh/id_ecdsa_tunnel
On affecte les bons droits :
On envoie cette clé publique sur le serveur distant :
On teste de se connecter :
On se connecte donc bien sans mot de passe !
Sur le serveur distant, on va sécuriser l'utilisateur tunnel.
On va l'empêcher de pouvoir ouvrir un shell, désactiver la connexion par mot de passe :
Toujours sur le serveur distant, on va éditer le fichier /etc/ssh/sshd_config et on va autoriser notre utilisateur tunnel à ne mapper que le(s) port(s) concerné(s), exemple avec le 3306 ici :
On lancera la commande ss pour voir les ports en écoute sur la machine :
Avant :
Lançons le tunnel dans un screen qu'on va appeler test :
Ici, l'option -N permet de ne pas exécuter de commande (et n'ouvre donc pas de shell)
On détache le screen (Ctrl+A D)
On vérifie que notre port est mappé avec ss :
On peut tester les applicatifs et vérifier que ça marche !
On peut alors quitter le screen en cours et passer à l'étape suivante de l'automatisation.
Quand nous avons testé les commandes précédentes, le fichier known_hosts de root s'est complété.
Le service qui lancera la commande lancera cela en root, donc pas de soucis.
Pour prévenir des coupures réseaux (qui feraient éventuellement sauter le tunnel), ou que le tunnel soit de nouveau opérationnel après un reboot d'un ou l'autre serveur, nous allons créer un script avec une boucle infinie :
J'ai ajouté un peu d'affichage avec echo, mis une pause d'1 seconde afin d'éviter une boucle qui consomme trop de CPU si en face ça ne répond pas, et des options à SSH pour contacter le serveur régulièrement.
On le rend exécutable évidemment :
On peut tester de lancer le script en mode détaché direct avec screen via :
Voici un service, pour systemd, afin de lancer le service.
On va donc créer le fichier /etc/systemd/system/tunnel.service
On recharge le démon :
Et on active le service
Et on démarre le service (s'assurer que le screen est fermé pour pas lancer 2 connexions) :
Avec OpenRC, on va utiliser le service local.
S'il n'est pas activé, on l'active :
On va créer un fichier /etc/local.d/00-tunnel.start
On le rend exécutable :
On redémarre le service local ou on redémarre la machine.
Introduction
Dans cet article, nous allons voir comment créer un tunnel SSH en redirigeant des ports d'une machine sur une autre.
Pour expliquer le contexte :
Code :
Machine source Machine dest
|----------------| |----------------|
| Serveur WEB |_________| Serveur BDD |
| 192.168.21.159 | | 192.168.21.158 |
|----------------| |----------------|
Le but est, pour le serveur web, de se connecter à la base de données de manière sécurisée.
Nous allons donc créer, avec un tunnel SSH, qui va mapper le port 3306 du serveur MariaDB sur le port 3306 du serveur Web.
Ainsi, le serveur web pourra se connecter à une base de données distante de manière sécurisée.
Sur le serveur Web, on se connectera à la base de données MariaDB avec la commande suivante :
Code BASH :
mysql -u root -p -h 127.0.0.1
Le port 3306 (par défaut de MySQL) écoutant sur le serveur Web ira contacter le port 3306 du serveur de base de données à travers le tunnel sécurisé !
Évidement, ce que je montre ici peut s'appliquer à n'importe quel service écoutant sur un port TCP.
C'est plus léger que de mettre en oeuvre un OpenVPN ou autre, puisque la partie certificat et tout le bazar n'est pas à faire et seul SSH est nécessaire !
Prérequis
Sur la machine de destination :
- installer un serveur OpenSSH (openssh-server)
- le pare-feu doit être ouvert sur le port SSH concerné
- le pare-feu doit être ouvert sur le port SSH concerné
Sur la machine source :
- installer le client OpenSSH. (openssh-clients)
- installer un multiplexeur de terminal (screen)
- installer un multiplexeur de terminal (screen)
Plus d'infos sur l'article SSH Linuxtricks :
- SSH : https://www.linuxtricks.fr/wiki/ssh-installer-et-configurer-un-serveur-ssh
- Screen : https://www.linuxtricks.fr/wiki/screen-plusieurs-terminaux-virtuels-dans-un-seul
Si votre serveur SSH n'écoute pas sur le port 22, par exemple sur le port 2222, modifiez les commandes en ajoutant l'option -p et le numéro du port :
Code BASH :
ssh -p 2222 ... ssh-copy-id -p 2222 ...
Création de l'utilisateur tunnel
Tout d'abord, sur la machine de destination, nous allons créer un utilisateur "tunnel".
Cet utilisateur n'aura pas de droits d'administration, et n'aura aucun droit concernant l'ouverture d'un shell).
Code BASH :
useradd tunnel -m -s /bin/bash
On définit un mot de passe (nous permettra d'envoyer la clé plus tard) :
Code BASH :
passwd tunnel
Création de la clé SSH
Sur la machine source, on va créer une clé sans passphrase, pour ouvrir le tunnel sans demande de mot de passe (on arrête avec le RSA SVP !!!) :
Code BASH :
ssh-keygen -t ecdsa -b 521 -f /root/.ssh/id_ecdsa_tunnel
On a nos 2 clés :
La clé publique qu'on va envoyer sur le serveur distant : /root/.ssh/id_ecdsa_tunnel.pub
La clé privée qu'on va bien garder : /root/.ssh/id_ecdsa_tunnel
On affecte les bons droits :
Code BASH :
chmod 600 /root/.ssh/id_ecdsa_tunnel chmod 600 /root/.ssh/id_ecdsa_tunnel.pub
On envoie cette clé publique sur le serveur distant :
Code BASH :
ssh-copy-id -i /root/.ssh/id_ecdsa_tunnel.pub tunnel@192.168.21.158
On teste de se connecter :
Code BASH :
ssh -i /root/.ssh/id_ecdsa_tunnel.pub tunnel@192.168.21.158
On se connecte donc bien sans mot de passe !
Sécurisation de l'utilisateur tunnel
Sur le serveur distant, on va sécuriser l'utilisateur tunnel.
On va l'empêcher de pouvoir ouvrir un shell, désactiver la connexion par mot de passe :
Code BASH :
usermod -s /sbin/nologin tunnel
Code BASH :
passwd -l tunnel
Toujours sur le serveur distant, on va éditer le fichier /etc/ssh/sshd_config et on va autoriser notre utilisateur tunnel à ne mapper que le(s) port(s) concerné(s), exemple avec le 3306 ici :
Code :
Match User tunnel
AllowTcpForwarding yes
PermitOpen 127.0.0.1:3306
X11Forwarding no
AllowAgentForwarding no
Tester que ça fonctionne
On lancera la commande ss pour voir les ports en écoute sur la machine :
Code BASH :
ss -tuln
Avant :
Code TEXT :
NetidState Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 0 0 192.168.21.159%eth0:68 0.0.0.0:* tcp LISTEN 0 511 *:80 *:*
Lançons le tunnel dans un screen qu'on va appeler test :
Code BASH :
screen -S test
Code BASH :
ssh -i /root/.ssh/id_ecdsa_tunnel -L 3306:127.0.0.1:3306 -N tunnel@192.168.21.158
Ici, l'option -N permet de ne pas exécuter de commande (et n'ouvre donc pas de shell)
On détache le screen (Ctrl+A D)
On vérifie que notre port est mappé avec ss :
Code TEXT :
NetidState Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 0 0 192.168.21.159%eth0:68 0.0.0.0:* tcp LISTEN 0 128 127.0.0.1:3306 0.0.0.0:* tcp LISTEN 0 511 *:80 *:* tcp LISTEN 0 128 [::1]:3306 [::]:*
On peut tester les applicatifs et vérifier que ça marche !
On peut alors quitter le screen en cours et passer à l'étape suivante de l'automatisation.
Ouvrir le tunnel en automatique
Quand nous avons testé les commandes précédentes, le fichier known_hosts de root s'est complété.
Le service qui lancera la commande lancera cela en root, donc pas de soucis.
Création du script
Pour prévenir des coupures réseaux (qui feraient éventuellement sauter le tunnel), ou que le tunnel soit de nouveau opérationnel après un reboot d'un ou l'autre serveur, nous allons créer un script avec une boucle infinie :
Code BASH :
vi /root/tunnel.sh
Code BASH :
#! /bin/bash while : do echo "Ouverture du tunnel $(date)" ssh -i /root/.ssh/id_ecdsa_tunnel -L 3306:127.0.0.1:3306 -N -o ServerAliveInterval=5 -o ServerAliveCountMax=2 tunnel@192.168.21.158 echo "Fermeture du tunnel $(date)" sleep 1 done
J'ai ajouté un peu d'affichage avec echo, mis une pause d'1 seconde afin d'éviter une boucle qui consomme trop de CPU si en face ça ne répond pas, et des options à SSH pour contacter le serveur régulièrement.
On le rend exécutable évidemment :
Code BASH :
chmod +x /root/tunnel.sh
On peut tester de lancer le script en mode détaché direct avec screen via :
Code BASH :
screen -dmS tunnel /root/tunnel.sh
Service systemd
Voici un service, pour systemd, afin de lancer le service.
On va donc créer le fichier /etc/systemd/system/tunnel.service
Code BASH :
vi /etc/systemd/system/tunnel.service
Code BASH :
[Unit] Description=Démarrer le tunnel ConditionFileIsExecutable=/usr/bin/screen After=network.target [Service] Type=forking ExecStart=screen -dmS tunnel /root/tunnel.sh RemainAfterExit=yes [Install] WantedBy=multi-user.target
On recharge le démon :
Code BASH :
systemctl --system daemon-reload
Et on active le service
Code BASH :
systemctl enable tunnel.service
Et on démarre le service (s'assurer que le screen est fermé pour pas lancer 2 connexions) :
Code BASH :
systemctl start tunnel.service
Service OpenRC
Avec OpenRC, on va utiliser le service local.
S'il n'est pas activé, on l'active :
Code BASH :
rc-update add local
On va créer un fichier /etc/local.d/00-tunnel.start
Code BASH :
vi /etc/local.d/00-tunnel.start
Code BASH :
#! /bin/bash screen -dmS tunnel /root/tunnel.sh
On le rend exécutable :
Code BASH :
chmod +x /etc/local.d/00-tunnel.start
On redémarre le service local ou on redémarre la machine.