Menu

Restreindre l’accès SSH selon le pays sur Debian GNU/Linux

10 novembre 2020 - Informatique
Restreindre l’accès SSH selon le pays sur Debian GNU/Linux

Vous avez un serveur Linux qui expose son service SSH de l’Internet et vous remarquez des tentatives de connexions non désirées même si vous opérez le service sur un port non standard (autre que 22)? Puisque ces connexions ont souvent tendance à venir des quelques mêmes endroits (États-Unis, Chine, Russie, etc.), la restriction par pays semble une bonne solution.

Plusieurs tutoriels existent sur le web à ce sujet mais ceux sur lesquels je suis tombé contiennent des erreurs, sont incomplets, trop vieux ou ne sont pas optimaux. En voici donc un en français et qui est efficace à près de 100% (c’est-à-dire que le mécanisme proposé va trouver un pays pour chaque IP de provenance, sauf pour ceux du réseau local, et va autoriser ou bloquer l’accès). D’abord, il faut noter que je n’ai pas réussi à faire cela sur la dernière version (8) de Oracle Linux (ou CentOS) puisque tcpwrapper n’est plus inclus avec le système. À mon souvenir, je n’ai pas eu de succès non plus même avec la version 7 du OS. J’ai donc utilisé Debian 10 (Buster) pour faire cela.

La plupart des “tutos” qu’on retrouve sur le web indiquent d’installer les paquets geoip-bin et geoip-database, or, le paquet “geoip-database” est une vieille version non mise à jour de la base de données de pays et, pour l’avoir essayée, ne permet pas d’obtenir une efficacité de près de 100% pour restreindre par pays l’accès SSH: C’est-à-dire que certains IP autres que ceux du réseau local ne seront pas reconnus donc entraînent possiblement une erreur, quel que soit l’action choisie (blocage ou autorisation). Le mieux, si on veut une efficacité qui frôle 100%, est de se créer un compte sur le site maxmind.com pour télécharger une base de données à jour et, surtout, automatiser la mise-à-jour de celle-ci. Il est à noter que certaines versions des bases de données sont payantes mais la version “GeoLite2” est gratuite. Une fois le compte créé, il faut se rendre à la page des licences (“Services” -> “My licence key”) dans le menu de gauche et en générer une. Dans cette page, l’identifiant de compte (“Account/User ID”) est également indiqué. Il est bon de noter ces deux informations puisqu’elles seront utilisées pour la m-a-j automatisée de la BD. Quant au paquet geoip-bin, celui-ci n’est compatible qu’avec les anciennes versions de la BD donc n’est pas approprié non plus.

Les paquets à installer sont donc les suivants. Il est à noter que la source “contrib” doit être active sur le système.

root@serveur:~# apt install mmdb-bin geoipupdate

mmdb-bin sera utilisé pour interroger la BD pour chaque IP qui tente de se connecter et geoipupdate servira pour mettre à jour le fichier de BD, qui se trouvera dans /var/lib/GeoIP . Le paquet geoipupdate installe un cron qui s’exécute chaque jour mais Maxmind fournit une nouvelle version de la BD environ une fois par semaine. Je suggère de surveiller avec un outil de surveillance l’âge du fichier de BD pour s’assurer qu’il est mis à jour régulièrement. C’est ce que je fais personnellement. Il faut renseigner le fichier de configuration /etc/GeoIP.conf de la façon suivante:

AccountID <numéro de compte>
LicenseKey <numéro de licence>
EditionIDs GeoLite2-Country
PreserveFileTimes 1

Pour les deux premiers paramètres, il faut fournir les valeurs tel qu’obtenues sur le site de Maxmind. Le troisième paramètre (“EditionIDs”) indique quelle base de données mettre à jour et “PreserveFileTimes” indique de conserver l’heure originale du fichier téléchargé à partir du site de Maxmind. Afin d’obtenir immédiatement une base de données, il sera bon d’exécuter la commande suivante avec l’usager root:

root@serveur:~# /usr/bin/geoipupdate

On peut ensuite vérifier que la BD est bien là:

