PaSSHport, bastion SSH

GNU/Linux Magazine n° 219 | octobre 2018 | Raphaël Berlamont
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
Passée une masse critique, la gestion des accès à vos serveurs et briques réseaux peut s’avérer fastidieuse. Qui a accès à quoi ? Qui a fait quoi ? Il existe plusieurs solutions plus ou moins onéreuses et complexes, comme adminbastion de Wallix, Balabit, Cyberark, Ekran… Nous allons voir ici la solution libre PaSSHport, que l'on peut voir comme un bastion SSH, qui contrôle, centralise, et enregistre les sessions SSH du S.I.

Jeudi matin, 10h, à la machine à café, devant mon ristretto au goût suspect, j’échange avec l’admin réseau de la boîte… On trolle paisiblement sur IPv6 quand soudain, le « chef » débarque ! Entre la colère et l’inquiétude, il s’exclame : « On a une merde : des bases ont été vidées, on pense qu’un presta (NDR c’est toujours la faute du presta…) qui ne devait pas avoir accès au serveur, a fait une boulette. ». Je passe alors ma matinée et mon après-midi à remettre en fonction le bousin, au lieu de bouquiner sur Kubernetes, sans comprendre ni ce qui a pu se passer, ni déterminer qui a pu être à l’origine de cette destruction. Je ronge mon frein et me promets une chose : ça n’arrivera plus, j’installe PaSSHport.

1. La stratégie de mise en place

Dans PaSSHport, les serveurs, les switchs, les routeurs (et tout ce qui se contrôle par SSH avec certificat), s'appellent des targets. PaSSHport permet de regrouper ces targets dans des targetgroups, en fonction de leur utilité. Ceci permet de fournir un accès à l’ensemble d’un groupe de targets au lieu de le faire unitairement. Pareil pour les utilisateurs, que PaSSHport appelle users, que l'on peut regrouper dans des usergroups.

Je vais donc organiser mes targets dans trois groupes distincts : Network, Databases et Unices, dans lesquels je mettrai respectivement tous les périphériques réseaux comme les switchs, les routeurs, et les bornes Wi-Fi, les serveurs de bases de données, et enfin les Linux/Unix. On voit déjà ici qu'il y aura quelques croisements, que l'on verra en détail par la suite.

Mes utilisateurs seront classés dans le même esprit, par périmètre de responsabilité : Network-admins, DBA, Unices-admins, et Web-dev.

Un petit récap' est posé à plat en figure 1.

Fig. 1 : On ne détaillera pas la configuration de l'ensemble des éléments ci-dessus, mais ce schéma permet, à titre d'exemple, de se faire une idée de l'organisation des accès SSH via PaSSHport.

À gauche se trouvent les différents utilisateurs, avec les groupes dans lesquels j'ai décidé de les assigner. Au centre, PaSSHport dont nous allons voir l'installation et la configuration dans les sections suivantes. À droite, les targets, classées dans les targetgroups expliqués précédemment.

2. L'installation

Bonne nouvelle, PaSSHport est un logiciel libre en GPLv3, disponible sur GitHub [1]. Ça ne nous coûtera pas un rond en licence, mon chef étant prompt à grimacer dès qu’il s’agit de faire une dépense. En cas de pépin, il y a une société française derrière pour le support [2].

Mon socle est une VM avec 40Gio de disque, et 1Gio de RAM, une Debian 9 avec les paquets openssh-server et curl installés. PaSSHport n’est pas encore disponible sous forme de paquet, mais un script d’installation s’occupe de tout (il existe un script pour CentOS7/RHEL7 aussi)  :

# bash <(curl -s https://raw.githubusercontent.com/LibrIT/passhport/master/tools/passhport-install-script-debian-8-9.sh)

Hi there ! Please read carefully the following (not long).

This script will attempt to install PaSSHport on this system.

This script works on Debian 8 (Jessy) and Debian 9 (Stretch).

It may also work on Debian 7, but it hasn't been tested.

[…]

Once you read and understood the above lines, you may proceed by typing

