Login Logout

Howto OpenSSH

OpenSSH est l’implémentation la plus répandue du protocole SSH permettant de se connecter sur une machine à travers le réseau de manière sécurisée. OpenSSH est développé par le projet OpenBSD avec une grande attention sur la sécurité. Il offre de nombreuses fonctionnalités : SCP, SFTP, clés SSH, tunnels, VPN, etc.

Installation

Debian

On peut installer les parties client et serveur indépendamment :

# apt install openssh-client
# ssh -V
OpenSSH_9.2p1 Debian-2, OpenSSL 3.0.9 30 May 2023

# apt install openssh-server
# nc localhost 22
SSH-2.0-OpenSSH_9.2p1 Debian-2
# systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; preset: enabled)
     Active: active (running) since Fri 2023-07-28 15:55:57 CEST; 7h ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 619 (sshd)
      Tasks: 1 (limit: 2356)
     Memory: 6.9M
        CPU: 628ms
     CGroup: /system.slice/ssh.service
             └─619 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"

OpenBSD

Les parties client et serveur sont incluses dans la base OpenBSD.

$ ssh -V
OpenSSH_7.5, LibreSSL 2.5.2

La partie serveur est activée par défaut, sauf mention contraire à l’installation.

# grep -r sshd /etc/rc.conf*
/etc/rc.conf:sshd_flags=
/etc/rc.conf.local:sshd_flags=NO

# rcctl enable sshd

Configuration

Configuration sshd_config

http://man.openbsd.org/sshd_config

Fichiers de configuration :

/etc/ssh
├── moduli
├── ssh_config
├── sshd_config
├── ssh_host_ecdsa_key
├── ssh_host_ecdsa_key.pub
├── ssh_host_ed25519_key
├── ssh_host_ed25519_key.pub
├── ssh_host_rsa_key
└── ssh_host_rsa_key.pub

La configuration de base que nous utilisons en général (les options commentées sont les valeurs par défaut).

#   $OpenBSD: sshd_config,v 1.100 2016/08/15 12:32:04 naddy Exp $

# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin

# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options override the
# default value.

Port 22
Port 2200
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key

# Ciphers and keying
#RekeyLimit default none

# Logging
#SyslogFacility AUTH
LogLevel VERBOSE

# Authentication:

#LoginGraceTime 2m
PermitRootLogin no
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#PubkeyAuthentication yes

# Expect .ssh/authorized_keys2 to be disregarded by default in future.
#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2

#AuthorizedPrincipalsFile none

#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication no

# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no

# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes

#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding yes
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
#UsePrivilegeSeparation sandbox
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /var/run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none

# no default banner path
#Banner none

# Allow client to pass locale environment variables
#AcceptEnv LANG LC_*

# override default of no subsystems
Subsystem   sftp    /usr/lib/openssh/sftp-server -l INFO

# Example of overriding settings on a per-user basis
#Match User anoncvs
#   X11Forwarding no
#   AllowTcpForwarding no
#   PermitTTY no
#   ForceCommand cvs server

AllowUsers jdoe alice bob

Match Address 192.0.2.42
    PasswordAuthentication yes
Match User alice bob
    PasswordAuthentication no
Match Group adm
    PasswordAuthentication no

Il est possible de spécifier les directives AllowUsers et AllowGroups plusieurs fois. La ligne suivante :

AllowUsers jdoe alice bob

est équivalente à celles-ci :

AllowUsers jdoe
AllowUsers alice
AllowUsers bob

Vérification / check de la configuration SSHD

Utile pour ne pas s’enfermer dehors :

$ sshd -t

Même chose, en affichant aussi la configuration sur la sortie standard :

$ sshd -T

Pour tester la configuration en précisant l’utilisateur et l’IP du client :

# sshd -T -C user=<username>,addr=<ip>
# sshd -T -C user=<username>,addr=<ip> | grep -i -e 'passwordauthentication' -e 'allowgroups' -e 'allowusers'

Configuration ssh_config

http://man.openbsd.org/ssh_config

On peut configurer la partie client via le fichier ~/.ssh/config.

On peut ainsi passer certaines options générales :

UseRoaming no
User jdoe
IdentityFile ~/.ssh/id_ed25519
IdentityFile ~/.ssh/id_rsa

On peut aussi passer des options spécifiques à chaque serveur distant sur lequel on va se connecter. On définit ainsi des alias (utilisables ensuite via ssh) et des options correspondantes :

Host foo ssh.example.com
    Hostname ssh.example.com
    Port 2222
    User john

Host sql.example.com
    IdentityFile ~/.ssh/id_bastion_ed25519
    ProxyCommand ssh bastion.example.com -W %h:%p
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

Host nfs.example.com
    ProxyCommand ssh -W bastion.example.com:2222 john@192.0.2.111

