rebonds_ssh

Rebonds SSH

De nos jours, il est devenu habituel d’avoir ce que l’on appelle un hôte bastion. Ce serveur est généralement dans un sous-réseau public et sert de passerelle SSH vers un sous-réseau privé.

Les hôtes bastions sont essentiellement utilisés pour séparer les activités privées d’une attaque venant de l’extérieur, tout en fournissant un service accessible depuis l’extérieur. En l’occurence, un service SSH est souvent proposé à des prestataires extérieurs pour leur donner la possibilité d’interagir avec une machine avec un pseudo-terminal.

Il est parfois nécessaire de passer successivement par plusieurs hôtes bastions afin d’atteindre le serveur avec lequel on souhaite interagir.

Ainsi, en se connectant en SSH successivement à chaque serveur intermédiaire il est possible d’atteindre le serveur cible. Cependant, il est possible d’automatise l’ensemble des connexions intermédiaires. Pour ce faire, il existe plusieurs méthodes, soit on utilise la fonctionnalité ProxyCommand, qui offre beaucoup de souplesse en contre partie d’une certaine complexité, soit la fonctionnalité plus récente ProxyJump qui est bien plus simple à appréhender.

Dans tous les exemples qui suivront, nous nous placerons en tant que l’utilisateur Bob, avec la configuration de départ suivante :

bob:~$ ls -l ~/.ssh
total 16
-rw-------  1 demo  demo  387 May  1 11:07 id_ed25519
-rw-r--r--  1 demo  demo   85 May  1 11:07 id_ed25519.pub

bob:~$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAcqaPvZXFHgHzqOplzzmRg4AnxUYpFO0r4LrtFGsC/i bob

Pour illustrer nos propros, nous prendrons également l’architecture suivante comme exemple.

Configuration du serveur bastion A :

bob:~$ ssh admin@server_A
The authenticity of host 'server_a (140.82.53.221)' can't be established.
ED25519 key fingerprint is SHA256:FRO3JsAygUYpREAWWJR75wi6VLqIXdHi2uohJ3Fc0k4.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'server_a,140.82.53.221' (ED25519) to the list of known hosts.
Last login: Sat May  1 09:29:36 2021 from 176.160.93.220
admin@a:~$

admin@a:~$ ip a | grep "inet "
    inet 127.0.0.1/8 scope host lo
    inet 140.82.53.221/23 brd 140.82.53.255 scope global dynamic ens0
    inet 10.0.0.1/24 scope global ens1

admin@a:~$ ls -l ~/.ssh/
total 12
-rw-r--r-- 1 admin admin  85 May  1 09:28 authorized_keys
-rw------- 1 admin admin 399 May  1 09:33 id_ed25519
-rw-r--r-- 1 admin admin  90 May  1 09:33 id_ed25519.pub

admin@a:~$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJAKbCinjGw/0BKTZnGP182A2XdvYD5mfTOz8TB06g23 server_a

admin@a:~$ cat ~/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAcqaPvZXFHgHzqOplzzmRg4AnxUYpFO0r4LrtFGsC/i bob

Configuration du serveur B :

admin@a:~$ ssh admin@server_B
The authenticity of host 'server_b (10.0.0.2)' can't be established.
ED25519 key fingerprint is SHA256:KmxCNSOZGgulC9c4BfCwY4Sj3+n3Oe56hvr4Uvlxyv4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'server_b,10.0.0.2' (ED25519) to the list of known hosts.
Last login: Sat May  1 08:56:36 2021 from 176.160.93.220
admin@b:~$

admin@b:~$ ip a | grep "inet "
    inet 127.0.0.1/8 scope host lo
    inet 10.0.0.2/24 scope global ens0

admin@b:~$ ls -l ~/.ssh/
total 12
-rw-r--r-- 1 admin admin  90 May  1 09:34 authorized_keys
-rw------- 1 admin admin 399 May  1 09:34 id_ed25519
-rw-r--r-- 1 admin admin  90 May  1 09:34 id_ed25519.pub

admin@b:~$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOqDONyCJcsoBiKz7r53dvQMgjGaS/OELNq/gfAUDGx4 server_b