"yes", or exit by the famous "CTRL+C" :

yes

La sortie du script étant un peu longue, j'ai raccourci jusqu'à la première interaction avec l'utilisateur, qui demande simplement si je suis prêt à installer PaSSHport… Évidemment : « yes ».

Le script va alors préparer le terrain :

  • installation de l’ensemble des briques dont a besoin PaSSHport ;
  • création d’un utilisateur système passhport ;
  • installation d’un virtualenv Python pour cet utilisateur.

Soyons rassurés, PaSSHport ne touche pas à mon OpenSSH : il se sert du paquet de ma distribution et de la configuration déjà mise en place.

Une fois le terrain préparé, il télécharge PaSSHport, génère des clefs SSH qui seront à installer sur les targets, crée les fichiers de conf, peuple sa base SQLite, génère un certificat SSL auto-signé pour protéger l'API, crée un fichier bash_completion pour la commande passhport-admin et enfin, crée un service pour systemd.

Le script demande alors si on veut ajouter un utilisateur tout de suite :

Do you want to add your first user now ? Y/n

Y

Remember : no space in the user name!

Email (user name): raphael.berlamont@librit.fr    

SSH Key: ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAC5yhzISs2O0d01qk3nEm7Cxd/ma24XFId2R4qnu2YSYJ1bl7EyP6mTIBf3QXePw9eFtwvB+s9p9nmrbh97QHSoXQHLI3WxLVECAPK7bKHh/VSRyR7UdZdHnvcxWKY+g/+0w++GTSldyyveMZKCIMFF8OtHUwIf9Sf0Gck1l4F/whknfA== raphael.berlamont@x1-carbon

Comment: Ceci est le premier utilisateur configuré pendant l'installation. C'est en général l'admin de PaSSHport.

OK: "raphael.berlamont@librit.fr" -> created

Je me suis donc ajouté puis, quand cela a été demandé, j'ai copié/collé ma clé publique, et ajouté un petit commentaire.

Le script me demande alors si je veux que cet utilisateur puisse se connecter à la target locale, soit la plateforme PaSSHport elle-même, en tant que root :

Do you want to link this user to the target root@localhost ? Y/n

Y

OK: "raphael.berlamont@librit.fr" added to "root@localhost"

PaSSHport is now installed on your system.

##############################################################

# You can test that passhportd is running by running :

# curl -s --insecure https://localhost:5000

# if it displays :

# "passhportd is running, gratz!"

# you successfuly installed PaSSHport. Well done !

# If you created your first user, you can connect to PaSSHport

# using "ssh -i the_key_you_used passhport@PASSHPORT_HOST"

##############################################################

#

PaSSHport est désormais installé, et prêt à être configuré selon ma stratégie !

3. Configuration

Rentrons un peu dans les entrailles de PaSSHport. Ce dernier est composé de 3 éléments :

  • passhport : script Python qui va être lancé lorsqu'un utilisateur se connecte à PaSSHport. Il s’appuie sur la commande script [3] pour enregistrer la session de l'utilisateur.
  • passhport-admin : outil de configuration de PaSSHport.
  • passhportd : démon auquel s’adressent les deux précédents composants. Par défaut, il écoute sur le port TCP/5000 sur localhost.

Nous allons donc utiliser passhport-admin pour ajouter nos targets. Il y a deux méthodes disponibles :

  • la méthode interactive, pour laquelle il suffit de taper passhport-admin target create, puis on se laisse guider :

# passhport-admin target create

Name: www-server1

Hostname: 10.0.1.21

Type (default is ssh): ssh

Login (default is root): root

Port: 22

SSH Options:

Comment: Linux Web Server

OK: "www-server1" -> created

  • la méthode « en une ligne », très pratique quand on veut scripter une importation de masse :

# passhport-admin target create vpn-serv1 10.0.2.2 --comment="OpenBSD VPN server"

OK: "vpn-serv1" -> created

Pour cette dernière méthode, je n'ai précisé que le nom, l'IP, et le commentaire, car les valeurs par défaut (type=ssh, login=root et port=22) me vont parfaitement.

