URL: https://linuxfr.org/users/bouvier-pierrick/journaux/tunneling-ssh-conteneurise Title: Tunneling SSH conteneurisé Authors: Pierrick Bouvier Date: 2020年01月04日T02:05:16+01:00 License: CC By-SA Tags: docker, ssh et tunneling Score: 8 Cher journal, j'aimerais te présenter un petit projet pour monter très facilement ton propre tunnel SSH à la maison, comme un grand: https://github.com/second-reality/reverse-tunnel Pour résumer, cela permet de faire transiter un trafic donné via ssh. Cela offre différentes possibilités: - utiliser un protocole non sécurisé (comme ftp) sur un réseau ouvert, en passant via une machine relai, sans risque d'interception. - accéder à une machine non disponible publiquement sur le net, via une machine relai (publique). - crée un ersatz de VPN pour accéder à des données bloquées dans ton pays (si tu disposes d'un serveur à l'étranger). - et bien d'autres choses... Le cas qui m'intéresse, c'est le deuxième. À savoir, accéder à la machine du boulot depuis chez moi, facilement. Voici un résumé bien plus complet: https://wiki.korben.info/Tunnel_SSH --------------------------------------------------------- # Exemples ## Accès à une machine derrière un relai Imaginons que je possède une `machine_publique` accessible sur le net à l'adresse 80.80.80.80 ``` moule@machine_publique> ./server.sh 6666 myserver:22 ----------------------------------------------------- Running ssh server as moule Listening on port 6666 Authorized destinations: myserver:22 Authorized keys file: /home/user/.ssh/authorized_keys ----------------------------------------------------- Server listening on 0.0.0.0 port 6666. ``` Depuis n'importe où sur le net, on ouvre le tunnel: ``` user@some_machine> ./access.sh moule 80.80.80.80 6666 10000 myserver 22 ``` Et, depuis un autre terminal: ``` user@some_machine> ssh -p 10000 localhost Welcome to the world of Moules! user@myserver> ``` Il faut bien sûr que la clé publique de `user` soit dans les `authorized_keys` de `moule` sur `machine_publique` et sur `myserver`. ## Reverse tunneling for fun! On imagine maintenant qu'on désire accéder à une machine `work` depuis `home` (ip 95.95.95.95). Le port 6666 de `home` est ouvert sur le net. ``` moule@home> ./server.sh 6666 localhost:* ``` Depuis le boulot: ``` serious@work> ./give_access.sh moule 95.95.95.95 6666 10000 localhost 22 ``` Un peu plus tard, en rentrant: ``` moule@home> ./access.sh moule localhost 6666 15000 localhost 10000 moule@home> ssh serious@localhost -p 15000 serious@work> ``` On peut donc remonter le tunnel créé depuis `work`. C'est ce qu'on appelle du reverse tunneling. Et c'est précisément ce qui m'intéressait au départ! ## Autres possibilités On peut bien sûr accéder à autre chose qu'un serveur ssh (redirection http, imap, ...) ## La sécurité par l'exemple Pour illustrer à quoi sert la restriction de destinations, voici un exemple (entièrement en local) ouvrant un tunnel vers le port 22 de github.com. ``` user@pc> ./server.sh 6666 localhost:* ``` ``` user@pc> ./access.sh user localhost 6667 10000 github.com 22 ``` On voit apparaître sur le terminal du serveur: ``` Postponed publickey for user from 172.17.0.1 port 37252 ssh2 [preauth] Accepted publickey for user from 172.17.0.1 port 37252 ssh2: RSA ``` ``` user@pc> ssh git@localhost -p 10000 ssh_exchange_identification: read: Connection reset by peer ``` Sur le terminal du serveur: ``` Received request to connect to host github.com port 22, but the request was denied. ``` Sur le terminal ouvrant l'accès: ``` channel 1: open failed: administratively prohibited: open failed ``` On refait cette fois l'expérience en autorisant la destination github.com:22 ``` user@pc> ./server.sh 6666 localhost:* github.com:22 ``` ``` user@pc> ssh git@localhost -p 10000 Hi second-reality! You've successfully authenticated, but GitHub does not provide shell access. ``` Cette fois, cela fonctionne bien! Ainsi, on possède un contrôle très fin sur ce qui peut-être ouvert depuis une machine extérieure. --------------------------------------------------------- # Description La petite originalité de ce projet, c'est l'utilisation de Docker pour conteneuriser le serveur SSH. Je voulais une solution qui: - soit techniquement sûre - soit simple et sans installation - ne dépende pas d'une distribution particulière - même en cas d'intrusion par un attaquant, que celui-ci ne puisse pas accéder à mes données, clés, ou celles d'une machine ayant ouvert un tunnel. - ne demande pas de bidouiller mon sshd_config - n'expose pas le serveur ssh de ma machine sur internet. - que mon serveur ssh ne soit pas nécessairement executé par root - permette de contrôler finement les destinations des tunnels Aussi étonnant que celui puisse paraître, en dehors de quelques projets similaires sur Github (pour lesquels j'ai une confiance assez réduite), je n'ai pas vraiment trouvé d'outil dédié. Quelques heures plus tard, le résultat est: - un seul port est exposé sur Internet (redirection sur ma freebox), celui publié par le conteneur. - les destinations des tunnels sont explicitement nommées - J'ouvre mon serveur au besoin (et mon client essaye continuellement de s'y connecter). - j'ai appris quelques trucs sur Docker et OpenSSH au passage. Fichiers intéressants: - le Dockerfile, contenant la config du serveur SSH: https://github.com/second-reality/reverse-tunnel/blob/master/Dockerfile ```shell # run sshd on port $SSH_PORT, only allowing $AUTHORIZED_USER to connect # and only authorizing forwarding to $AUTHORIZED_DESTINATIONS # User running this is the one running the container ENTRYPOINT /usr/sbin/sshd -p "$SSH_PORT" -D\ # show log of connections on stderr -e\ # keep clients alive -o ClientAliveInterval=180 -o ClientAliveCountMax=2\ # restrict local tunneling to only some destinations # This is the heart of security! -o PermitOpen="$AUTHORIZED_DESTINATIONS"\ # allow anyone to access forwarded ports and not only localhost -o GatewayPorts=yes\ # only allow a specific user to connect (the one running container) -o AllowUsers="$AUTHORIZED_USER"\ # prevent execution of any command -o ForceCommand="/No/Shell/Available/ONLY_TUNNELING_IS_POSSIBLE"\ # other params (man sshd_config) -o UsePAM=no\ -o PasswordAuthentication=no\ -o AllowStreamLocalForwarding=no\ -o AllowAgentForwarding=no\ -o AllowTcpForwarding=yes\ -o AuthenticationMethods=publickey\ -o MaxAuthTries=1\ -o PermitRootLogin=no\ -o PermitTunnel=yes\ -o PrintMotd=no\ -o PidFile=none\ ``` - le server.sh, contenant la commande de lancement du conteneur: https://github.com/second-reality/reverse-tunnel/blob/master/server.sh ```shell docker run --rm=true -p $ssh_port:$ssh_port $terminal\ -v $authorized_keys:$authorized_keys:ro\ -v /etc/passwd:/etc/passwd:ro \ -v /etc/group:/etc/group:ro \ -e SSH_PORT=$ssh_port\ -e AUTHORIZED_DESTINATIONS="$authorized_destinations"\ -e AUTHORIZED_USER="$USER"\ -u $(id -u)\ reverse-tunnel || die "running server failed" ``` ----------------------------------------------------------------------------------- # Pièges Il existe plusieurs pièges possibles dans un projet comme celui-ci: - oublier de bloquer la possibilité d'exécuter une commande sur le serveur SSH. Cela peut augmenter la surface d'attaque en cas d'intrusion. - oublier de bloquer les destinations: Si on ne limite pas les destinations, on peut accéder à un port de la machine hôte (et à tout son LAN globalement), hors du conteneur! (oups!) - Des petites blagues (classiques avec Docker) sur les droits des fichiers accédés dans le conteneur. ---------------------------------------------------------------------------------- # Conclusion N'hésitez pas à jouer en local, ça fonctionne de la même façon! Il est notamment intéressant de jouer sur les destinations autorisées. **Note: Ouvrir un tunnel SSH vers une machine personnelle depuis votre lieu de travail peut-être considéré comme une faute professionnelle. Il s'agit d'une brèche de sécurité importante dans l'infrastructure de votre entreprise.** Si vous avez des retours, des critiques, ou des suggestions, je suis preneur! Si vous avez besoin d'un coup de main pour l'utilisation, je peux aussi aider. Happy tunneling!