admin@b:~$ cat ~/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJAKbCinjGw/0BKTZnGP182A2XdvYD5mfTOz8TB06g23 server_a

Utilisation de ProxyJump

Au lieu de se connecter d’abord en SSH à l’hôte du bastion A, puis d’utiliser à nouveau OpenSSH sur le bastion pour se connecter à l’hôte distant BOpenSSH peut, depuis la version 7.3, créer lui-même les première et deuxième connexions en utilisant ProxyJump. On peut définir des noms d’utilisateur et des ports spécifiques s’ils diffèrent entre les hôtes :

$ ssh -J user@<bastion:port> <user@remote:port>

Cependant, il est important de noter que l’usage exclusif de l’authentification par clés cryptographiques requiert que notre clé publique soit positionné à la fois sur le serveur bastion A et sur le serveur de destination B. Actuellement, ce n’est pas le cas, aussi, nous faisont face à ce message d’erreur :

bob:~$ ssh -J admin@server_A admin@server_B
admin@server_b: Permission denied (publickey,keyboard-interactive).

Ce qui signifie que la premier tunnel SSH a bien été établi, mais que le second échoue à l’authentification. Il est donc primordial de pousser notre clé publique SSH sur l’ensemble des serveurs intermédiaires et sur le serveur de destination si l’on souhaite pouvoir utiliser convenablement la fonctionnalité ProxyJump. Nous verrons plus loin, qu’avec la méthode ProxyCommand, il est possible de récupérer successivement les identités des serveurs intermédiaires afin de ne pas être contraint de pousser notre clé publique SSH sur chaque serveurs.

Nous poussons donc notre clé publique SSH sur le serveur destination B.

bob:~$ cat ~/.ssh/id_ed25519.pub | ssh admin@server_A "cat | ssh admin@server_B \"cat >> .ssh/authorized_keys\""

Nous pouvons à présent nous connecter directement sur le serveur B.

bob:~$ ssh -J admin@server_A admin@server_B
Last login: Sat May  1 09:36:22 2021 from 10.0.0.1
admin@b:~$

admin@b:~$ cat ~/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJAKbCinjGw/0BKTZnGP182A2XdvYD5mfTOz8TB06g23 server_a
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAcqaPvZXFHgHzqOplzzmRg4AnxUYpFO0r4LrtFGsC/i bob

C’est fonctionnalité est très pratique, mais on peut aller encore plus loin en l’utilisant directement depuis le fichier de configuration de notre client OpenSSH.

bob:~$ cat << EOF > ~/.ssh/config
> Host server_A
>   Hostname server_A
>   User admin
>   PubkeyAuthentication yes
>   IdentityFile ~/.ssh/id_ed25519
>
> Host server_B
>   Hostname server_B
>   User admin
>   IdentityFile ~/.ssh/id_ed25519
>   ProxyJump server_A
> EOF

bob:~$ ssh server_B
Last login: Sat May  1 09:59:47 2021 from 10.0.0.1
admin@b:~$

Un avantage indéniable est de pouvoir ici spécifier des identités différentes selon le serveur intermédiaire sur lequel on souhaite rebondir.

Utilisation de ProxyCommand

ProxyJump est la manière simplifiée d’utiliser une fonctionnalité que le client OpenSSH possède depuis longtemps : ProxyCommand. Elle fonctionne en transférant les entrées standard (stdin) et les sorties standard (stdout) de la machine distante à travers les hôtes intermédiaires. Pour ce faire, on doit spécifier la commande qui sera exécuté sur le serveur intermédiaire.

$ ssh -o ProxyCommand="ssh -W %h:%p bastion" user@<remote:port>

Les arguments %h:%p de l’option -W ci-dessus spécifient la transmission des entrées et sorties standard vers l’hôte distant, identifié par %h et le port de l’hôte distant, identifié par %p.

Comme précédemment, il est nécessaire que nous poussions notre clé publique SSH sur le serveur destination B, pour que l’authentification sur le serveur B puisse s’effectuer correctement.

bob:~$ ssh -o ProxyCommand="ssh -W %h:%p admin@server_A" admin@server_B
admin@server_b: Permission denied (publickey,keyboard-interactive).

bob:~$ cat ~/.ssh/id_ed25519.pub | ssh admin@server_A "cat | ssh admin@server_B \"cat >> .ssh/authorized_keys\""