Je fais la même chose avec les autres targets (nas-srv1, net-ap1, net-ro1, net-sw1, DB1, et DB2) puis je vérifie tout ça avec la commande passhport-admin target list :

# passhport-admin target list

DB1

DB2

nas-srv1

net-ap1

net-ro1

net-sw1

root@localhost

vpn-serv1

www-server1

Note

Auto-complétion pour la commande passhport-admin

Le script d'installation met dans le répertoire /etc/bash_completion.d/ un fichier passhport-admin qui permet de profiter de la complétion pour la commande du même nom. Pour rendre cette complétion fonctionnelle, il faut s'assurer que le paquet bash-completion est bien installé. Il faut parfois ensuite se déconnecter/reconnecter à la plateforme pour que le fichier soit chargé (ou le sourcer directement : source /etc/bash_completion.d/passhport-admin). Vous pourrez ensuite profiter de la complétion, qui facilite grandement l'utilisation habituelle de passhport-admin, surtout quand on oublie l'ordre de certains arguments !

Je vais grouper ces targets dans des targetgroups. Je crée d'abord les targetgroups :

# passhport-admin targetgroup create Unices

OK: "Unices" -> created

# passhport-admin targetgroup create Network

OK: "Network" -> created

# passhport-admin targetgroup create Databases

OK: "Databases" -> created

Puis j'ajoute les targets dans ces targetgroups :

# passhport-admin targetgroup addtarget www-server1 Unices

OK: "www-server1" added to "Unices"

[…NDR: vous voyez le principe, je ne détaille pas tout…]

# passhport-admin targetgroup addtarget DB2 Databases

OK: "DB2" added to "Databases"

Passons maintenant à la création de nos utilisateurs. C'est un peu plus fastidieux, car chaque utilisateur doit me fournir sa clef publique RSA ou ECDSA. Après avoir obtenu leurs clefs publiques, je peux ajouter ces utilisateurs à PaSSHport, par exemple :

# passhport-admin user create erwan@glmf.fr "ssh-rsa AAAAB3NzaC1yc[…]kSG1jx9Yj3 erwan@laptop" --comment="Sysadmin Linux et BSD"

OK: "erwan@glmf.fr" -> created

Ensuite, je crée les usergroups :

# passhport-admin usergroup create Unices-admins

OK: "Unices-admins" -> created

# passhport-admin usergroup create Network-admins

OK: "Network-admins" -> created

# passhport-admin usergroup create DBA

OK: "DBA" -> created

# passhport-admin usergroup create Web-dev

OK: "Web-dev" -> created

J'ajoute Erwan au usergroup Unices-admins :

# passhport-admin usergroup adduser erwan@glmf.fr Unices-admins

OK: "erwan@glmf.fr" added to "Unices-admins"

Puis je fais la même chose avec les autres utilisateurs…

Maintenant, il faut que j'interconnecte les usergroups aux targetgroups, afin que les utilisateurs puissent accéder aux machines de leur périmètre :

# passhport-admin targetgroup addusergroup Unices-admins Unices

OK: "Unices-admins" added to "Unices"

Bon, par contre, je fais quoi avec le usergroup Web-dev ? Je n'ai pas de targetgroup qui corresponde parfaitement à son périmètre (il doit accéder au serveur web www-server1 et DB1). J'ai donc deux possibilités (figure 2) :

  • créer un nouveau targetgroup avec les bonnes targets ;
  • donner directement l’accès au usergroup Web-dev aux targets.

Fig. 2 : Lien direct entre le usergroup Web-dev et les targets ? Ou bien création d'un targetgroup propre à ce cas de figure ? Un choix pas toujours évident.

Ici, la première solution est plus propre, à savoir qu'il est une bonne habitude que de créer un targetgroup dès qu'il y a des targets qui seront conjointement accessibles. Mais pour l'exemple et montrer la souplesse de PaSSHport, je vais faire la seconde, à savoir un lien direct entre le usergroup et les targets :

# passhport-admin target addusergroup Web-dev www-server1