Host switch42
    User admin
    HostName 192.0.2.150
    ProxyCommand sh -c "nc -w1 %h %p 2>/dev/null || ssh bastion.example.com -W %h:%p"
    KexAlgorithms diffie-hellman-group1-sha1

Pour les cas simples d’utilisation de ProxyCommand, il est possible d’utiliser ProxyJump [user@]host[:port] (équivalent à l’option -J en ligne de commande :

Host private.example.com
    ProxyJump bastion.example.com

SCP

Le protocole SCP (Secure Copy) est basé sur SSH, il permet de transférer des fichiers entre deux machines distantes de façon sécurisée.

Utilisation simple :

$ scp /foo/FICHIER ssh.example.com:/bar/

Pour envoyer un répertoire, on utilisera l’option -r :

$ scp -r /foo/ ssh.example.com:/bar/

Options utiles pour scp :

  • -P 2222 : utilise le port 2222 pour le serveur distant (malheureusement différent de ssh…)
  • -q : mode silencieux
  • -C : active la compression
  • -i identity_file : utilise une clé SSH spécifique

SFTP

Le protocole SFTP correspond au SSH File Transfer Protocol : c’est une extension du protocole SSH qui permet non seulement de transférer des fichiers (comme SCP) mais aussi d’avoir des fonctionnalités plus avancées (listing de répertoire, suppression de fichiers, etc.) similaire au vieux protocole FTP :

$ sftp ssh.example.com
Connected to ssh.example.com.
sftp> ls
sftp> lls
sftp> pwd
sftp> get FICHIER
sftp> put FICHIER
sftp> bye

chroot SFTP

On peut restreindre un accès SFTP dans un répertoire, les utilisateurs n’auront accès qu’à une vue limitée de l’arborescence du système.

Pour mettre en place un chroot SFTP, on crée un répertoire /home/sftp et un groupe sftp :

# mkdir /home/sftp
# chown root:sftp /home/sftp
# chmod 750 /home/sftp

# groupadd sftp

et on configure via /etc/ssh/sshd_config :

Subsystem sftp internal-sftp

Match group sftp
   ChrootDirectory /home/sftp
   X11Forwarding no
   AllowTcpForwarding no
   ForceCommand internal-sftp

On peut ensuite créer un utilisateur restreint :

# useradd -g sftp -d /jdoe jdoe
# mkdir /home/sftp/jdoe/
# chown jdoe:sftp /home/sftp/jdoe/
# chmod 700 /home/sftp/jdoe/

SFTP Only

On peut restreindre un utilisateur au protocole SFTP. Il faut s’assurer que /usr/lib/sftp-server soit un shell valide et lui définir comme shell par défaut :

# echo '/usr/lib/stfp-server' >> /etc/shells
# usermod -s /usr/lib/sftp-server userna

Clé SSH

Il y a plusieurs types de clé, avec suivant les algorithmes, des tailles de clés variables ou non. L’algorithme que nous conseillons est ed25519 mais attention, ce n’est pas supporté sur des anciennes machines (Debian 7 par exemple) où il faut utiliser l’algorithme RSA (on conseille alors d’utiliser une taille de clé de 4096).

Attention, lors de la génération de votre paire de clés, si vous ne conservez pas le nom et l’emplacement par défaut ( qui est sous la forme ~/.ssh/id_{rsa,ed25519}) alors la clé ne sera pas utilisée par le client SSH. Il faudra alors lui indiquer de l’utiliser de manière explicite avec avec l’argument -i pour spécifier la clé privée, ou le définir dans le fichier de configuration de SSH,

Pour générer une clé SSH ed25519 :

$ ssh-keygen -t ed25519 -a 100
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/jdoe/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/jdoe/.ssh/id_ed25519.
Your public key has been saved in /home/jdoe/.ssh/id_ed25519.pub.
The key fingerprint is:
SHA256:7tdFlczm4VJkEl4yM08O4VOPkGQiU1YR8kKuLsm9v84 jdoe@example.com
The key's randomart image is:
+--[ED25519 256]--+
|        o.*oB&**.|
|         * =+.#Oo|
|          o .+*+o|
|         . . .oo |
|        S    ..  |
|     . =      .  |
|      + +  . .   |
|       o o. .    |
|        o+E.     |
+----[SHA256]-----+

Note : l’option -a 100 spécifie le nombre d’itérations de l’algorithme de dérivation de clé qui protège la clé privée avec la passphrase utilisée. Cela protège la clé privée contre le brute-force de la passphrase. Mais attention, plus ce nombre est grand, plus le déchiffrement de la clé est ralenti. Un bon compromis est 100 en nombre d’itérations.

Pour générer une clé SSH RSA :

$ ssh-keygen -o -t rsa -b 4096 -a 100
Generating public/private rsa key pair.
Enter file in which to save the key (/home/jdoe/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/jdoe/.ssh/id_rsa.
Your public key has been saved in /home/jdoe/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:9tGKNLEbiUyAbhShJ7rncBpn/ydCXZHtJkdIsUoW2TM jdoe@example.com
The key's randomart image is:
+---[RSA 4096]----+
|  o+. .+o=       |
| .o  ...E.o      |
|oo.   + o*       |
|.oo  = oo++.     |
|..   .+.S+. .    |
| .  . .o * o     |
|+ =.    o o      |
| X .. . .        |
|. . .o.o         |
+----[SHA256]-----+

Note : dans le cas de RSA, on utilise l’argument -o afin que la clé privée utilise le nouveau format qui est à la fois standard et plus résistant aux attaques par brute-force.

authorized_keys

Pour autoriser une connexion avec une clé SSH, il faut la placer dans le fichier ~/.ssh/authorized_keys et ajuster les droits :

$ ssh ssh.example.com mkdir -p ~/.ssh
$ ssh ssh.example.com chmod 700 ~/.ssh
$ scp ~/.ssh/id_ed25519.pub ssh.example.com:.ssh/authorized_keys
$ ssh ssh.example.com chmod 600 ~/.ssh/authorized_keys

Pour faire cela de façon plus simple, on peut utiliser le script ssh-copy-id :

$ ssh-copy-id ssh.example.com

Note : pour préciser un port alternatif, on peut utiliser l’option -p NN depuis Debian 8. En Debian 7, il faut utiliser ssh-copy-id "-pNN ssh.example.com"

On peut mettre des restrictions d’accès via le fichier .ssh/authorized_keys, par exemple :

no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa XXXXX commentaires

On peut vérifier que les clefs publiques dans .ssh/authorized_keys sont valides avec le return code de ssh-keygen :

$ ssh-keygen -l -f .ssh/authorized_keys
4096 SHA256:...
256 SHA256:...
...
$ echo $?
0

Limitations des commandes autorisées

Il est possible de limiter une clée à une commande spécifique en le déclarant dans le fichier .ssh/authorized_keys :

command="cat /etc/os-release" ssh-ed25519 XXXXX commentaires

ou encore pour limiter à du SFTP :

command="internal-sftp" ssh-ed25519 XXXXX commentaires

C’est aussi faisable dans la configuration du serveur sshd_config avec la directive ForceCommand.

Dans l’exemple précédent on est limité à une seule commande par clée SSH, ce qui dans la plus part des cas est assez limitant. Pour autoriser plusieurs commandes pour une seule clée, il suffit d’écrire un script qui vérifie la variable d’environement SSH_ORIGINAL_COMMAND qui contiens la commande spécifiée par le client SSH. Ci-dessous se trouve un exemple pour ce type de script ainsi que sa configuration ; sans garantie aucune de sécurité :

$ cat /usr/local/libexec/ssh-authorized-commands
#!/bin/sh
#
# Execute file present in directory $1 without support for arguments.

set -e

authorized_command_dir=$(realpath $1)

# When no command is specified by the client
test -z "${SSH_ORIGINAL_COMMAND}" && exit 1

command=$(realpath "${authorized_command_dir}/${SSH_ORIGINAL_COMMAND}")

# Trying to avoid path traversal attack
test $(dirname "$command") != "$authorized_command_dir" && exit 1

if [ -x "$command" ]; then
    "$command"
else
    echo "$(basename $0): ${SSH_ORIGINAL_COMMAND}: command not found" >&2 && exit 127
fi
$ grep ssh-authorized-commands ~/.ssh/authorized_keys
command="/usr/local/libexec/ssh-authorized-commands /etc/ssh/authorized_command.d" ssh-ed25519 XXXXX commentaires
$ head -n-0  /etc/ssh/authorized_command.d/*
==> /etc/ssh/authorized_command.d/machine-id <==
#!/bin/sh
cat /etc/machine-id

==> /etc/ssh/authorized_command.d/os-release <==
#!/bin/sh
cat /etc/os-release

Agent SSH

Il est important de protéger une clé SSH utilisée par un humain par une passphrase. Pour éviter de saisir sa passphrase à chaque utilisation, on peut utiliser un agent SSH qui va retenir la clé SSH en mémoire.

En général, un agent SSH est lancé par son Window Manager. Sinon, on peut le lancer automatiquement via son ~/.profile :

eval $(ssh-agent) 1> /dev/null

Une fois lancé, l’agent ouvre une socket Unix dans $TMPDIR/ssh-XXXXXXXXXX/agent.PPID et la rend disponible via les variables d’environnement suivantes :

SSH_AUTH_SOCK=/tmp/ssh-IklIVmCGvWgT/agent.1151
SSH_AGENT_PID=1198

On peut alors lancer la commande ssh-add qui va nous demander notre passphrase pour transmettre la clé à l’agent SSH :

$ ssh-add -t 36000
Enter passphrase for /home/jdoe/.ssh/id_ed25519:
Identity added: /home/jdoe/.ssh/id_ed25519 (/home/jdoe/.ssh/id_ed25519)

Note : l’option -t permet de limiter à 10h (36 000 secondes) le temps où l’agent va conserver la passphrase en mémoire. Si l’option n’est pas utilisée, ce temps sera infini, ce que l’on déconseille en général.

Sous Gnome, si vous utilisez gnome-keyring-ssh il se peut que vous ayez l’erreur Could not add identity pour votre clé id_ed25519. Il faut alors appliquer le correctif suivant :

mkdir -p ~/.config/autostart
cp /etc/xdg/autostart/gnome-keyring-ssh.desktop ~/.config/autostart/
echo "X-GNOME-Autostart-enabled=false" >> ~/.config/autostart/gnome-keyring-ssh.desktop

On peut également utiliser l’outil keychain qui facilite l’utilisation d’un agent SSH (et d’un agent GPG). Il va notamment lancer les agents si ils ne sont pas encore lancés, et transmettre plusieurs clés SSH si besoin.

$ keychain id_ed25519 id_rsa --timeout 600

 * keychain 2.7.1 ~ http://www.funtoo.org
 * Starting ssh-agent...
 * Starting gpg-agent...
 * Adding 2 ssh key(s): /home/jdoe/.ssh/id_ed25519 /home/jdoe/.ssh/id_rsa
Enter passphrase for /home/jdoe/.ssh/id_ed25519:
 * ssh-add: Identities added: /home/jdoe/.ssh/id_ed25519 /home/jdoe/.ssh/id_rsa (life=600m)

Note : on pourra bien sûr lancer keychain via son ~/.profile

Enfin, il existe d’autres implémentations d’agent SSH, comme le gpg-agent qui permet notamment l’utilisation de token USB pour sécuriser ses clés (Voir HowtoGPG).

Changer passphrase

L’option -p de la commande ssh-keygen permet de changer la passphrase d’une clé.

$ ssh-keygen -p -a 100 -f <chemin-clé-privée>

Tester passphrase

$ ssh-keygen -y -f <chemin-clé-privée>

Tunnel SSH

Un tunnel SSH permet d’accéder à une ressource distante via une connexion SSH. Cela s’utilise classiquement pour accéder à un service distant non accessible directement.

Tunnel SSH vers un service

Exemple simple : on veut accéder à un service MySQL accessible en local sur un serveur distant. On lance la commande suivante :

$ ssh -L 3306:127.0.0.1:3306 ssh.example.com

Et l’on pourra accéder au service MySQL sur sa propre machine via 127.0.0.1 sur le port 3306.

Si le service MySQL est sur une 3ème machine (accessible depuis le serveur distant), il suffit de préciser son IP avec l’option -L :

$ ssh -L 3306:192.0.2.33:3306 ssh.example.com

On peut bien sûr utiliser un port différent pour sa propre machine, par exemple 8306 :

$ ssh -L 8306:192.0.2.33:3306 ssh.example.com

De même, si l’on veut se « binder » sur une autre IP que 127.0.0.1 sur sa propre machine, on peut le préciser avec l’option -L :

$ ssh -L 127.0.0.33:8306:192.0.2.33:3306 ssh.example.com

Enfin il faut noter que si l’on veut utiliser un port local inférieur à 1024, il faudra lancer la commande en “root” :

# ssh -L 443:127.0.0.1:443 ssh.example.com

Tunnel SSH inverse

Si l’on est derrière une connexion NAT par exemple, on peut ouvrir un tunnel pour permettre une connexion distante depuis une machine accessible en SSH :

$ ssh -R 22000:127.0.0.1:22 ssh.example.com

Depuis ssh.example.com on peut ainsi accéder à la machine derrière le NAT via ssh -p22000 127.0.0.1.

Proxy SOCKS via SSH

On peut lancer un proxy SOCKS passant par un serveur distant ainsi :

$ ssh -D 6789 ssh.example.com

Il reste ensuite à configurer le logiciel qui va utiliser ce proxy SOCKS (4a ou 5) avec l’adresse 127.0.0.1 et le port 6789. C’est ainsi pratique avec un navigateur où l’ensemble du trafic vers le web passera ainsi par le serveur distant.

Astuces

Convertir une clé publique format PUTTY en format OpenSSH

ssh-keygen -i -f keyfile.pub > newkeyfile.pub

Loguer/exécuter script avant chaque connexions SSH

Grâce au fichier placé dans ~/etc/ssh/sshrc~, juste avant toutes connexions ssh (rsync, sftp, ssh, …), le contenu sera exécuté avant la session ssh ouverte.

Séquences d’échappement SSH

https://lonesysadmin.net/2011/11/08/ssh-escape-sequences-aka-kill-dead-ssh-sessions/

On peut utiliser des combinaisons de touches spéciales via une connexion SSH pour intéragir avec la connexion en cours.

En tapant Entrée + ~ + ? on obtient la liste des séquences possibles :

Supported escape sequences:
 ~.   - terminate connection (and any multiplexed sessions)
 ~B   - send a BREAK to the remote system
 ~C   - open a command line
 ~R   - request rekey
 ~V/v - decrease/increase verbosity (LogLevel)
 ~^Z  - suspend ssh
 ~#   - list forwarded connections
 ~&   - background ssh (when waiting for connections to terminate)
 ~?   - this message
 ~~   - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)

Voici les séquences les plus utiles :

  • Entrée + ~ + . : forcer à terminer la connexion SSH en cours (utile en cas de perte de connexion…)
  • Entrée + ~ + v : diminue la verbosité, par exemple quand on s’est connecté avec ssh -vvv

Quelques exemples de manipulations possibles (en anglais) : https://omecha.info/blog/ssh-escape-commands-for-fun-and-profit.html

Connexion SSH par rebond

Il se peut qu’on doive passer par une machine pour se connecter en SSH à une machine cible (c’est le principe d’un bastion).

On peut ainsi se connecter avec -o ProxyCommand :

$ ssh -o ProxyCommand="ssh -W cible.example.com:22 bastion.example.com" cible.example.com

Dans les versions récentes d’OpenSSH (notamment sur Debian 9) il existe une méthode plus simple :

$ ssh -J bastion.example.com cible.example.com

Note : il ne faut pas utiliser l’option -A pour rebondir car elle est dangereuse, à moins que vous ne vouliez vraiment faire du Agent forwarding

Agent forwarding

Si vous voulez utiliser une clé SSH locale depuis un serveur distant, vous pouvez utiliser l’option -A qui va rendre votre agent local accessible depuis le serveur distant :

$ ssh -A ssh.example.com
$ env | grep ^SSH_A
SSH_AUTH_SOCK=/tmp/ssh-sUSruIwyCa/agent.3265

Note : attention, cette option doit être utilisée en connaissance de cause car l’utilisateur root sur la machine ssh.example.com aura accès à vos clés et donc aux machines distantes auxquelles vous avez accès avec vos clés.

On peut également ajouter les clés SSH présentes sur un serveur distant en faisant :

$ ssh ssh.example.com -t -A ssh-add

VPN over SSH

On peut utiliser OpenSSH comme VPN ! Côté serveur, vous devez :

  • Ajouter PermitTunnel yes
  • Autoriser l’IP Forwarding : sysctl -w net.ipv4.ip_forward=1
  • Si besoin, mettre une règle de NAT, par exemple sous Linux : iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Pour lancer la connexion VPN, on fera :

# ssh -w 42:42 ssh.example.com

On a ensuite un périphérique tun42 utilisable, à configurer des 2 côtés.

  • Côté serveur SSH : ifconfig tun42 172.17.18.2/24
  • Côté client SSH : ifconfig tun42 172.17.18.1/24

On peut alors enfin configurer le routage au niveau client, par exemple sous Linux :

# ip route add 8.8.8.8/32 via 172.17.18.2 dev tun42`

UserKnownHostsFile

OpenSSH se sert du fichier ~/.ssh/known_hosts pour retenir l’empreinte (fingerprint) de chaque connexion effectuée. Cela lui permet de détecter un changement des clés du serveur, notamment une tentative d’usurpation d’un serveur :

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.

On peut accepter automatiquement la clé à la première connexion ainsi :

$ ssh -o "StrictHostKeyChecking=accept-new" ssh.example.com

Si besoin, on peut supprimer la ligne concernée dans ~/.ssh/known_hosts via une commande du type :

$ ssh-keygen -f "~/.ssh/known_hosts" -R ssh.example.com
$ ssh-keygen -f "~/.ssh/known_hosts" -R [ssh.example.com]:2222

On peut également désactiver cette vérification en utilisant un fichier alternatif :

$ ssh -o UserKnownHostsFile=/dev/null ssh.example.com

On peut également générer à l’avance les fingerpints dans un fichier ~/.ssh/known_hosts2 (utilisé par défaut).

Pour générer les fingerprints, on peut utiliser la commande ssh-keyscan :

$ ssh-keyscan ssh.example.com
ssh.example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2nP2WzQ8hkZDiEq5+QSGFhTB7SYfRa7/IG0gMOIoCDXH8b3sfsBUUEL8ZSFaWgNKskUnpgg/fqAhvnDLOPskr3OGXRfrCR68fCqn5H1zBrHB1kpdjPW9ezUe1xoY3hGp6LITANHWpZie1wBjYAzaBO70hNAo4JMCQvZXDsQdyws2DRSuYtiAoG/ZY0+t2VZh3MJLcofv1wNo43M+aHJR2xWmQSE7cWjMlcw/QOmsWJBv1+Kb/nK2Q7buX/byvY8ySu5ydATnyEinzbutZXc/t/FioOtWMeqh6NlD3aPGIpUTmf8ow+c1QwZWOC3T2GWyTD5KVmAZSm77lWkpYFcd1
ssh.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBsUneE642qr/kzKwJcKl9Cgog/kgCqRLZwZs4J7RRt8

Puis on crée un fichier contenant :

example-ssh,ssh.example.com,192.0.2.42 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2nP2WzQ8hkZDiEq5+QSGFhTB7SYfRa7/IG0gMOIoCDXH8b3sfsBUUEL8ZSFaWgNKskUnpgg/fqAhvnDLOPskr3OGXRfrCR68fCqn5H1zBrHB1kpdjPW9ezUe1xoY3hGp6LITANHWpZie1wBjYAzaBO70hNAo4JMCQvZXDsQdyws2DRSuYtiAoG/ZY0+t2VZh3MJLcofv1wNo43M+aHJR2xWmQSE7cWjMlcw/QOmsWJBv1+Kb/nK2Q7buX/byvY8ySu5ydATnyEinzbutZXc/t/FioOtWMeqh6NlD3aPGIpUTmf8ow+c1QwZWOC3T2GWyTD5KVmAZSm77lWkpYFcd1
example-ssh,ssh.example.com,192.0.2.42 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBsUneE642qr/kzKwJcKl9Cgog/kgCqRLZwZs4J7RRt8

Ce fichier contenant les fingerprints peut aussi être placé dans /etc/ssh/ssh_known_hosts ou /etc/ssh/ssh_known_hosts2 et sera ainsi utilisé par défaut par tous les utilisateurs d’une machine.

Une autre technique pour vérifier un fingerprint est d’avoir des enregistrements DNS SSHFP. On peut les générer à l’aide de la commande ssh-keygen :

$ ssh-keygen -r ssh.example.com
ssh.example.com IN SSHFP 1 1 b0db7dbfe0775199662453c9d6eace6f993b9fdc
ssh.example.com IN SSHFP 1 2 13e60f2c4d2848aea91c420b783273afe6c23efd23818ab220f167a53a0be886
ssh.example.com IN SSHFP 2 1 c2ea7d50f05af4cabc8e6add3491a08654127f83
ssh.example.com IN SSHFP 2 2 1222b6f7ad0d3447a31e3cba1af2eaf8aab7316570b63cc3eccdd4d865fee474
ssh.example.com IN SSHFP 3 1 d93dbd0af9c7fd456b3fa4e9094ede031791da84
ssh.example.com IN SSHFP 3 2 3b885c365ae3cebab6dea63b3eb697d2060d9da906e129011841d665a4ccfe4f
ssh.example.com IN SSHFP 4 1 39d521c3d4b6f672149c1cc053014c9ebdfd4727
ssh.example.com IN SSHFP 4 2 0f28d1764793b519c9c74bfb7c6d0dc9b980bdf22c68d1e324d19247d8cbe161

On peut ainsi vérifier à la connexion :

$ ssh -o "VerifyHostKeyDNS ask" ssh.example.com
The authenticity of host 'ssh.example.com (192.0.2.42)' can't be established.
ED25519 key fingerprint is SHA256:sln94vzrKSsTezPvT6pyO/Glavvl7/Ao8Wcd46BeRb0.
Matching host key fingerprint found in DNS.
Are you sure you want to continue connecting (yes/no)?

ControlMaster

OpenSSH peut permettre de réutiliser une connexion en cours pour d’autres connexions via l’option ControlMaster. Si l’on veut activer ce comportement, on pourra ajouter dans ~/.ssh/config :

ControlMaster auto
ControlPath ~/.ssh/ssh_control_%h_%p_%r

SSHFS

SSHFS permet de créer des montages via une connexion SSH.

Pour monter :

# apt install sshfs
$ sshfs user@ssh.example.com:/foo/bar /mnt

Pour démonter :

$ fusermount -u /mnt

Pour automatiser dans /etc/fstab :

user@ssh.example.com:/foo/bar /mnt fuse.sshfs noauto,user,_netdev,reconnect,x-systemd.automount,x-systemd.device-timeout=10,identityfile=<PRIVATE_KEY_PATH> 0 0

X Forwarding

SSH peut créer automatiquement un tunnel et gérer la variable $DISPLAY pour afficher en local les applications graphiques distantes. Côté serveur, il faut la présence du package xauth et l’option X11Forwarding yes dans la configuration SSH. On peut alors lancer :

$ ssh -X ssh.example.com
$ xlogo

mosh

https://mosh.org/

En cas de connexion réseau avec une latence réseau élevée ou instable (perte de paquets), on peut utiliser mosh qui vient se positionner après une connexion SSH en utilisant un port UDP dédiée à une session/utilisateur. Il gère aussi le roaming, c’est-à-dire que l’adresse IP source n’est pas importante pour garder la connexion (bascule ADSL vers LTE par exemple) et que la connexion peut être suspendue plusieurs heures sans problème (appareil en veille, coupure réseau…).

# apt install mosh

Pour se connecter basiquement (port SSH 22, utilisateur actuel). Il suffit grosso-modo de remplacer la commande ssh par mosh.

$ mosh server

Un exemple plus avancé.

$ mosh --ssh "ssh -p2222 -l user" server

Mosh va ouvrir une connexion ssh, puis la quitter pour basculer en UDP sur un port négocié (plage 60001:60999 par défaut). On peut tout à fait avoir la connexion SSH restreinte par un pare-feu (par exemple il faudra utiliser un VPN pour la connexion SSH), mais avoir la plage de ports UDP publiques.

Restriction par IP en même temps que AllowGroups

Lorsqu’on utilise des autorisations à base de AllowGroups il n’est pas possible de spécifier des restriction par IP comme lorsqu’on utilise AllowUsers (exemple : AllowUsers user1@IP user2@IP […] user3 user4).

On peut contourner cette limitation en ajoutant un bloc Match avec le group et la ou les IP en conditions. Exemple avec root, restreint à 2 IP, avec clé uniquement :

Match User root Address 10.0.0.1,192.168.0.1
    AllowGroups root
    PubkeyAuthentication yes
    PasswordAuthentication no
    PermitRootLogin without-password

Revenir à la configuration globale après une directive Match

Si on utilise une directive Match, les directives suivantes seront toutes relatives au contexte défini dans la directives Match jusqu’à la fin du fichier ou jusqu’à la prochaine directive Match.

Pour “revenir” à la configuration globale après une directive Match, on peut utiliser Match all.

Match user someuser
    PasswordAuthentication no
Match all
PasswordAuthentication yes

FAQ

Obtenir l’empreinte de la clé publique du serveur

$ ssh-keygen -lf /etc/ssh/clé.pub

Regénérer les clés du serveur

Sous Debian :

# rm -i /etc/ssh/ssh_host_*
# dpkg-reconfigure openssh-server
Creating SSH2 RSA key; this may take some time ...
Creating SSH2 DSA key; this may take some time ...
Restarting OpenBSD Secure Shell server: sshd.

Sous OpenBSD :

# rm -i /etc/ssh/ssh_host_*
# ssh-keygen -A
ssh-keygen: generating new host keys: RSA DSA ECDSA ED25519

À noter que c’est fait à chaque boot (via /etc/rc), si les clés n’existent pas donc une autre solution est de supprimer les clés et de rebooter.

reload/restart le démon ssh sur Debian sans passer par le script d’init

Pour reload :

# start-stop-daemon --stop --signal 1 --quiet --oknodo --pidfile /var/run/sshd.pid --exec /usr/sbin/sshd

Pour redémarrer :

# start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile /var/run/sshd.pid
# start-stop-daemon --start --quiet --pidfile /var/run/sshd.pid --exec /usr/sbin/sshd

Peut-on encore utiliser une clé SSH DSA ?

Les clés SSH avec l’algorithme DSA sont dépréciées depuis Stretch.

Limitation du temps de connexion via SSH

On peut limiter le temps d’une connexion SSH inactive en utilisant la variable d’environnement TMOUT. On peut provoquer une déconnexion automatique au bout d’1h d’activité en positionnant :

export TMOUT=3600

À l’inverse, si l’on veut éviter une déconnexion automatique quand cette variable est positionnée, on peut la supprimer ou l’augmenter :

$ unset TMOUT
$ export TMOUT=999999

Comment lister les clés SSH en mémoire d’un agent SSH ?

On peut utiliser la commande ssh-add -l :

$ ssh-add -l
256 71:fd:ba:ef:aa:94:27:14:05:d8:b6:db:28:e4:65:c2 /home/jdoe/.ssh/id_ed25519 (ED25519)
2048 2b:c8:ae:c0:d3:25:93:83:d4:41:4d:21:1e:80:2f:f4 rsa w/o comment (RSA)

À noter que l’on peut forcer un agent SSH à oublier les clés SSH à l’aide d’une des deux commandes suivantes :

$ ssh-add -D
All identities removed.

$ keychain --clear
 * ssh-agent: All identities removed.
 * gpg-agent: All identities removed.

On peut aussi verrouiller temporairement son agent SSH via :

$ ssh-add -x
Enter lock password:
Again:
Agent locked.

$ ssh-add -l
The agent has no identities.

$ ssh-add -X
Enter lock password:
Agent unlocked.

Ouvrir un tunnel, en passant par un bastion

Voici un exemple pour exposer localement un service VNC sur le port 5901 qui est accessible uniquement localement sur le port 5906 d’un serveur VNC, qui lui même n’est accessible que par un bastion :

ssh -J bastion.example.com -L 5906:127.0.0.1:5901 vnc-server.example.com

Comment protéger un serveur SSH des attaques ?

Voici plusieurs méthodes pour protéger son serveur SSH (qui peuvent se cumuler)

  • ne pas ouvrir son port SSH à l’extérieur à l’exception de certaines adresses IP
  • modifier le port d’écoute de son serveur SSH pour diminuer un peu les attaques automatiques (Port 2200 dans sshd_config)
  • utiliser du Port Knocking pour camoufler son port SSH
  • ne pas autoriser les connexions SSH avec un mot de passe (PasswordAuthentication no dans sshd_config)
  • configurer Fail2Ban pour bannir les adresses IP qui se trompent de mot de passe

Que ce passe t’il quand sshd_config contient à la fois AllowUsers et AllowGroups ?

C’est autorisé par le serveur OpenSSH donc sshd -t ne sortira pas d’erreur ! Au niveau de la connexion, la man page sshd_config(5) dit:

The allow/deny directives are processed in the following order: DenyUsers, AllowUsers, DenyGroups, and finally AllowGroups.

Cela signifie que lors d’une tentative de connexion SSH, la logique suivante est suivie :

Dans le cas où l’une de ces options n’est pas défini, on saute la ligne correspondante.

  • si l’utilisateur tentant de se connecter est listé dans DenyUsers alors il ne peut pas se connecter, sinon continuer,
  • si l’utilisateur tentant de se connecter n’est pas listé dans AllowUsers alors il ne peut pas se connecter, sinon continuer,
  • si l’utilisateur appartient à un groupe listé dans DenyGroups alors il ne peut pas se connecter, sinon continuer,
  • si l’utilisateur n’appartient pas à un groupe listé dans AllowGroups alors il ne peut pas se connecter, sinon continuer,
  • l’utilisateur peut se connecté.

Donc pour résumé : Si AllowUsers et AllowGroups sont définis dans la configuration du serveur OpenSSH alors il faut que l’utilisateur soit à la fois dans AllowUsers et AllowGroups pour pouvoir se connecter par SSH.

### SSH refuse d’ouvrir une session, ou nombre maximum de sessions SSH atteint

Côté client SSH, on peut rencontrer les erreurs suivantes :

Remote command execution failed: mux_client_request_session: session request failed: Session open refused by peer

Côté serveur, dans /var/log/auth.log :

sshd[14063]: error: no more sessions

Dans ce cas :

  1. Augmenter la valeur de MaxSessions dans /etc/ssh/sshd_config (par défaut à 10).
  2. Redémarrer le service ssh (faire un restart, pas un reload).
  3. Vérifier que le service tourne bien.
  4. S’il y a des sessions SSH persistantes (par exemple dans le cas d’un monitoring Nagios/Icinga), les tuer.

send_pubkey_test: no mutual signature algorithm

Lorsqu’on se connecte à un machine trop ancienne, il est possible que l’authentification par clé ne fonctionne pas alors que la clé publique est bien présente dans le fichier ~/.ssh/authorized_keys sur le serveur de destination.

Si en exécutant la commande ssh -v <host>, on lit send_pubkey_test: no mutual signature algorithm pour la clé en question, un contournement en attendant une mise à jour du service est d’utiliser cette commande :

$ ssh -o 'PubkeyAcceptedAlgorithms=+ssh-rsa' <host>

Pour ne pas avoir à ajouter l’option à chaque fois, on peut ajouter ces lignes dans le fichier ~/.ssh/config :

Host <host>
    PubkeyAcceptedAlgorithms=+ssh-rsa

Erreur kex_exchange_identification: Connection closed by remote host

Le serveur refuse les connexions entrante, parfois de manière aléatoire, car les valeurs indiquées dans MaxStartups de /etc/ssh/sshd_config sont dépassées.

Avant de les augmenter, il faut vérifier si on n’est pas en train de se faire brute-forcer.

Vérifier si on a bien activé les jails sshd et recidive de Fail2ban.

Utilisation des clés SSH par le client ssh

Par défaut, le client SSH tente d’utiliser en premier les clés suivantes si elle existe :

  • ~/.ssh/id_dsa
  • ~/.ssh/id_ecdsa
  • ~/.ssh/id_ecdsa_sk
  • ~/.ssh/id_ed25519
  • ~/.ssh/id_ed25519_sk
  • ~/.ssh/id_rsa

puis en cas d’échec il va essayer tous les fichiers dans ~/.ssh/ contenant des clés !

On peut aussi « forcer » à utiliser une clé SSH via l’option -i identity_file

Attention, on conseille donc de ne pas stocker ses « identity_file » alternatives dans le répertoire ~/.ssh/ car même avec l’option -i identity_file le client SSH va essayer toutes les clés présentes dans ce répertoire (pas seulement celles par défaut !). On conseille aussi d’utiliser l’option -o IdentitiesOnly=yes pour éviter ce comportement : en cas d’échec le client SSH ne « testera » que les clés listées plus haut.