Services
Blog
English
Un réseau d’entreprise classique ne permet pas aux admins de se connecter directement aux machines qu’ils administrent, et obligent à la connexion à une machine intermédiaire: le bastion.
On ne va pas pouvoir être exhaustif sur ce guide tant il existent de techniques pour contourner ce type de limitation, nous nous contenterons ici de présenter les diverses options de SSH, ainsi qu’un proxy SOCKS séparé à savoir Dante (sockd), et comment les combiner dans divers scénarios d’entreprise.
Un proxy SOCKS (Socket Secure) est un protocole de niveau session (couche 5 du modèle OSI) qui permet de rediriger n’importe quel flux TCP, et parfois UDP, à travers un serveur intermédiaire sans que l’application cliente ait besoin de connaître la destination finale.
Nous allons voir les techniques suivantes:
Et comment combiner plusieurs de ces techniques.
Le plus simple pour se faire un proxy, quand le serveur distant le permet, est
d’utiliser ssh -D, extrait du man ssh:
-D [bind_address:]port
Active un forwarding de port local « dynamique » au niveau applicatif (Dynamic Port Forwarding). Cela consiste à allouer un socket d’écoute sur le port indiqué sur la machine locale (le client), éventuellement lié à une adresse spécifique via bind_address.
Dès qu’une connexion arrive sur ce port local, elle est transmise à travers le tunnel SSH sécurisé, puis l’application qui se connecte (généralement un navigateur ou tout autre client configuré pour utiliser un proxy SOCKS) décide elle-même de la destination finale.
OpenSSH implémente alors un vrai proxy SOCKS : les protocoles SOCKS4 et SOCKS5 sont tous deux supportés, et ssh joue le rôle de serveur proxy SOCKS qui relaie les requêtes vers les destinations demandées par le client.
[…]
Par défaut, le port d’écoute est lié selon le paramètre GatewayPorts.
Donc, dans un terminal, je me connecte à mon serveur avec -D 1236:
09/12 2025 13:51:23 jpic@jpic ~
$ ssh -D 1236 ci.yourlabs.io
[jpic@ci ~]$
Et dans un autre terminal, je sors toujours avec mon ip:
09/12 2025 13:51:30 jpic@jpic ~
$ curl ipconfig.io
98.110.80.162
Mais, si je passe par le proxy socks avec la variable d’environnement
ALL_PROXY, alors je sors bien avec l’ip de mon serveur distant:
09/12 2025 13:51:44 jpic@jpic ~
$ ALL_PROXY=socks5://localhost:1236 curl ipconfig.io
163.172.69.187
Donc c’est clairement l’option la plus simple et la plus pratique, mais certains serveurs SSH d’entreprise ne le permettent pas de changer la configuration sshd, donc, nous devrons recourrir à d’autres pratiques.
En effet, le serveur peut refuser parce que AllowTcpForwarding est à no
dans sshd_config, auquel cas les manipulations ressembleront à ça:
09/12 2025 14:00:52 jpic@jpic ~
$ ssh -D 1236 ci.yourlabs.io
[jpic@ci ~]$ channel 3: open failed: administratively prohibited: open failed
$ ALL_PROXY=socks5://localhost:1236 curl ipconfig.io
curl: (97) Failed to receive SOCKS response, proxy closed connection
Mais pas de panique, on va utiliser Dante sockd et forwarder le port depuis le bastion vers notre machine locale pour bénéficier de la même fonctionnalité!
dante-server est un package disponnible sur RHEL par exemple, donc on va
pouvoir se l’installer et se faire une petite configuration minimale:
logoutput: stderr
internal: 127.0.0.1 port = 1337
external: <l'ip du bastion>
debug: 2
clientmethod: none
socksmethod: none
client pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
}
socks pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
}
Qu’on met dans un fichier, au hasard dante.conf et qui permet de lancer le
serveur proxy avec la commande suivante:
sockd -f dante.conf
Ce qui ouvre un proxy socks sur le port 1337 du bastion, ce qui nous est complêtement inutile, jusqu’au moment ou on trouve un moyen de forwarder ce port du bastion vers un port local.
Nous allons donc voir 2 techniques permettant d’y arriver, puis, nous joindrons les deux bouts avec un example.
C’est tout simplement l’option -R de la commande ssh, pour forwarded un port
local vers un port distant, voici un extrait de la documentation de cette
option dans man ssh:
-R [bind_address:]port:host:hostport
-R [bind_address:]port:local_socket
-R remote_socket:host:hostport
-R remote_socket:local_socket
-R [bind_address:]port
Indique que les connexions arrivant sur un port TCP donné ou sur un socket Unix côté distant (serveur SSH) doivent être redirigées vers la machine locale (le client SSH).
Cela fonctionne en créant un socket d’écoute sur la machine distante, soit sur un port TCP, soit sur un socket Unix. Dès qu’une connexion arrive sur ce port ou socket distant, elle est transmise à travers le tunnel SSH sécurisé, puis une connexion est établie depuis la machine locale (le client) vers la destination indiquée.
Pour tester, rien de plus simple, on lance un petit serveur en local sur le port 1234 avec la commande:
09/12 2025 13:26:52 jpic@jpic ~
python3 -m http.server 1234
Puis, on se trouve un serveur cible sur lequel on se connecte ainsi:
$ ssh -R 1234:localhost:1234 ci.yourlabs.io
Et on constate que le port 1234 de la machine jpic est maintenant accessible
depuis le port 1234 de la machine ci:
[jpic@ci ~]$ curl -I localhost:1234
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.13.7
Date: Tue, 09 Dec 2025 12:33:39 GMT
Content-type: text/html
Content-Length: 4154
Last-Modified: Thu, 27 Feb 2025 21:49:05 GMT
Cependant, cela peut également échouer:
$ ssh -R 1234:localhost:1234 ci.yourlabs.io
Warning: remote port forwarding failed for listen port 1234
[jpic@ci ~]$
On peut parfois voir le problème en refaisant la commande ssh avec -v:
09/12 2025 13:34:49 jpic@jpic ~
$ ssh -v -R 1234:localhost:1234 ci.yourlabs.io
debug1: OpenSSH_10.2p1, OpenSSL 3.6.0 1 Oct 2025
[...]
debug1: Remote: Server has disabled port forwarding.
debug1: remote forward failure for: listen 1234, connect localhost:1234
Warning: remote port forwarding failed for listen port 1234
debug1: pledge: network
Et là c’est plutot clair: le serveur refuse le forwarding TCP. La solution:
changer AllowTcpForwarding no en AllowTcpForwarding yes dans le fichier
/etc/ssh/sshd_config et recharger le service sshd avec systemctl restart sshd.
Dans le cas d’un réseau d’entreprise, il est possible que ce soit un serveur
sshd de centrify, auquel cas la configuration sera plutot dans
/etc/centrifydc/ssh/sshd_config et le service à recharger centrify-sshd.
Pour éffectuer l’opération inverse, c’est ssh -L:
-L [bind_address:]port:host:hostport
-L [bind_address:]port:remote_socket
-L local_socket:host:hostport
-L local_socket:remote_socket
Indique que les connexions vers un port TCP donné ou un socket Unix sur l’hôte local (le client) doivent être redirigées vers l’hôte et le port spécifiés, ou vers le socket Unix indiqué, côté distant (serveur SSH).
Cela fonctionne en allouant un socket d’écoute, soit sur un port TCP côté local (éventuellement lié à une adresse de bind spécifique avec bind_address), soit sur un socket Unix local.
Dès qu’une connexion arrive sur ce port ou socket local, elle est transmise à travers le tunnel SSH sécurisé, puis une nouvelle connexion est établie depuis la machine distante vers le host:port (hostport) ou vers le socket Unix remote_socket indiqué.
Pour essayer, on se connecte donc ainsi et puis on lance un serveur sur le port 1235 du serveur distant:
$ ssh -L 1235:localhost:1235 ci.yourlabs.io
[jpic@ci ~]$ python -m http.server 1235
Serving HTTP on 0.0.0.0 port 1235 (http://0.0.0.0:1235/) ...
Et comme vous pouvez le voir, on peut donc établir une connection sur le port 1235 de la machine locale pour acceder au port 1235 de la machine distante:
09/12 2025 13:42:33 jpic@jpic ~
$ curl -I localhost:1235
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.13.7
Date: Tue, 09 Dec 2025 12:34:21 GMT
Content-type: text/html; charset=utf-8
Content-Length: 2842
Si AllowTcpForwarding no, alors vous verrez quelque chose comme cela sur le
serveur:
$ ssh -L 1235:localhost:1235 ci.yourlabs.io
[jpic@ci ~]$ python -m http.server 1235
Serving HTTP on 0.0.0.0 port 1235 (http://0.0.0.0:1235/) ...
channel 3: open failed: administratively prohibited: open failed
Et évidemment, une erreur de connection en local:
$ curl -I localhost:1235
curl: (56) Recv failure: Connection reset by peer
Combinons ce que nous avons appris, et voici notre plan:
La première étape sera d’être capables de nous connecter depuis le bastion vers notre machine de dev, à l’envers donc. C’est donc ce que nous allons essayer dans un premier temps en suivant les étapes suivantes sur votre machine de dev:
find /etc -name sshd_config pour trouver le chemin vers notre fichier de
configuration sshd, si vous voyez un chemin comme
/etc/centrifydc/ssh/sshd_config alors c’est probablement celui-là qu’il faut
utiliserAllowTcpForwarding est bien à yes dans ce fichierDenyUser et assurrez vous que votre utilisateur n’est pas en
DenyUser, car si c’est le cas il faudra supprimer la ligneLogLevel à DEBUG, cela vous sera utile si
vous avez besoin de débuggerAuthorizedKeysFile, par défaut c’est
.ssh/authorized_keys, mais cela peut varier dans des configurations
d’entreprisePuis, toujours sur votre machine de dev, créez donc une clef ssh avec la commande suivante par exemple:
ssh-keygen -t ed25519 -a 100
Elle devrait atterir dans ~/.ssh/id_ed25519, et la clef publique dans
~/.ssh/id_ed25519.pub. Il faudra, pour pouvoir se connecter depuis votre
bastion vers votre machine de dev, envoyer la paire de clef sur le bastion,
mais aussi ajouter la clef publique en .pub au fichier AuthorizedKeysFile.
Si AuthorizedKeysFile est à .ssh/authorized_keys, alors procédez ainsi:
cat ~/.ssh/id_ed25519.pub >> .ssh/authorized_keys
# et, très important pour que sshd accepte de lire le fichier, le mettre en
# user-writable seulement:
chmod 600 .ssh/authorized_keys
Mais, si vous avez, par exemple, AuthorizedKeys /etc/ssh/keys/%u.key, alors
il faudra plutot proceder ainsi:
cat ~/.ssh/id_ed25519.pub | sudo tee -a /etc/ssh/keys/${USER}.key
sudo chmod 600 /etc/ssh/keys/${USER}.key
Puis, copiez votre paire de clef sur votre bastion:
scp .ssh/id_ed* mon-bastion:/tmp
Ensuite, vous devez absolument arriver à vous connecter depuis votre bastion vers votre serveur local, essayez donc ainsi:
[ma-dev] $ ip a # regarder l'ip de ma-dev
[ma-dev] $ ssh mon-bastion
[mon-bastion] $ ssh -i /tmp/id_ed25519 mon-uid@mon-ip
Si ça ne marche pas, regardez les journaux du serveur sshd sur votre machine de dev avec l’une des commandes suivantes, selon votre serveur sshd:
sudo journalctl -fu sshd
# ou, dans le cas de centrify:
sudo journalctl -fu centrify-sshd
Si LogLevel est bien à DEBUG sur votre serveur sshd alors l’erreur devrait
être proprement indiquée dans ces journaux.
Cela fait, lançons un multiplexeur comme tmux ou screen, dans un premier terminal lancer le proxy socks:
while :; do sockd -f dante.conf; done
Et dans un deuxième terminal à l’interieur de notre multiplexer, nous connecter vers notre machine en forwardant le port 1337:
while :; do ssh -R 1337:localhost:1337 <notre user>@<notre dev>; done
Ainsi, sur votre machine de dev, vous pourrez faire sortir vos connections par le bastion avec:
export ALL_PROXY=socks5://localhost:1337
C’est un package pip tout mignon qui permet de forwarder tout le traffic de la machine sur un bastion, TCP + UDP + DNS par exemple avec la commande suivante:
sshuttle --dns -r user@serveur 0.0.0.0/0
Je vous laisse regarder le README du projet sshuttle pour découvrir tous les trucs sympas qu’on peut faire!