root@serveur:~# ls -l /var/lib/GeoIP/
total 46121
-rw-r--r--. 1 root root  3934555 Nov  3 15:48 GeoLite2-Country.mmdb
root@serveur:~# 

Maintenant que nous avons une BD à jour, on peut configurer le nécessaire pour l’interroger lorsqu’une connexion SSH survient. D’abord, voici le script que j’ai conçu pour cela (il est inspiré des “tutos” qu’on retrouve sur le web). On peut placer son contenu dans /usr/local/bin/ipfilter.sh .

#!/bin/bash
# UPPERCASE space-separated country codes to ACCEPT
# https://dev.maxmind.com/geoip/legacy/codes/iso3166/
ALLOW_COUNTRIES="CA US"
LOGDENY_FACILITY="authpriv.notice"

if [ $# -ne 1 ]; then
  echo "Usage:  `basename $0` " 1>&2
  exit 0 # return true in case of config issue
fi

COUNTRY_SHORT=`/usr/bin/mmdblookup --file /var/lib/GeoIP/GeoLite2-Country.mmdb --ip "$1" country iso_code 2>&1 | grep . | cut -d\" -f2 | sed 's/^ *//g'`
COUNTRY_LONG=`/usr/bin/mmdblookup --file /var/lib/GeoIP/GeoLite2-Country.mmdb --ip "$1" country names en 2>&1 | grep . | cut -d\" -f2 | sed 's/^ *//g'`

[[ $COUNTRY_SHORT = "Could not find an entry for this IP address ($1)" || $ALLOW_COUNTRIES =~ $COUNTRY_SHORT ]] && RESPONSE="ALLOW" || RESPONSE="DENY"

if [[ $COUNTRY_SHORT = "Could not find an entry for this IP address ($1)" ]]
then
        COUNTRY_LONG="IP address not found"
fi

if [[ "$RESPONSE" == "ALLOW" ]] ; then
  logger -p $LOGDENY_FACILITY "$RESPONSE sshd connection from $1 ($COUNTRY_LONG)"
  exit 0
else
  logger -p $LOGDENY_FACILITY "$RESPONSE sshd connection from $1 ($COUNTRY_LONG)"
  exit 1
fi

La variable “ALLOW_COUNTRIES” permet d’ajuster les pays autorisés. Pour l’exemple, on permet le Canada et les États-Unis. Il faudra rendre le script exécutable:

root@serveur:~# chmod +x /usr/local/bin/ipfilter.sh 

Ensuite, on fait le lien entre ce script et SSH en ajoutant la ligne suivante dans /etc/hosts.allow :

sshd: ALL: aclexec /usr/local/bin/ipfilter.sh %a

… et celle-ci dans /etc/hosts.deny :

sshd: ALL

Il ne reste qu’à vérifier que cela fonctionne. Lorsqu’une connexion SSH sera tentée à partir d’Internet dans un pays non autorisé, un bloc de lignes du genre s’affichera dans /var/log/auth.log :

Oct 13 18:20:33 serveur root: DENY sshd connection from 45.93.201.X (Russia)
Oct 13 18:20:33 serveur sshd[386036]: aclexec returned 1
Oct 13 18:20:33 serveur sshd[386036]: refused connect from 45.93.201.X (45.93.201.X)

S’il s’agit d’un pays permis, on aura plutôt:

Nov 10 17:55:54 serveur root: ALLOW sshd connection from 184.145.226.X (Canada)

Enfin, pour les IP non trouvés, il devrait s’agir exclusivement des connexions provenant du réseau local, par exemple:

Nov 10 18:05:15 serveur root: ALLOW sshd connection from 192.168.0.10 (IP address not found)

En six mois d’utilisation avec la méthode proposée dans ce “tuto”, j’ai eu plusieurs centaines de connexions SSH tentées d’un peu partout sur la planète et ça ne m’est pas arrivé une seule fois que la commande mmdblookup appelée par mon script ipfilter.sh ne puisse résoudre un certain IP, contrairement à la courte période de quelques jours où j’utilisais plutôt les paquets geoip-bin et geoip-database et où j’ai vu plusieurs IP non résolus.

Étiquettes : , , , , ,

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *