Aller au contenu

Administration Unix - CM séance 18

18. Services réseau

18.1. Service ssh

Le terme ssh (Secure Shell) désigne un protocole et des outils pour effectuer des échanges sécurisés avec une machine distante.

ssh est à l'origine un produit commercial (1995) ; l'implémentation libre OpenSSH (1999) est devenue la norme.

18.1.1. Présentation

Le protocole ssh permet :

  • d'établir une console distante (commande ssh),
  • d'exécuter des commandes à distance (ssh),
  • de transférer des fichiers (scp, sftp),
  • de rediriger un environnement graphique (ssh -X ou -Y),
  • de véhiculer un autre protocole dans un tunnel (ssh -L ou -R),
  • etc.

Il est décrit par des RFCs :

  • 4251   architecture générale du protocole
  • 4252   protocole d'authentification
  • 4253   protocole de transport sécurisé
  • 4254   protocole de connexion
  • ...   (voir fin de man ssh)

Les commandes principales de la famille ssh sont :

ssh           commande principale (remplace rlogin, telnet, ...)
sftp          copie de fichier interactive (remplace ftp)
scp           copie de fichier directe (remplace rcp)
ssh-keygen    génère des clés d'identification
ssh-copy-id   copie des clés d'identification

Il y a d'autres utilitaires qui s'appuient sur ssh, par exemple :

rsync         copie ou mise à jour de fichiers
autossh       maintient une connexion et un tunnel ssh

Les paquets usuels sont :

openssh-client      commandes ssh
openssh-server      serveur ssh et configuration
rsync
autossh

La configuration se fait via plusieurs fichiers auto-documentés (il y a beaucoup de réglages possibles) :

/etc/ssh/sshd_config        configuration du serveur
/etc/ssh/ssh_config         connexions pour tous les utilisateurs

~/.ssh/ssh_config           configuration pour un utilisateur
                            (raccourcis, logins, ports, etc)

18.1.2. Première connexion

Soit l'utilisatrice alice loguée sur la machine alpha, et qui veut se connecter via ssh en temps que bob sur la machine beta ; elle tapera :

alice@alpha$ ssh bob@beta

Lors de la première connexion il y a un message :

The authenticity of host 'beta' can't be established.
ECDSA key fingerprint is SHA256:zViBXDMc/LFRWTv6BLwIcAwudYfnEa6rxWX14imXUSg.
Are you sure you want to continue connecting (yes/no)? yes

(il faut taper yes).

Warning: Permanently added 'beta' (ECDSA) to the list of known hosts.
bob@beta$ 

Une "trace" numérique de la machine beta est alors rajoutée dans le fichier ~/.ssh/known_hosts sur 1 ligne (assez longue) :

alice@alpha$ cat ~/.ssh/known_hosts
....
|1|muvGH5EqgJBxd3m+Z+DfJ4l+G3g=|k4qxnZ5gbIgXl5uDNiEI+fo92Vk= \
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA...
...5seTj1jYtA/OsYi+SbctCA9kPLQ6uQ9IZHnMJsGo=
....

Lors de chaque connexion, la trace sera comparée, pour prouver que c'est bien la machine beta.

Cette trace est en réalité composée de 2 parties :

  • des informations (adresse IP, port, ...) hachées (pour ne pas révéler d'informations sur les adresses distantes à un attaquant éventuel) ;
  • la clé publique de la machine distante :

    bob@beta cat /etc/ssh/ssh_host_ecdsa_key.pub
    ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA...
    ...5seTj1jYtA/OsYi+SbctCA9kPLQ6uQ9IZHnMJsGo= root@beta
    

Bien que la ligne identifiant le host soit automatiquement rajoutée par ssh, on peut aussi l'obtenir par :

alice@alpha$ ssh-keyscan -H -t ecdsa beta
# beta:22 SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1
|1|r6IPhNUs2BzFApmCIkkP6OEWCCs=|/EWjzRPS+xxaw1rBFYexBQkeI1k= \
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA...
...5seTj1jYtA/OsYi+SbctCA9kPLQ6uQ9IZHnMJsGo= root@beta

⟶ permet de scripter une mise à jour de known_hosts (rq : la partie hachée |1|...= change chaque fois).

Si la clé publique de beta change (réinstallation, ...) ou est usurpée, il y aura un message d'erreur :

bob@beta$ sudo apt purge openssh-server
bob@beta$ sudo apt install openssh-server       # réinstallation
....
Creating SSH2 ECDSA key; this may take some time ...
256 SHA256:+tPdpt7VECZMysTP5/mYvqqxJBemU/iTdkAkpKlky7s root@beta (ECDSA)
....

alice@alpha$ ssh bob@beta

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    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.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:+tPdpt7VECZMysTP5/mYvqqxJBemU/iTdkAkpKlky7s.
Please contact your system administrator.
Add correct host key in /home/alice/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/alice/.ssh/known_hosts:23
  remove with:
  ssh-keygen -f "/home/alice/.ssh/known_hosts" -R "beta"
ECDSA host key for beta has changed and you have requested strict checking.
Host key verification failed.

Solutions conseillées :

  • éditer known_hosts, supprimer la ligne n°23, ou
  • appeler la commande ssh-keygen affichée ci-dessus (c'est équivalent).

Il peut y avoir plusieurs lignes incriminées, dans ce cas recommencer jusqu'à ce que ça marche.

Autres solutions :

  • appeler ssh -o StrictHostKeyChecking=no bob@beta (aucune protection), ou
  • supprimer known_hosts (violent, on perd tous les hosts).

18.1.3. Authentification

L'authentification pour le protocole ssh se fait soit par un mot de passe, soit par une paire de clés (publique-privée).

  • par mot de passe : le mot de passe de l'utilisateur sur la machine distante est demandé pour chaque commande ssh/scp/sftp. C'est pénible pour l'utilisateur, et problématique pour les scripts.

  • par clé : la clé publique de l'utilisateur est explicitement autorisée sur une machine distante ; l'utilisateur n'a plus besoin de s'authentifier.

Étape 1 : générer une clé

alice@alpha$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/alice/.ssh/id_rsa): 
Created directory '/home/alice/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/alice/.ssh/id_rsa.
Your public key has been saved in /home/alice/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:huBCqb6v0vAxfGPGV0mpcyAJTlxSq90Ea5cRqpxJjUg alice@alpha
The key's randomart image is:
+---[RSA 2048]----+
| E+++.o. .       |
|.+.=o=.oo        |
|. * B.++ .       |
| = O =o.+        |
|..B.o o+S        |
|o +.* ..         |
| = * o           |
|. +              |
|.oo.             |
+----[SHA256]-----+

⟶ génération de

~/.ssh/id_rsa       clé privée, PERSONNE ne doit accéder à ce fichier
~/.ssh/id_rsa.pub   clé publique

alice@alpha$ cat ~/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
...
at2h0o3GCmd5ROlcqwAAAA50aGllbEBuYWtlZGViMQECAw==
-----END OPENSSH PRIVATE KEY-----

alice@alpha$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDgM+9gAZRA1Vlt4Q5lClJFRtgFRqxwYejxIii
... gMbtIRKeG/wFA9o8h1bLpnq8+h/ alice@alpha

La clé privée et la clé publique forment un couple lié par une propriété mathématique ; la robustesse tient en ce qu'il est impossible de déduire la clé privée de la clé publique.

Il existe plusieurs types de clés, on peut le spécifier par

$ ssh-keygen -t type

type est parmi

dsa         faible, déprécié depuis openssh 7.0
rsa         par défaut, fonctionne partout
ecdsa       relativement récent, non accepté partout ; clés plus courtes
ed25519     le plus récent et robuste, non accepté partout

On peut aussi spécifier la taille de la clé en nombre de bits (les choix dépendent du type) :

$ ssh-keygen -t rsa -b 4096

Une clé de chaque type peut être créée (ssh-keygen -A), la paire sera stockée dans les fichiers ~/.ssh/id_<type> et ~/.ssh/id_<type>.pub.

Compléments : on peut retrouver le hash et le randomart d'une clé publique avec

$ ssh-keygen -lvf fichier_clé.pub 

et afficher le randomart d'un host à la connexion avec l'option

$ ssh -o VisualHostKey=yes user@cible

Étape 2 : recopie de la clé

La deuxième étape consiste à envoyer la clé publique de l'utilisateur sur la machine distante :

ssh-copy-id -i clé_publique [user_distant@]machine_distante

Suite de notre exemple :

alice@alpha$ ssh-copy-id -i ~/.ssh/id_rsa.pub bob@beta
Source of key(s) to be installed: "/home/alice/.ssh/id_rsa.pub"
1 key(s) remain to be installed -- if you are prompted now it is to install 
the new keys
bob@beta's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'bob@beta'"
and check to make sure that only the key(s) you wanted were added.

alice@alpha$$ ssh bob@beta

Welcome to beta.
Last login: Mon Mar  8 15:24:42 2021 from alpha

bob@beta$ cat ~/.ssh/authorized_keys 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDgM+9gAZRA1Vlt4Q5lClJFRtgFRqxwYejxIii
... gMbtIRKeG/wFA9o8h1bLpnq8+h/ alice@alpha

Remarques :

  • sur certains vieux systèmes il n'y a pas la commande ssh-copy-id ; dans ce cas il suffit de copier à la main sa clé publique dans le fichier authorized_keys.

  • lors de la création de la clé on peut donner une passphrase (= un mot de passe sur 1 ou plusieurs mots) ; ce passphrase est alors demandé à chaque connexion.

    On peut ensuite mémoriser le mot de passe le temps d'une session avec ssh-agent / ssh-add :

    • ssh-agent est en général déjà lancé avec la session graphique en local, c'est lui qui stocke les clés et passphrase pour ssh ;
    • ssh-add transmet des clés/passphrase à ssh-agent :
    alice@alpha$ ssh bob@beta
    Enter passphrase for key '/home/alice/.ssh/id_rsa':
    
    alice@alpha$ ssh-add ~/.ssh/id_rsa              # ou : ssh-add
    Enter passphrase for /home/alice/.ssh/id_rsa: 
    Identity added: /home/alice/.ssh/id_rsa (alice@alpha)
    
    alice@alpha$ ssh bob@beta
    Welcome to beta!
    

    On peut enlever l'autorisation :

    alice@alpha$ ssh-add -d ~/.ssh/id_rsa
    Identity removed:/home/alice/.ssh/id_rsa (alice@alpha)
    
    alice@alpha$ ssh bob@beta
    Enter passphrase for key '/home/alice/.ssh/id_rsa':
    
  • ssh-keygen permet aussi de gérer des certificats pour des authentifications groupées.

18.1.4. Port ssh

Par défaut, le port du protocole ssh est le port 22.

Pour des raisons de sécurité, le port est parfois changé (c'est le cas pour le sas d'accès aux machines de TP à AMU).

Les commandes ssh ont une option pour spécifier le port distant :

ssh  -p port ...
scp  -P port ...            # -P en majuscule
sftp -P port ...            # idem

On peut aussi spécifier le port et d'autres informations dans : (man ssh_config)

/etc/ssh/ssh_config
~/.ssh/config

Par exemple si on a :

alice@alpha$ cat ~/.ssh/config
Host beta2
  HostKeyAlias beta2        # nom utilisé dans known_hosts
  HostName beta             # vrai nom de la machine, pour l'atteindre
  Port 22000
  User bob

alors

alice@alpha$ ssh beta2

sera presque équivalent à

alice@alpha$ ssh -p 22000 bob@beta

18.1.5. Tunnel ssh

La commande ssh permet de créer des tunnels (aussi appelés : redirections de port, ou port forwarding).

Étant donné 3 machines A, B, C, un tunnel ssh de A via B vers C

A  <===================>  B  <---------------------->  C
     tunnel ssh crypté         protocole quelconque

est un canal crypté entre A et B, tel que les paquets envoyés depuis A seront acheminés à B, puis envoyés par B à destination de C (et vice-versa).

Pour le mettre en place il faut choisir des ports :

                                                     démon
ssh                      sshd                      quelconque
A  <===================>  B  <---------------------->  C
  portA             portB   (portB')             portC

portA est le port local, portC est le port distant, portB est le port sshd. On peut choisir arbitrairement le port local, pourvu qu'il soit libre et > 1024.

La commande pour créer le tunel est

$ ssh -L portA:C:portC [-p portB] [user_b@]B

On peut alors appeler dans un autre terminal :

$ commande -p portA [user_c@]localhost

Comme le ssh de la première commande est en écoute sur le portA local, il demandera à sshd sur B de se connecter à C sur le portC en temps que user_c, et de lui transmettre les paquets.

ssh supporte le multiplexage : on peut faire simultanément plusieurs connexions sur le portA local.

On peut donc aussi "emboîter" des tunnels :

A  <========>  B  <-------->  C
A  <=======================>  C  <-------->  D
A  <======================================>  D  <-------->  E

Les tunnels ssh sont extrêmement utiles : ils permettent

  • d'accéder à des machines normalement inaccessibles via des sas ;
  • de crypter des protocoles normalement en clair (attention, que la partie matérialisée par <====>) ;
  • de créer et arrêter dynamiquement des tunnels.

Exemple 1

Connexion ssh sur gamma :

alice@alpha  <==========>  bob@beta  <---------->  carole@gamma
            10000       22                      22

Terminal 1 :

alice@alpha$ ssh -L 10000:gamma:22 bob@beta

Terminal 2 :

alice@alpha$ ssh -p 10000 carole@localhost
alice@alpha$ sftp -P 10000 carole@localhost

Exemple 2

Connexion http (port 80) sur delta :

alice@alpha  <==========>  bob@beta  <---------->  delta
            11000     5022                      80

Terminal 1 :

alice@alpha$ ssh -L 11000:delta:80 -p 5022 bob@beta

Terminal 2 :

alice@alpha$ wget http://localhost:11000/chemin/fichier

Compléments :

  • il est possible de créer des "tunnels inverses" avec ssh -R ;

  • il est possible de tunneler l'affichage X11 avec ssh -X ou ssh -Y ;

  • on peut spécifier des redirections de port dans ssh_config ;

  • il est possible de créer un VPN sur ssh (man ssh à la fin) ;

  • la commande autossh permet de maintenir des tunnels et les relancer si besoin ;

  • les commandes sshfs et fuse permettent à un user de monter un système de fichiers distant via ssh (paquet sshfs) :

    $ mkdir point_montage && sshfs [user@]hostname:[directory] point_montage
    $ fusermount -u point_montage && rmdir point_montage  # démontage
    

    ⚠ Ne pas utiliser sudo devant sshfs si on veut utiliser l'authentification par la clé publique de l'utilisateur (ce serait celle de root).

  • la commande rsync s'appuie sur ssh et permet de faire des transferts et mises à jour de fichiers et répertoires très efficaces (elle compare des dates et des empreintes MD5).

18.2. La résolution de noms

Elle permet d'associer un nom à une adresse IP.

18.2.1. Présentation

Il est possible d'associer quelques noms de machines à des adresses IP dans un fichier local ; mais au niveau de l'internet c'est impossible (taille, réseau décentralisé et changeant, répartition de charge, etc).

C'est pourquoi la résolution de nom s'appuie sur un système hiérarchique de domaines et un réseau distribué de serveurs de noms ; on appelle cet ensemble le DNS (Domain Name System, 1983).

  • Le système de noms de domaine est une hiérarchie telle que :

    • la racine est notée . (à la fin, souvent omise)

    • le nom complet est appelé le FQDN (Full Qualified Domain Name)

      machine[.sous-domaines].domaine.tld.
      
    • les domaines immédiatement sous la racine sont appelés domaine de premier niveau (TLD, Top Level Domain) : .org, .com, .fr, etc

    • les TDL sont gérés par des organismes agréés par l'ICANN (AFNIC pour .fr, Verisign pour .com, etc)

    • les enregistrements de noms de domaines dans un TLD sont confiés à des sociétés agréées, appelées des registrars (Gandi, Nordnet, Ovh, ...).

  • Le réseau de serveurs du DNS :

    • est distribué, pour résister aux pannes et attaques, et limiter la taille de leur base de données ;

    • lorsqu'un client veut obtenir une adresse, il demande à un serveur DNS récursif, qui soit lui répond s'il a l'adresse en cache, soit demande de proche en proche à d'autres serveurs.

    • des serveurs racine répondent aux requêtes de premier niveau (TLD) et les redirige vers des serveurs du TLD concerné ;

    • Une association nom = adresse IP est faite à la base dans 1 ou 2 serveurs DNS ; ont dit qu'ils font autorité pour ce nom.

Des outils de test permettent d'interroger les DNS (paquet dnsutils) : traceroute, dig, whois, ...

Voir : https://fr.wikipedia.org/wiki/Domain_Name_System

18.2.2. Configuration d'un client

Le nom de la machine est le contenu du fichier : /etc/hostname.

Le comportement de la machine pour obtenir des informations (noms d'hôtes, utilisateurs, mots de passe, etc) est conditionné par le fichier : /etc/nsswitch.conf (Name Service Switch : man nsswitch.conf).

Ce fichier contient par exemple :

passwd:         compat
group:          compat
shadow:         compat

hosts:          dns [!UNAVAIL=return] files
networks:       nis [NOTFOUND=return] files
ethers:         nis [NOTFOUND=return] files
protocols:      nis [NOTFOUND=return] files
rpc:            nis [NOTFOUND=return] files
services:       nis [NOTFOUND=return] files

La première colonne contient un nom de base de données ; la suite d'une ligne définit l'ordre dans lequel il faut chercher les informations.

Par exemple pour la résolution des noms en adresse IP :

hosts:      files dns

signifie qu'il faut d'abord regarder dans la base locale, et sinon, utiliser le DNS ;

hosts:      dns [!UNAVAIL=return] files

signifie qu'il faut d'abord utiliser le DNS, et que si les DNS ne sont pas injoignables de façon permanente, alors on arrête maintenant la recherche ; sinon, on regarde la base locale.

Dans le cas où la recherche de noms est faite dans la base locale (cas files), cette base est le fichier /etc/hosts qui contient des lignes sous la forme :

ip    nom_hôte  alias...

Par exemple :

127.0.0.1       localhost                                     # loopback
127.0.1.1       tesla.dil.univ-amu.fr tesla                   # idem
::1             ip6-localhost ip6-loopback                    # idem en IPV6
192.168.20.40   xubuntu2004 xubu1                             # une VM
186.2.163.31    sci-hub.do moscow.sci-hub.do zero.sci-hub.do  # sci-hub

Le DNS est configuré dans le fichier : /etc/resolv.conf qui contiendra des directives de la forme : (man resolv.conf)

search mondomaine1 mondomaine2
nameserver 127.0.1.1

⚠  Ce fichier peut être modifié dynamiquement par le système pour permettre de travailler avec des connexions via plusieurs interfaces, ou lorsque les serveurs de noms sont obtenus pas DHCP :

  • par networkmanager
  • par resolvconf(8)

Remarque : la commande getent (get entry : man getent) permet d'interroger les bases définies dans nsswitch.conf :

$ getent passwd
$ getent passwd thiel
$ getent hosts
$ getent hosts linuxfr.org
$ getent rpc
$ getent services ssh

18.2.3. Configuration d'un serveur bind

Paquets bind9, voire bind9-doc et dnsutils.

Configuration : fichier /etc/bind/named.conf.

Sous Debian et dérivés, ce fichier est éclaté dans :

named.conf                      fichier principal, appel les autres
named.conf.options              options de configuration
named.conf.local                définition des zones du DNS local
named.conf.local.default-zones  définition de zones par défaut

et des Ressources Records (RRs) qui contiennent les informations des zones locales et inverses, définies dans named.conf.local.

Le statut et démarrage du serveur est obtenu par systemctl :

$ systemctl status bind9
$ systemctl start|stop|restart|reload bind9

Exemple de fichier named.conf :

include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
include "/etc/bind/named.conf.default-zones";

Exemple de fichier named.conf.options :

# définit le groupe d'hôtes "internals" ; ses autorisations seront ensuite
# données dans le bloc options.
acl internals { 127.0.0.0/8; 192.168.0.0/16; };

options {
    # Répertoire de travail de bind
    directory "/var/cache/bind";

    # contrôle le comportement de retransmission d'une directive forwarders :
    # - first : les serveurs de noms dans forwarders sont d'abord interrogés
    #           avant que named le fasse lui-même ;
    # - only :  ne consulte que les forwarders.
    forward only;

    # Liste d'IP valides de serveurs de noms pour la résolution
    forwarders { 192.168.1.1; };

    # interfaces réseaux par lesquelles on accepte des requêtes
    listen-on { 127.0.0.1; 192.168.10.50; 192.168.20.50; }
    listen-on-v6 { none; };

    # autorise uniquement ces hôtes à interroger ce serveur,
    # par défaut tous ; l'inverse est : blackhole { ... };
    allow-query { internals };

    # idem pour faire des demandes récursives
    allow-recursion { internals };
};

Il y a de nombreuses choses à configurer, voir :