OK: "Web-dev" added to "www-server1"

# passhport-admin target addusergroup Web-dev DB1

OK: "Web-dev" added to "DB1"

En résumé, PaSSHport offre la souplesse de pouvoir lier directement, comme le montre la figure 3 :

  • un user à un targetgroup ;
  • un user à une target ;
  • un usergroup à une target ;
  • un usergroup à une targetgroup ;
  • un usergroup à un autre usergroup ;
  • un targetgroup à un autre targetgroup.

Fig. 3 : Les liens possibles entre user, usergroup, targetgroup, et target. Pour des raisons de lisibilité ont été omis les liens usergroup → usergroup et targetgroup → targetgroup. Ces deux dernières combinaisons peuvent être utiles dans certains cas.

À vous de choisir comment vous voulez organiser vos accès !

4. Installation de la clé sur les targets

Pour pouvoir se connecter aux targets, il faut diffuser la ou les clefs qui ont été générées par le script d'installation. Elles se trouvent dans /home/passhport/.ssh/, et se nomment id_ecdsa (521 bits) et id_rsa (4096 bits). Je vais copier la clé publique id_ecdsa(.pub) sur mes machines distantes, via la commande ssh-copy-id (et comme ma clef perso est déjà sur le serveur, il ne me demande pas mon mot de passe) :

# ssh-copy-id -i /home/passhport/.ssh/id_ecdsa root@10.0.1.21

/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed

/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'root@10.0.1.21'"

and check to make sure that only the key(s) you wanted were added.

Je fais la même chose avec l'ensemble des targets. Il se peut que certains périphériques n'acceptent pas les clés ECDSA, il faut alors utiliser la clé RSA qui a plus de chance de passer.

5. C’est parti !

Ça y est, on a fini la configuration, et il faut maintenant tester ! PaSSHport s’appuie sur le serveur SSH de la distribution, sans modification. On peut donc se connecter via la commande ssh, le logiciel graphique putty, ou tout autre outil classique, capable de se connecter avec une clef SSH.

Je demande à Erwan de passer désormais par PaSSHport pour se connecter aux machines Linux. Erwan utilise un PC sous Ubuntu, il n'a donc qu'à lancer sa console, et se connecter en SSH au bastion PaSSHport (10.100.0.158), en tant qu'utilisateur passhport :

erwan@ubuntu$ ssh passhport@10.100.0.158

Welcome erwan@glmf.fr.

Here is the list of servers you can access:

1  www-server1  10.0.1.21  Linux Web Server

2  vpn-serv1    10.0.2.2   OpenBSD VPN server

3  nas-srv1     10.0.3.3   FreeBSD NAS server

Type the number, name or hostname of the server you want to connect to :

La liste des serveurs sur lesquels Erwan a le droit de se connecter s'affiche. Pour se connecter à sa cible, il peut donner au choix :

  • le numéro de la ligne ;
  • le nom de la target ;
  • l'IP de la target.

Le seul élément qui n'est pas forcement unique est la troisième colonne, représentant l'IP. Si Erwan tape une IP qui existe plusieurs fois, c'est la première occurrence qui sera prise comme cible.

Erwan va se connecter sur www-server1, soit le choix 1 :

Type the number, name or hostname of the server you want to connect to : 1

Connecting to: www-server1...

Linux www-server1 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64

The programs included with the Debian GNU/Linux system are free software;

the exact distribution terms for each program are described in the

individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent

permitted by applicable law.

Last login: Fri Jun  1 10:54:50 2018 from 10.100.0.3

root@www-server1:~# echo "coucou"

coucou

root@www-server1:~# exit

Connection to 10.0.1.21 closed.

Connection to 10.100.0.158 closed.

Erwan s’est connecté, et toutes ses commandes ont été enregistrées, jusqu'à sa déconnexion.

6. Analyse des logs d'une session

