SSH

Published: 02-02-2015

Updated: 18-03-2018

By: Maxime de Roucy

tags: ssh

Mes notes concernant SSH.

known_hosts

Pour empécher l’enregistrement de la clé d’un host dans ~/.ssh/known_hosts, pour certaines machines. Dans le ~/.ssh/config ajouter :

Host 192.168.0.*
   StrictHostKeyChecking no
   UserKnownHostsFile=/dev/null

Ça peut être pratique lorsqu’on se connecte sur des machines virtuelle qui change régulièrement d’IP.

authorized_keys

Le fichier ~/.ssh/authorized_keys2 s’il est présent est pris en compte par ssh. La valeur par défaut de AuthorizedKeysFile est “.ssh/authorized_keys .ssh/authorized_keys2”.

tunnel

Il est possible de créer des tunnel ssh avec les option -L et -R. Ces options permettent respectivement de faire la redirection d’un port sur la machine local ou distante, vers une adresse est un port donnée, toute en faisant en sorte que les données transites par la connection ssh.

redirection locale

max@laptop % ssh -L 8000:serveur.un:80 serveur.deux

La commande précédente redirige le port 8000 de la machine local laptop vers le port 80 de la machine serveur.un tout en faisant en sorte que les données transite par la connection ssh. Les données sont donc vue par serveur.un comme arrivant de serveur.deux.

Un segment de donnée envoyé sur laptop:8000 passera par la connection ssh et sera envoyé par serveur.deux à server.un:80.

redirection distante

max@laptop % ssh -R 8000:serveur.un:80 serveur.deux

La commande précédente redirige le port 8000 de la machine distante serveur.deux vers le port 80 de la machine serveur.un tout en faisant en sorte que les données transite par la connection ssh. Les données sont donc vue par serveur.un comme arrivant de laptop.

Un segment de donnée envoyé sur serveur.deux:8000 passera par la connection ssh et sera envoyé par laptop à server.un:80.

contrôle via une socket

TODO

proxy SOCKS

Il est possible d’utiliser SSH comme un proxy SOCKS ça permet de naviguer avec sont navigateur web « comme si » on était de l’autre coté de la connection ssh.

ssh (6.9p1) support les version 4 et 5 du protocol SOCKS. La version 5 ajoute entre autre :

exemple

Démarrage le proxy SOCKS.

max@laptop % ssh -D 3128 server

Configuration de Firefox pour utiliser le proxy SOCKS.

image : configuration de firefox pour utiliser le proxy SOCKS

J’ai coché la case « Remote DNS » pour que la résolution des noms de domaines se fasse sur la machine « server » et non sur « laptop ».

À partir de là, la navigation web se passe « comme si » Firefox était lancé sur la machine « server » et non « laptop ».

TTY

L’option « -t » permet de forcer l’allocation d’un tty.

CLI

Quelques options de la commande ssh :

Lorsque l’option « -f » n’est pas utilisé, ssh s’arrête dès qu’il reçoit un EOF dans sont entrée standard (/dev/null retourne un EOF). ssh -N -n … et ssh -N … < /dev/null s’arrête donc tous de suite.

ssh_config

Le fichier est lu complètement mais la première valeur de chaque paramètre est utilisée. Il faut donc mettre les configuration les plus spécifique en premier.

Une exception, IdentityFile dont toutes les valeurs sont prisent en compte. Les clés sont testé auprès du serveur suivant leur ordre d’apparition.

It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. Multiple IdentityFile directives will add to the list of identities tried (this behaviour differs from that of other configuration directives).

Le client ssh tente de s’authentifier avec les paires de clés spécifié via l’option -i puis celles spécifié via le keyword IdentityFile (dans leur ordre d’apparition dans le fichier de configuration) puis celles enregistré dans l’agent ssh (si le keyword IdentitiesOnly est à « no »)

Include

Attention, si « Include » est déclaré après un keyword « Host » ou « Match » il n’est appliqué que lorsque le host accédé match le keyword.

ProxyJump

Sources :

ProxyJump indique à ssh de passer par un proxy SSH/bastion. C’est un wrapper à ProxyCommand (plus compliqué à utiliser).

Host 10.*
	ProxyJump max@bastion.exemple.net:22

ControlMaster

Sources :

ControlMaster auto permet de garder la connexion ouverte (via une socket) pendant quelques temps après que l’utilisateur ai quitté sa session ssh. auto indique que la socket doit être créée si elle n’existe pas déjà.

Host …
	User max
	# Enables the sharing of multiple sessions over a single network connection
	ControlMaster auto
	# Path to the control socket used for connection sharing
	# ‘%C’ is the concatenation: %l%h%p%r
	# ‘%l’ will be substituted by the local host name (including any domain name),
	# ‘%h’ will be substituted by the target host name,
	# ‘%p’ the destination port,
	# ‘%r’ by the remote login username,
	# This ensures that shared connections are uniquely identified.
	ControlPath ~/.ssh/sockets/%C
	# la socket reste ouvert 10 minutes après la fermeture de la dernière session interactive
	ControlPersist 600

Le répertoire de stockage des sockets doit être créé au préalable (avec les droits 0700).

ssh-keygen

Quelques options de ssh-keygen que j’ai utiliser quelques fois, mais pas assez souvent pour que je m’en souviennent :

scp

L’option -3 permet de scp entre deux serveurs distants.

max@laptop % scp -C -r -3 server1:/toto server2:/titi

L’option -r permet de copier récursivement. Elle follow les symlink.

Récupérer un dossier distant sous forme d’archive

Récupérer le dossier titi contenu dans le dossier /toto sous forme d’une archive (on peut aussi ajouter de la compression).

max@laptop % ssh server "tar cf - -C /toto titi" > titi.tar

escape sequences

Certaines séquences de caractères sont directement interprété par le client ssh local :

max@max-server % ~?
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.)

Pour stopper une connexion ssh sur un server qui ne répond plus sans avoir à quitter le shell courant :

<Entré>~~.

socat

Source : blog.rom1v.com: Serveur-client

Pour aider quelqu’un il est parfois interressant d’avoir la main sur sont ordi… donc : connexion ssh. Mais si cette persone n’est pas geek et ne sais pas faire de redirection de port, d’ouverture de firewall… ça peut facilement devenir compliquer.

Le problème vient du fait que je veut, en tant que client, me connecter au serveur ssh de ladite personne, qui se trouve généralement derrière un pare-feu et/ou un NAT.

Pour remédier à ce problème je transforme mon client en serveur et le serveur en client.

D’abord je fait en sorte d’être joignable sur le port 12345 depuis l’exterieur (ouverture de port sur le firewall + redirection au niveau du NAT). Je lance ensuite un serveur socat qui écoute sur les port 12345 (qui servira à mon ami pour se connecter), et 23456 sur lequel je me connecterai en ssh. Lorsque qu’un client se sera connecté sur le port 12345 et un autre sur port 23456 alors tous le traffic qui arrivera d’une connection sera transférée vers l’autre.

max@laptop % socat -d -d tcp-listen:12345 tcp-listen:23456

Ensuite je demande à mon ami de lancer la commande suivante. Elle a pour effet de se connecter au serveur socat précédent et au serveur ssh de mon ami, et de transférer tous ce qui transite sur l’une des connection vers l’autre.

truc@ami % socat tcp:<mon-addresse-public>:12345 tcp:127.0.0.1:22

À ce stade, c’est « comme si » le serveur ssh de mon ami écouté sur le port 23456 de ma machine. Je peut donc m’y connecter via la commande suivante :

max@laptop % ssh -p 23456 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no truc@127.0.0.1

notes

Les options -d -d me permette de détecter lorsque mon ami s’est connecté.

Dès que l’une des connections est rompu, tous les « socat » sont terminé.

ssh-agent

ssh-agent est un outil permettant de stocker en RAM des paires de clés (+ certificats) déchiffrées.

ssh-add est le principal programme qui permet de manipuler une instance ssh-agent tournant en tache de fond.

utiliser une clé spécifique de l’agent

Lorsque le client ssh se connecte à un serveur distant sans que ne soit utilisé l’option -i ou le keyword IdentityFile par défaut celui-ci tests toutes les clés stockées dans l’agent.

Il est possible de désactiver ce comportement via le keyword IdentitiesOnly (à « no » par défaut).

max@laptop % ssh-add -L
ssh-ed25519 … server
max@laptop % ssh -G server | grep -i -e IdentitiesOnly -e IdentityFile
identitiesonly no
max@laptop % ssh server echo toto
toto
max@laptop % ssh -o IdentitiesOnly=yes server echo toto
Permission denied (publickey).

Avec « IdentitiesOnly=no » (valeur par défaut), si l’agent contient beaucoup de clés, le serveur peut rejeter la connexion suite à un nombre trop important de tentative. Pour remédier à ce problème on peut spécifier une clé privée ou public sous forme de fichier avec l’option -i (ou le keyword IdentityFile), ssh utilisera alors automatiquement la version déchiffré de la clé privée stockée dans l’agent correspondant à la clé publique correspondant au fichier passé en paramètre.

C’est le seul moyen que j’ai trouvé pour permettre d’utilisation d’une clé spécifique de l’agent.

Exemple avec ssh -i ~/.ssh/a server~/.ssh/a est chiffrée :

  1. le client ssh récupère la clé publique ~/.ssh/a.pub
    • si ce fichier n’existe pas le client ssh demande à l’utilisateur le mot de passe permettant de déchiffrer ~/.ssh/a
  2. il envoie cette clé publique (oui, je suis sur de moi ☺) au serveur pour acceptation/rejet
  3. si le serveur accepte la clé :
    1. il cherche dans l’agent une version déchiffré de la clé privée correspondant à cette clé publique
      • s’il n’en trouve pas il demande à l’utilisateur le mot de passe permettant de déchiffrer ~/.ssh/a
    2. il utilise cette clé pour terminer l’authentification
  4. si le serveur rejette cette clé le client ssh passe à la paire de clé suivante (si une autre paire de clé est configuré/disponible)

Exemple avec ssh -i ~/.ssh/a.pub server :

  1. le client ssh envoie cette clé publique au serveur pour acceptation/rejet
  2. si le serveur accepte la clé :
    1. le client ssh cherche dans l’agent une version déchiffré de la clé privée correspondant à cette clé publique
      • s’il n’en trouve pas il fail car dans ce cas il considère que a.pub est une clé privée… ce qui n’est pas le cas.
    2. il utilise cette clé pour terminer l’authentification
  3. si le serveur rejette cette clé le client ssh passe à la paire de clé suivante (si une autre paire de clé est configuré/disponible)

Si vous ne disposez pas de la paire de clé sous forme de fichier, vous pouvez extraire la clé publique via ssh-add.

max@laptop % ssh-add -L
ssh-rsa … autre
ssh-ed25519 … max@laptop
max@laptop % ssh-add -L | grep 'ssh-ed25519 .* max@laptop' > tempfile-pub
max@laptop % ssh -i tempfile-pub server echo 'toto'
toto

Lorsque le serveur demande un certificat vous pouvez aussi l’extraire de l’agent.

max@laptop % ssh-add -L
ssh-rsa … autre
ssh-ed25519 … max@laptop
ssh-ed25519-cert-v01@openssh.com … max@laptop
max@laptop % ssh-add -L | grep 'ssh-ed25519-cert.* max@laptop' > tempfile-cert
max@laptop % ssh -i tempfile-cert server echo 'toto'
toto

certificats

ssh dispose d’un système de certificats. Il y a deux types de certificats, les certificats utilisateur et les certificats host/server. Pour faire simple ça permet, sur un serveur, d’accepter toutes les connections venant de clients ayant une clé signé par une autorité de certifications ; sans pour autant que toutes les clés publique de ces clients soit présente sur le serveur. De la même façon, sur un client, il est possible de considérer comme legit tous les serveurs dont la clé est signé par une autorité de certification ; sans avoir à lister les clés publique de tous les serveur dans le fichier known_hosts.

Un certificat est associé à un couple de clés privée/publique.

max@laptop % ls ~/.ssh/id_ed25519*
/home/max/.ssh/id_ed25519  /home/max/.ssh/id_ed25519-cert.pub  /home/max/.ssh/id_ed25519.pub

Dans la suite de cette section je ne parlerai que de certificats utilisateur.

Par défaut, lorsqu’un client se connecte à un serveur avec un clé privée ~/.ssh/toto ssh envoie le certificat ~/.ssh/toto-cert.pub si ce fichier existe. Il est possible de créer plusieurs certificats pour un même couple de clés. Il est aussi possible d’utiliser plusieurs certificats durant la connection d’un client à un serveur, dans ce cas les certificats sont testé un à un jusqu’à ce qu’un certificat foncitonnel soit découvert.

Le couple de clés du CA (clés qui vont permettre de créer et valider les certificats) est un couple de clés ssh standard.

max@laptop % ssh-keygen -t ed25519 -C CA -f …/ca
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in …/ca.
Your public key has been saved in …/ca.pub.
The key fingerprint is:
…
The key's randomart image is:
…

