Ce qu'il doit être
- Un petit serveur séparé qui exécute SSH et rien d'autre : pas de base de données, pas de site web, pas d'outils de développement.
- Des clés uniquement, avec des mises à jour de sécurité automatiques.
- Aucune clé privée dessus. Un rebond rend cela inutile. La connexion au serveur interne est chiffrée depuis votre propre machine, et le bastion ne fait que faire passer les données.
- Le seul point d'entrée. Les serveurs internes ne doivent accepter SSH que depuis l'adresse du bastion, au niveau du pare-feu.
Pour le reste des réglages SSH du serveur, voir comment durcir sshd_config.
Créer les utilisateurs
sudo groupadd jump
sudo useradd -m -G jump -s /bin/bash alice
sudo install -d -m 700 -o alice -g alice /home/alice/.ssh
sudo install -m 600 -o alice -g alice alice.pub /home/alice/.ssh/authorized_keys
Gardez votre propre compte d'administration hors du groupe jump, pour conserver un shell.
La configuration de sshd
# /etc/ssh/sshd_config.d/10-bastion.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
AllowAgentForwarding no
AllowTcpForwarding no
AllowStreamLocalForwarding no
X11Forwarding no
PermitTunnel no
GatewayPorts no
LogLevel VERBOSE
Match Group jump
AllowTcpForwarding local
PermitOpen 10.0.1.10:22 10.0.1.11:22
PermitTTY no
ForceCommand /usr/sbin/nologin
Chaque réglage du bloc a un rôle :
AllowTcpForwarding localautorise les rebonds et-L, mais pas-R.PermitOpenliste les destinations possibles. Énumérez chaque serveur :*peut remplacer un hôte ou un port entier, mais pas une partie d'adresse comme10.0.1.*.PermitTTY noetForceCommandsuppriment le shell.ForceCommandseul n'empêche pas la redirection, et c'est ce qui lui permet de fonctionner ici.
Placez-le dans un fichier à part sous /etc/ssh/sshd_config.d/, et vérifiez d'abord qu'aucun autre fichier ne définit les mêmes options avant lui : sshd garde la première valeur lue. Un bloc Match dans un tel fichier s'arrête à la fin du fichier. Vérifiez ensuite et rechargez, en laissant une session ouverte :
sudo sshd -t
sudo sshd -T -C user=alice,host=client,addr=203.0.113.10 | grep -iE '^(allowtcpforwarding|permitopen|permittty|forcecommand) '
sudo systemctl reload ssh
permittty no
allowtcpforwarding local
forcecommand /usr/sbin/nologin
permitopen 10.0.1.10:22 10.0.1.11:22
Le chemin de nologin peut être /sbin/nologin sur d'autres systèmes : vérifiez-le avec command -v nologin.
DisableForwarding est pour les autres utilisateurs
DisableForwarding yes désactive toute redirection et l'emporte sur les autres réglages : il convient donc à un groupe qui ne doit jamais rien rediriger. Ne l'utilisez pas pour les utilisateurs de rebond. Un rebond est une redirection, et dans notre essai il a échoué avec stdio forwarding failed.
Se connecter par son intermédiaire
ssh -J alice@bastion.example.com user@10.0.1.10
Ou, pour moins taper, dans ~/.ssh/config sur votre propre machine :
Host bastion
HostName bastion.example.com
User alice
Host app1
HostName 10.0.1.10
User user
ProxyJump bastion
Lancez ensuite ssh app1. Votre clé doit se trouver à la fois sur le bastion et sur le serveur interne. Le côté client est détaillé dans hôtes de rebond et ProxyJump. Évitez ForwardAgent, pour les raisons données dans les risques de la redirection d'agent.
Tester ce qui est refusé
Voici les résultats obtenus avec cette configuration, et vous devriez voir les mêmes :
| Test | Résultat |
|---|---|
ssh -J alice@bastion user@10.0.1.10 | Connecté au serveur interne |
ssh -W 10.0.1.10:80 alice@bastion | stdio forwarding failed |
ssh -tt alice@bastion | PTY allocation request failed on channel 0 |
ssh alice@bastion id | This account is currently not available. |
ssh -N -o ExitOnForwardFailure=yes -R 8080:localhost:22 alice@bastion | Error: remote port forwarding failed for listen port 8080 |
Journalisation : ce que vous obtenez et ce que vous n'obtenez pas
Dès le niveau par défaut, chaque connexion est enregistrée avec l'empreinte de sa clé, ce qui indique qui s'est connecté. LogLevel VERBOSE enregistre aussi les clés refusées. Une connexion ressemble à ceci :
Accepted publickey for alice from 203.0.113.10 port 51018 ssh2: ED25519 SHA256:NxFS05Q+k1zf...
Les cibles refusées sont aussi journalisées :
Received request from 203.0.113.10 port 51036 to connect to host 10.0.1.10 port 80, but the request was denied.
Les cibles réussies ne le sont pas, en INFO ou VERBOSE. Nous avons vérifié : le journal du bastion montre qu'alice s'est connectée, mais pas vers quel serveur elle est allée ensuite. Dans notre essai, cela n'apparaissait qu'à LogLevel DEBUG1, et le manuel prévient que les niveaux de débogage peuvent enregistrer des détails privés des utilisateurs :
debug1: server_request_direct_tcpip: originator 127.0.0.1 port 65535, target 10.0.1.10 port 22
Lisez donc les deux côtés. Le journal du serveur interne montre qui s'est connecté et avec quelle clé, avec l'adresse du bastion comme source. Rapprochez les deux par l'heure, et envoyez les deux vers une machine extérieure au bastion, car un attaquant ayant root dessus pourrait modifier la copie locale. Voir comment vérifier les tentatives de connexion pour lire ces journaux.
Retirer l'accès
Retirez la clé publique de l'utilisateur du bastion, et il perd cette voie vers tous les serveurs derrière. Il peut encore utiliser tout serveur interne qu'il atteint autrement : retirez donc aussi la clé là. Voir comment révoquer une clé.
Questions
Quelle différence entre un bastion et un hôte de rebond ?
Peu. Un hôte de rebond est tout serveur par lequel on passe pour en atteindre un autre. Un bastion est un hôte de rebond configuré et durci pour cette seule tâche, et c'est la seule entrée depuis l'extérieur.
Le bastion a-t-il besoin de ma clé privée ?
Non. Avec ProxyJump, la connexion au serveur interne part de votre machine et reste chiffrée de bout en bout. Le bastion ne fait que relayer la connexion.
Pourquoi ssh -J échoue-t-il avec stdio forwarding failed ?
Le bastion a refusé la redirection. Vérifiez que AllowTcpForwarding l'autorise pour votre utilisateur, que la cible figure dans PermitOpen et que DisableForwarding n'est pas activé.