Reverse ssh : Accéder à un serveur derrière un NAT - Firewall

De wikiGite

Avec au moins un accès externe

Un des serveurs du réseau local protégé par un firewall est accessible en SSH depuis l'extérieur. Lui-même a ensuite accès aux autres serveurs du réseau local.

On peut, en ligne de commande, passer par ce serveur pour en atteindre un autre (Tunnel_SSH_via_un_serveur_intermédiaire).

On peut aussi se servir de du paramètre ProxyCommand, dans ce cas le plus simple est de préconfigurer la connexion par le fichier .ssh/config du poste local. On y déclare :

Host serveur_intermediaire
   Hostname serveur_intermediaire.domain.com
   User root
   ForwardAgent yes
Host serveur_de_destination
   Hostname 192.168.0.200
   User admin
   ProxyCommand ssh serveur_intermediaire -q -W %h:%p

"ForwardAgent" : transférer l'agent local (donc notre config) sur le reverse proxy "Hostname 192.168.0.200" : pour le serveur final on utilise l'adresse réseau interne, celle connue du serveur_intermediaire

On a plus qu'à se connecter par

ssh serveur_de_destination

et l'agent + proxyCommand font le reste.

Sans aucun accès externe

Reverse.png

Le serveur A se trouvant derriere le par-feu créé un tunnel vers le serveur B. Depuis B on se connecte au serveur A au travers du tunnel ssh.

Prérequis

Ajouter cette ligne dans /etc/ssh/sshd_config sur B:

AllowTcpForwarding yes

Par sécurité créer un utilisateur dédié au tunnel sur B :

adduser userssh

Reverse ssh

Le port 22222 de l'exemple suivant doit se trouver entre 1024 et 65535. Il faut evidement tenir une liste des ports associés aux machines.

Créez le tunnel sur le serveur A :

ssh -NR 22222:localhost:22 userssh@serveurB

Se connecter au tunnel depuis le serveur B

ssh -p 22222 rootA@127.0.0.1


Lancement automatique sur le serveur A avec autossh

aptitude install autossh

Générer une paire de clef avec root

ssh-keygen -t dsa

Faire un echange de clef avec le serveur B:

ssh-copy-id -i /root/.ssh/id_dsa.pub userssh@serveurB


En l’exécutant au démarrage (rc.local)

ajouter dans /etc/rc.local :

autossh -i /root/.ssh/id_dsa -NR 22222:localhost:22 userssh@serveurB &


En créant un script d'init

SYSVINIT

Créer un script dans /etc/init.d/autosshd et remplacer les variables REMOTE_ADDR et LISTEN_PORT :

#!/bin/bash
 
### BEGIN INIT INFO
# Provides:          autosshd
# Required-Start:    
# Required-Stop:     
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the autossh daemon
# Description:       starts autossh
### END INIT INFO
 
REMOTE_USER="userssh"
REMOTE_ADDR="test.systea.fr"
LISTEN_PORT="22222"
 
DAEMON="/usr/bin/autossh"
DESC="Autossh daemon"
LOGFILE="/var/log/autossh.log"
PIDFILE="/tmp/$REMOTE_USER-$REMOTE_ADDR.pid"
 
export AUTOSSH_PIDFILE=/tmp/$REMOTE_USER-$REMOTE_ADDR.pid
 
test -f $DAEMON || exit 0
 
is_running() {
    if [ -f $PIDFILE ]; then
        PID=`cat $PIDFILE`
        if [ -n "$PID" ]; then
            return 0
        else
            return 1
        fi
    else
        return 1
    fi
}
 
start_autossh() {
    if ! is_running; then
        echo -n "Starting $DESC"
        $DAEMON -i /root/.ssh/id_dsa -NR $LISTEN_PORT:localhost:22 $REMOTE_USER@$REMOTE_ADDR >> $LOGFILE 2>&1 &
        sleep 1;
        if is_running; then
            echo " [DONE] : Running @ pid $PID "
        else
            echo '[FAIL]';
        fi
    else
        echo "[FAIL] Already running (pid $PID)"
    fi
}
 
stop_autossh() {
    if is_running; then
        echo -n "Stopping $DESC: "
        kill -s SIGTERM $PID
        echo "[DONE]"
    else
        echo "[FAIL] : Not running "
    fi
    [ -f $APIDFILE ] && rm -f $PIDFILE
}
 
case "$1" in
    start)
        start_autossh
    ;;
    stop)
        stop_autossh
    ;;
    force-reload|restart)
        stop_autossh
        start_autossh
    ;;
    status)
        if is_running; then
            echo "$DESC: running (pid $PID)"
            exit 0
        else
            echo "$DESC: not running"
            [ -f $PIDFILE ] && exit 1 || exit 3
        fi
    ;;
    log)
        if [ -f $LOGFILE ]; then
            tail $LOGFILE
        else
            echo "[FAIL] : log file '$LOGFILE' does't exist"
        fi
    ;;
    *)
        echo "Usage: $0 {start|stop|restart|force-reload|status|log}"
        exit 3
    ;;
esac
 
exit 0

Autoriser l'execution du script :

chmod +x /etc/init.d/autosshd

Ajouter le script au démarage :

update-rc.d autosshd defaults

SYSTEMD

En créant un script /etc/systemd/system/foo-autossh.service

[Unit]
 Description=AutoSSH service for a reverse tunnel from foo to bar
 After=network.target
 
[Service]
 User=foo
 ExecStart=autossh -M 0 -f -N -T -q -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -R 22222:localhost:22 userssh@serveurB
 Restart=always
 RestartSec=60
 
[Install]
 WantedBy=multi-user.target

Se connecter à d'autres ports

On souhaite par exemple se connecter à un serveur web se trouvant sur A.

Sur A :

ssh -NR 22280:localhost:80 userssh@serveurB

Sur B :

firefox "http://127.0.0.1:22280"

Se connecter à partir d'un poste client

Poste ---> INTERNET ---> serveurB ---> INTERNET ---> serveurA

Sur le poste on lance :

ssh root@serveurB -t ssh -p 22222 root@127.0.0.1