Le serveur openssh, implémentation libre du protocole SSH proposée à l’origine par Theo De Raadt pour OpenBSD, est à mon avis une pépite du logiciel libre. J’estime que c’est probablement le daemon qui est le plus sécurisé de mes serveurs Linux, loin devant le serveur web par exemple. Je m’intéresse néanmoins aux astuces permettant de limiter les risques et vulnérabilités sous Linux (Debian 9, raspbian pour Raspberry, …).
Mise en place de clés d’authentification
En théorie, la meilleure méthode d’authentification auprès d’un serveur SSH, c’est la clé générée avec une passphrase. Cela constitue une authentification à deux niveau avec un élément à posséder (la clé) et un élément à connaitre (la passphrase).
Les assaillants qui ne possèdent pas la clé sont directement rejetés, c’est l’idéal. Les attaques bruteforce sont contrecarrées.
Pour mettre en place une clé sur votre client SSH c’est assez simple avec la commande ssh-keygen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
user@ordinateur:~# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/home/user/.ssh/id_rsa): Created directory '/home/user/.ssh'. Enter passphrase (empty for no passphrase): <votre-passphrase> Enter same passphrase again: <votre-passphrase> Your identification has been saved in /home/user/.ssh/id_rsa. Your public key has been saved in /home/user/.ssh/id_rsa.pub. The key fingerprint is: SHA256:AjG[...] user@ordinateur The key's randomart image is: +---[RSA 2048]----+ | .. ooxx. | | ..xxxx | | . *xx. | |. *+ ..+ | |E. * + S | +----[SHA256]-----+ |
Pour que le serveur SSH distant reconnaisse la clé présentée par le client, il faut lui communiquer. La commande ssh-copy-id permet d’ajouter automatiquement la clé dans serveur:~/.ssh/authorized_keys .
1 2 3 4 5 6 7 8 9 10 11 12 13 |
user@ordinateur:~$ ssh-copy-id user@serveur /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_rsa.pub" The authenticity of host 'serveur' can't be established. ECDSA key fingerprint is SHA256:jfgbg[..]. Are you sure you want to continue connecting (yes/no)? yes /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 user@serveur's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'user@serveur'" and check to make sure that only the key(s) you wanted were added. |
La tentative de connexion suivante de l’utilisateur user auprès de serveur permettra normalement l’utilisation de la clé. Pour le vérifier on peut utiliser le mode verbose.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
user@ordinateur:~$ ssh -v user@serveur OpenSSH_7.4p1 Debian-10+deb9u2, OpenSSL 1.0.2l 25 May 2017 debug1: Reading configuration data /etc/ssh/ssh_config debug1: /etc/ssh/ssh_config line 19: Applying options for * debug1: Connecting to localhost [::1] port 22. debug1: Connection established. debug1: identity file /home/user/.ssh/id_rsa type 1 debug1: key_load_public: No such file or directory debug1: identity file /home/user/.ssh/id_rsa-cert type -1 debug1: key_load_public: No such file or directory debug1: identity file /home/user/.ssh/id_dsa type -1 debug1: key_load_public: No such file or directory debug1: identity file /home/paul2/.ssh/id_dsa-cert type -1 debug1: key_load_public: No such file or directory debug1: identity file /home/user/.ssh/id_ecdsa type -1 debug1: key_load_public: No such file or directory debug1: identity file /home/user/.ssh/id_ecdsa-cert type -1 debug1: key_load_public: No such file or directory debug1: identity file /home/user/.ssh/id_ed25519 type -1 debug1: key_load_public: No such file or directory debug1: identity file /home/user/.ssh/id_ed25519-cert type -1 debug1: Enabling compatibility mode for protocol 2.0 debug1: Local version string SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u2 debug1: Remote protocol version 2.0, remote software version OpenSSH_7.4p1 Debian-10+deb9u2 debug1: match: OpenSSH_7.4p1 Debian-10+deb9u2 pat OpenSSH* compat 0x04000000 debug1: Authenticating to localhost:22 as 'paul2' debug1: SSH2_MSG_KEXINIT sent debug1: SSH2_MSG_KEXINIT received debug1: kex: algorithm: curve25519-sha256 debug1: kex: host key algorithm: ecdsa-sha2-nistp256 debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none debug1: expecting SSH2_MSG_KEX_ECDH_REPLY debug1: Server host key: ecdsa-sha2-nistp256 SHA256:yFSlcVYqt0ICjcHMVnxfPif309AxaXvAOIPH/yvelqQ debug1: Host 'localhost' is known and matches the ECDSA host key. debug1: Found key in /home/user/.ssh/known_hosts:1 debug1: rekey after 134217728 blocks debug1: SSH2_MSG_NEWKEYS sent debug1: expecting SSH2_MSG_NEWKEYS debug1: SSH2_MSG_NEWKEYS received debug1: rekey after 134217728 blocks debug1: SSH2_MSG_EXT_INFO received debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521> debug1: SSH2_MSG_SERVICE_ACCEPT received debug1: Authentications that can continue: publickey,password debug1: Next authentication method: publickey debug1: Offering RSA public key: /home/user/.ssh/id_rsa debug1: Server accepts key: pkalg ssh-rsa blen 279 debug1: Authentication succeeded (publickey). Authenticated to localhost ([::1]:22). debug1: channel 0: new [client-session] debug1: Requesting no-more-sessions@openssh.com debug1: Entering interactive session. debug1: pledge: network debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0 debug1: Sending environment. debug1: Sending env LANG = fr_FR.UTF-8 Linux serveur 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: Thu Feb 1 12:14:20 2018 user@serveur:~$ |
Après cette vérification on pourra interdire l’utilisation du mot de passe simple auprès du serveur avec la directive PasswordAuthentication no et PermitRootLogin prohibit-password dans le fichier de configuration coté serveur /etc/ssh/sshd_config.
Si vous mettez en place les clés d’authentification, vous vous mettez en excellente posture du point de vue de la sécurité. Les mesures qui suivent ne me semblent du coup pas obligatoires, juste conseillées si vous recherchez la protection maximale.
Sécuriser openssh
En pratique, l’utilisation du mot de passe est bien pratique. Cela fonctionne par défaut, aussi bien avec un client SSH Linux qu’avec une appli smartphone ou tablette. Le mot de passe est un procédé ancestral et universel très largement adopté.
Les mesures qui suivent permettent de sécuriser l’utilisation du protocole SSH même en utilisant l’authentification par mot de passe.
Emploi des bons algorithmes de chiffrement
Dans le cadre de cet article, je m’intéresse plus à l’administration systèmes et au bonnes pratiques de hardening SSH. La confidentialité des échanges dépend des algorithmes de chiffrement utilisés d’une part pour l’authentification, et d’autre part pour le cryptage des échanges lorsque la connexion est établie. Cet article décrit très bien les algorithmes à autoriser et à interdire.
Interdire root avec un mot de passe
Coté serveur, la directive PermitRootLogin no permet d’empêcher que l’utilisateur root ne se logue par SSH, autant par clé que par mot de passe. Pour mettre en place cette interdiction seulement pour le mot de place on remplacera « no » par « prohibit-password ». C’est la configuration par défaut sur Debian.
Limiter le nombre de tentatives d’authentification par connexion
La directive MaxAuthTries <num> permet de limiter le nombre d’invites d’authentification. Les tentatives comptabilisées concernent à la fois la clé et le mot de passe. Les clients SSH ont tendance à proposer leur clé en premier puis à se rabattre sur le mot de passe en cas d’échec, si bien que la valeur numérique renseignée doit être choisie en tenant compte de cela.
Timeout d’authentification
Le délai de fermeture de connexion en cas de mot de passe ou de passphrase non renseigné est configurable avec la directive LoginGraceTime.
Modification du port d’écoute
La modification des ports standards pour l’écoute des services est intéressante, car les attaques automatiques sont calibrées justement pour des systèmes standards. Peu d’assaillants essaieront d’attaquer un serveur SSH par le port 80 par exemple (réservé à HTTP) ou encore le port 12345. Par contre j’ai déjà remarqué des tentatives sur le port 222.
Pour changer le port d’écoute SSH on pourrait mettre en place une règle iptables, ou encore toucher à la directive Port 22 dans /etc/ssh/sshd_config. Des options particulières peuvent également être spécifiées au démarrage du service SSH grâce au fichier /etc/default/ssh sous Debian.
Le client ssh devra être lancé avec l’option -p pour spécifier le port distant.
Limiter la connexion à seulement certains utilisateurs
La directive AllowUsers permet de spécifier la liste limitative des seuls utilisateurs pouvant se connecter. La directive DenyUsers permet d’en interdire. La syntaxe prend en compte les noms d’utilisateurs simples (mike) ainsi que les couples utilisateur-hôte (john@ip).
1 |
AllowUsers mike john@ip |
Chrooter les utilisateurs dans leurs répertoires respectifs
La directive ChrootDirectory %h permet de chrooter les utilisateurs dans leurs répertoires utilisateurs respectifs tels que définis dans /etc/passwd. C’est ce même fichier qui spécifie l’emplacement du shell, emplacement relatif à la racine absolue du système de fichiers ou à la racine chroot.
C’est pourquoi vous aurez le message d’erreur suivant si vous ne prenez pas de précautions particulières avec une arborescence chroot suffisante (exécutables comme bash, certaines librairies, …).
1 2 3 4 5 |
user@ordinateur:~$ ssh user@server user@server's password: [...] /bin/bash: No such file or directory Connection to server closed. |
Pour l’exécutable bash, on se rend compte avec la commande ldd que certaines librairies présentes dans /lib et /lib64 sont requises. On peut les monter comme ceci :
1 2 |
user@ordinateur:~$ mount -o bind,ro /lib/ lib/ user@ordinateur:~$ mount -o bind,ro /lib64/ lib64/ |
Mais le plus simple est encore de trouver un bash statique, c’est à dire ne nécessitant pas de librairies externes. Le paquet Debian bash-static est prévu pour cela.
Par ailleurs, openssh nous oblige à ce que les répertoires de chroot appartiennent à root. Par exemple le répertoire /home/user doit appartenir à root pour l’utiliser en tant que racine chroot. Sans cela, vous aurez le message d’erreur suivant lors de vos tentatives de connexion.
1 |
packet_write_wait: Connection to ::1 port 22: Broken pipe |
Et également dans auth.log :
1 |
Feb 1 14:36:45 server sshd[28436]: fatal: bad ownership or modes for chroot directory "/home/user" |
Il est toujours bon de tester le bon fonctionnement du chroot en ligne de commande avant de le configurer sur openssh.
Sécuriser l’usage de SSH par les possibilités Linux
Les conseils qui suivent font appel aux possibilités du système d’exploitation, plus que du daemon SSH en lui-même.
Délai de pause après un échec d’authentification (PAM)
Je trouve intéressant d’obliger le client SSH à partienter après un échec d’authentification. Cela décourage rapidement les petits curieux qui voudraient tester une attaque par force brute.
A l’heure actuelle il n’existe pas de directive de configuration coté serveur. Cela ce joue en amont, par le PAM (pluggable authentification modules).
La ligne permettant cela est la suivante, que l’on peut renseigner dans /etc/pam.d/ssh.
1 |
auth optional pam_faildelay.so delay=9000000 |
Je trouve que la configuration de PAM n’est vraiement pas simple, et j’ai souvent tenté des modifications sans qu’elles prennent effet. L’ordre des lignes de conf est important. Sur Debian 9 il faut intercaler la ligne ci-dessus au-dessus de @include common-auth .
La valeur numérique est en milisecondes, donc ici 9 secondes.
Désactivation du login de certains utilisateurs
On peut refuser à un utilisateur de s’authentifier auprès du système (par SSH ou la console) en préfixant le hash du mot de passe par un point d’exclamation dans /etc/shadow . Exemple :
1 |
user:!$6$Pz[...]:17555:0:99999:7::: |
Emploi d’un shell restreint
Le shell proposé à l’utilisateur suite à sa connexion est spécifié dans /etc/passwd . On remarque souvent /bin/bash comme shell par défaut. Le choix le plus restrictif est /bin/false qui n’est pas un shell mais plutôt un non-shell, un « rien du tout ».
Pour pousser un peu le hardening dans certaines situations (exemple : tunnel SSH simple), je recommande de chrooter l’utilisateur dans une arborescence minimale, avec un restricted shell. Il existe par exemple rssh dont le but est de permettre simplement SCP/SFTP/Rsync vis SSH en excluant tout le reste.
Bannissement des assaillants avec fail2ban
Les assaillants qui reviennent un peu trop souvent peuvent être bannis via iptables grâce à un programme de surveillance des logs comme fail2ban. Sur Debian, l’installation de fail2ban suffit à surveiller les échecs de connexion SSH.
1 |
user@ordinateur:~# apt-get install fail2ban |
Règles pare-feu contre les essais répétés
Le pare-feu iptables permet de bloquer une connexion SSH si son émetteur se montre un peu trop insistant. A utiliser avec précautions, car cette règle peut également bloquer une connexion légitime virt-manager (ssh+qemu://) ou d’autres tunnels démarrés automatiquement.
1 2 3 4 |
iptables -N SSH_CHECK iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j SSH_CHECK iptables -A SSH_CHECK -m recent --set --name SSH iptables -A SSH_CHECK -m recent --update --seconds 60 --hitcount 6 --name SSH -j DROP |
Pour aller plus loin
Je vous recommande la lecture du dossier PDF de l’agence française de la sécurité des systèmes d’information (ANSSI).
cool tes conseils/suggestions.
A noter le script ssh-audit-1.7.py qui permet de tester si las algorithmes utilisés sont suffisants ou pas. Modifier alors /etc/ssh/sshd_config (cf https://github.com/arthepsy/ssh-audit)
cmic sysadmin retraité…
Bonjour,
On peut aussi restreindre les accès utilisateurs avec le couple login@sous-réseau.
Par exemple:
‘AllowUsers john.doe@1.2.3.* ‘
permettra l’accès à john.doe venant de tout le réseau 1.2.3.0 (soit 1.2.3.0/24)