bob:~$ ssh -o ProxyCommand="ssh -W %h:%p admin@server_A" admin@server_B
Last login: Sat May  1 10:07:56 2021 from 10.0.0.1
admin@b:~$

De même que pour ProxyJump il est possible d’écrire un fichier de configuration pour notre client OpenSSH.

bob:~$ cat << EOF > ~/.ssh/config
Host server_A
  Hostname server_A
  User admin
  PubkeyAuthentication yes
  IdentityFile ~/.ssh/id_ed25519

Host server_B
  Hostname server_B
  User admin
  IdentityFile ~/.ssh/id_ed25519
  ProxyCommand ssh -W %h:%p server_A
EOF

bob:~$ ssh server_B
Last login: Sat May  1 13:46:28 2021 from 10.0.0.1
admin@b:~$

On peut là aussi spécifier des identités différentes selon le serveur intermédiaire sur lequel on souhaite rebondir. Très pratique.

Maintenant, voyons comment éviter d’avoir à pousser notre clé publique SSH sur l’ensemble des serveurs qui se trouvent après le premier serveur de rebond. L’idée, c’est de tirer partie des identités déjà présente sur chaque intermédiaire. Pour se faire, les seules conditions que nous devons remplir sont :

  • notre clé publique SSH doit être connu du premier serveur de rebond
  • chaque intermédiaire doit connaître au moins une identité d’un serveur qui le précède dans la chaîne de connexion
  • les serveurs pour lesquels on souhaite tirer partie de leur identité doivent l’option AllowAgentForwarding activée
  • l’agent OpenSSH soit lancé sur notre machine

On supprime donc notre configuration précédente, ainsi que notre clé publique SSH sur le serveur de destination B.

bob:~$ ssh server_B sed -i '/bob/d' .ssh/authorized_keys

bob:~$ rm ~/.ssh/config

Partant de la configuration initiale, nous savons que les deux premières conditions sont déjà remplies. Vérifions maintenant que le serveur OpenSSH du serveur A ait bien l’option AllowAgentForwarding activée.

bob:~$ ssh -tt admin@server_A "sudo sshd -T | grep allowagentforwarding"
[sudo] password for admin:
allowagentforwarding yes
Connection to server_a closed.

On lance notre agent OpenSSH.

$ eval `ssh-agent -s`
Agent pid 71349

$ ssh-add
Identity added: /home/demo/.ssh/id_ed25519 (bob)

bob:~$ ssh-add -l
256 SHA256:nvFWYoiZu5B2Lkc1mz5T2MfvGL77YMxaSh6g5HDl6Ko bob (ED25519)

On peut maintenant se connecter directement au serveur B en profitant de l’identité du serveur A.

bob:~$ ssh -o ProxyCommand="ssh -o ForwardAgent=yes admin@server_A 'ssh-add && nc %h %p'" admin@server_B
Identity added: /home/admin/.ssh/id_ed25519 (server_a)
Last login: Sun May  2 19:09:40 2021 from 10.0.0.1
admin@b:~$

Cependant, comme on peut le voir, l’identité du serveur A a été ajouté à notre agent OpenSSH local. On peut facilement le vérifier.

bob:~$ ssh-add -l
256 SHA256:nvFWYoiZu5B2Lkc1mz5T2MfvGL77YMxaSh6g5HDl6Ko bob (ED25519)
256 SHA256:0TX2zjs3UnwFyC1z0/syQ/qnq7Ora1rDVdpEqS0VFaY server_a (ED25519)

Il est primordial de comprendre ici que la clé privée SSH du serveur A n’est pas redescendu jusque notre machine, seulement sa capacité d’authentification. C’est quelque chose qui faut prendre en compte, car si il s’agit de la capacité d’authentication bastion d’un partenaire, il serait cohérent que celui-ci refuse que celle-ci sorte de son périmètre.

Nous verrons dans un prochain article comment une mauvaise utilisation de la redirection de port avec OpenSSH peut entraîner la compromission de notre propre système d’information.

Partage

Laisser un commentaire

Inscrivez-vous à la newsletter Syloé !

Recevez gratuitement les analyses de nos experts