Afficher le contenu d’un certificat :

max@laptop % ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub
/home/max/.ssh/id_ed25519-cert.pub:
        Type: ssh-ed25519-cert-v01@openssh.com user certificate
        Public key: ED25519-CERT SHA256:…
        Signing CA: ED25519 SHA256:…
        Key ID: "max@laptop"
        Serial: 1
        Valid: forever
        Principals:
                shell_all
                gitolite_admin
        Critical Options: (none)
        Extensions:
                permit-X11-forwarding
                permit-agent-forwarding
                permit-port-forwarding
                permit-pty
                permit-user-rc

Pour créer le certificat précèdent j’ai utilisé la commande suivante :

max@laptop % ssh-keygen -s …/ca -I max@laptop -n shell_all,gitolite_admin -z 1 ~/.ssh/id_ed25519.pub
Enter passphrase:
Signed user key /home/max/.ssh/id_ed25519-cert.pub: id "max@laptop" serial 1 for shell_all,gitolite_admin valid forever

J’utilise le sshd_config suivant sur mon serveur :

# the public key of the CA, used to validate/authenticat certificats
TrustedUserCAKeys /etc/ssh/ca.pub
# the KRL, to explicitely forbid some certificats
RevokedKeys /etc/ssh/KRL
# to restricts the access to/for some principals
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u

# disable standard ~/.ssh/authorized_keys{,2}
AuthorizedKeysFile none

# disable password authentication
PasswordAuthentication no
ChallengeResponseAuthentication no
# Process account and session PAM module
usePAM yes
PrintMotd no # PAM does that

À la ligne AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u, %u est remplacé par le nom de l’utilisateur auquel le client essaye d’accéder sur le serveur. E.g. toto@client % ssh titi@server : %u sera remplacé par « titi ».

Mes fichiers AuthorizedPrincipalsFile :

root@serveur # tail -n+1 /etc/ssh/auth_principals/*
==> /etc/ssh/auth_principals/git <==
restrict,command="/usr/libexec/gitolite/gitolite-shell admin" gitolite_admin
restrict,command="/usr/libexec/gitolite/gitolite-shell rwaccess" gitolite_rwaccess

==> /etc/ssh/auth_principals/max <==
shell_all
shell_max

==> /etc/ssh/auth_principals/root <==
shell_all

Ce qui se traduit par :

Dans un fichier AuthorizedPrincipalsFile (la première ligne) on ne peut mettre qu’un seul principal par ligne ; potentiellement précédé d’options.

Lorsqu’il contient des options celles-ci sont fusionnée avec les options du certificats de la manière la plus restrictive possible. Dit autrement, l’union la plus restrictive de ces deux liste est utiliser.

Attention, le premier principal du AuthorizedPrincipalsFile qui match l’un des principals du certificat client est utiliser. Si un client possédant un certificat contenant les principals gitolite_admin et gitolite_rwaccess essaye de se connecter en tant que git, la commande /usr/libexec/gitolite/gitolite-shell admin sera utilisée. Il ne sera pas possible pour ce client de faire en sorte que la commande /usr/libexec/gitolite/gitolite-shell rwaccess soit utilisée (en tout cas j’ai rien trouvé).

Pour créer/mettre à jour un KRL je vous conseil d’utiliser un KRL-spec-file. Il n’est actuellement (23/07/2017) pas possible de lister les clés/dumper le contenu d’une KRL (cf. www.lorier.net et openssh-unix-dev mailing list). Le KRL-spec-file fait donc office de version text du KRL. Je conserve un historique de toutes les créations de certificat en commentaire dans mon KRL-spec-file.

max@laptop % cat KRL-spec-file
# last  KRL serial: 1
# last cert serial: 3
#
# OK      -z    1 -I        max@laptop          -n shell_all,gitolite_admin
# REVOKED -z    2 -I         toto@titi          -n test
serial: 2
# OK      -z    3 -I     backup@srv-01 -O clear -n backup
max@laptop % ssh-keygen -k -f KRL -s ca -z 1 KRL-spec-file
Revoking from KRL-spec-file

L’option -z permet de spécifier le numéro de version (serial) du KRL (nombre entier positif sur 64bits). Ça permet de déterminer par la suite si un serveur possède ou non un KRL à jour.

Pour déterminer le numéro de version d’un KRL binaire on peut utiliser hexdump.