C'est un peu pourquoi j'ai installé PaSSHport : pouvoir comprendre ce qui a été fait, et par la même, « qui » a fait quoi. Les transcriptions des sessions se trouvent dans /var/log/passhport/ANNÉE/MOIS/JOUR/. Si je prends l'exemple de la session d'Erwan, il y a deux fichiers correspondants aux transcriptions, un fichier contenant les données « utiles », et un fichier d'horodatage (.timing) permettant de reproduire un affichage en temps réel :

/var/log/passhport/2018/06/01# ls -l

total 8

-rw-r--r-- 1 passhport passhport 895 juin   1 10:59 20180601T105913-PID27837-www-server1-root@10.0.1.21-erwan@glmf.fr

-rw-r--r-- 1 passhport passhport 431 juin   1 10:59 20180601T105913-PID27837-www-server1-root@10.0.1.21-erwan@glmf.fr.timing

/var/log/passhport/2018/05/30#

Pour lire ce fichier, on utilise la commande scriptreplay :

/var/log/passhport/2018/06/01# scriptreplay -t 20180601T105913-PID27837-www-server1-root@10.0.1.21-erwan@glmf.fr.timing 20180601T105913-PID27837-www-server1-root@10.0.1.21-erwan@glmf.fr

Linux www-server1 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64

The programs included with the Debian GNU/Linux system are free software;

the exact distribution terms for each program are described in the

individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent

permitted by applicable law.

Last login: Fri Jun  1 10:54:50 2018 from 10.100.0.3

root@www-server1:~# echo "coucou"

coucou

root@www-server1:~# exit

Connection to 10.0.1.21 closed.

/var/log/passhport/2018/06/01#

Je vois dans ce replay, tout ce qui a été effectué et vu par Erwan, du début de la session avec l'acceptation de l'empreinte SSH du serveur distant, jusqu'à la déconnexion.

7. Limiter les accès réseau

Cette partie ne concerne pas vraiment PaSSHport, mais montre comment j'ai implémenté ce dernier dans notre réseau.

Afin de blinder les accès et faire en sorte qu'aucune machine ne puisse être atteinte autrement que par   PaSSHport, j'ai consulté l'équipe réseau pour mettre en place des règles de pare-feu qui autoriseront seulement PaSSHport à atteindre les différentes targets :

Fig. 4 : Le réseau utilisateur ne peut plus accéder directement aux targets via SSH. Seul PaSSHport est autorisé à se connecter à celles-ci. Les utilisateurs ne peuvent accéder en SSH qu'à PaSSHport.

C'est simple et efficace : seul PaSSHport est autorisé par le pare-feu à se connecter aux targets, et il est aussi le seul à pouvoir recevoir des connexions SSH. Le pare-feu filtrera toute tentative de connexion SSH autres que celles-ci.

Conclusion

Depuis que j'ai mis en place PaSSHport, je suis rassuré sur plusieurs points :

  • si quelque chose a été fait sur une plateforme, je peux retrouver qui s'est connecté sur cette plateforme, à quelle date, en tant que tel utilisateur, et comprendre ce qui a été fait ;
  • grâce au blindage des accès réseau, les targets ne sont plus accessibles directement, et surtout, il n'est plus possible pour les utilisateurs de se transmettre des accès en se donnant des mots de passe, pour les quelques serveurs qui fonctionnent sans clé publique, comme certaines briques réseau ;
  • pour les mêmes raisons, si un petit malin met sa clef dans ~/.ssh/authorized_keys, les accès lui seront refusés ;
  • avec une installation en haute disponibilité et une base de données partagée : je n’ai pas de SPOF (Single Point Of Failure).

Je peux maintenant déguster tranquillement mon ristretto, troller sur le rachat de GitHub par Microsoft, et retourner sur mes tutos Kubernetes.

Références

[1] Sources sur GitHub : https://github.com/librit/passhport

[2] Société éditrice de PaSSHport : LibrIT - https://www.librit.fr

[3] La commande script appartient au projet/paquet util-linux, généralement déjà présent de base sur les distributions courantes : https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/

Pour aller plus loin

Une documentation couvrant globalement ce qui a été vu ici, est disponible : https://docs.passhport.org