Services et serveurs

SSH : Créer un tunnel pour rediriger des ports d'une machine à l'autre

Cet article a été mis à jour, vous consultez ici une archive de cet article!
Table des matières

ssh-server-server



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.

Évidement, ce que je montre ici peut s'appliquer à n'importe quel service écoutant sur un port TCP.

Prérequis



Sur la machine de destination :
- installer un serveur OpenSSH (openssh-server)
- 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)


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.