max@laptop % for i in {0..3} 16 $(seq 18446744073709551614 $(echo '2^64' | bc))
do
echo "###### serial : $i"
ssh-keygen -k -f $i -s …/ca -z $i …/KRL-spec-file
hexdump -C $i | head -n 2
echo
done
###### serial : 0
Revoking from …/KRL-spec-file
00000000  53 53 48 4b 52 4c 0a 00  00 00 00 01 00 00 00 00  |SSHKRL..........|
00000010  00 00 00 00 00 00 00 00  59 74 8f d0 00 00 00 00  |........Yt......|

###### serial : 1
Revoking from …/KRL-spec-file
00000000  53 53 48 4b 52 4c 0a 00  00 00 00 01 00 00 00 00  |SSHKRL..........|
00000010  00 00 00 01 00 00 00 00  59 74 8f d0 00 00 00 00  |........Yt......|

###### serial : 2
Revoking from …/KRL-spec-file
00000000  53 53 48 4b 52 4c 0a 00  00 00 00 01 00 00 00 00  |SSHKRL..........|
00000010  00 00 00 02 00 00 00 00  59 74 8f d0 00 00 00 00  |........Yt......|

###### serial : 3
Revoking from …/KRL-spec-file
00000000  53 53 48 4b 52 4c 0a 00  00 00 00 01 00 00 00 00  |SSHKRL..........|
00000010  00 00 00 03 00 00 00 00  59 74 8f d0 00 00 00 00  |........Yt......|

###### serial : 16
Revoking from …/KRL-spec-file
00000000  53 53 48 4b 52 4c 0a 00  00 00 00 01 00 00 00 00  |SSHKRL..........|
00000010  00 00 00 10 00 00 00 00  59 74 8f d0 00 00 00 00  |........Yt......|

###### serial : 18446744073709551614
Revoking from …/KRL-spec-file
00000000  53 53 48 4b 52 4c 0a 00  00 00 00 01 ff ff ff ff  |SSHKRL..........|
00000010  ff ff ff fe 00 00 00 00  59 74 8f d0 00 00 00 00  |........Yt......|

###### serial : 18446744073709551615
Revoking from …/KRL-spec-file
00000000  53 53 48 4b 52 4c 0a 00  00 00 00 01 ff ff ff ff  |SSHKRL..........|
00000010  ff ff ff ff 00 00 00 00  59 74 8f d0 00 00 00 00  |........Yt......|

###### serial : 18446744073709551616
Invalid serial number "18446744073709551616"
hexdump: 18446744073709551616: No such file or directory
hexdump: all input file arguments failed

On peut voir que le numéro de version est stocker sur 8 octet à partir du 12ieme. On peut utiliser od pour afficher uniquement le numéro de version (on ne peut pas utiliser hexdump car il ne permet pas de spécifier l’endianness).

max@laptop % for i in {0..3} 16 $(seq 18446744073709551614 $(echo '2^64' | bc))
do
echo "###### serial : $i"
ssh-keygen -k -f $i -s …/ca -z $i …/KRL-spec-file
od --endian=big --format u8 --skip-bytes=12 --read-bytes=8 $i | head -n 1
done
###### serial : 0
Revoking from ../KRL-spec-file
0000014                    0
###### serial : 1
Revoking from ../KRL-spec-file
0000014                    1
###### serial : 2
Revoking from ../KRL-spec-file
0000014                    2
###### serial : 3
Revoking from ../KRL-spec-file
0000014                    3
###### serial : 16
Revoking from ../KRL-spec-file
0000014                   16
###### serial : 18446744073709551614
Revoking from ../KRL-spec-file
0000014 18446744073709551614
###### serial : 18446744073709551615
Revoking from ../KRL-spec-file
0000014 18446744073709551615
###### serial : 18446744073709551616
Invalid serial number "18446744073709551616"
od: 18446744073709551616: No such file or directory

Sur le serveur, lorsqu’un utilisateur s’authentifie avec son couple clé/certificat, on obtient le log suivant. Comme indiqué plus haut, contrairement à l’authentification par clé classique, le log permet d’identifier rapidement l’auteur de la connexion grâce au « CERT ID » (« Key ID » du certificat).

juil. 23 16:58:57 server sshd[28233]: Accepted publickey for root from 1.2.3.4 port 45908 ssh2: ED25519-CERT ID max@laptop (serial 1) CA ED25519 SHA256:…