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

# fin du script

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 : , , , , ,

11 réflexions sur “ Restreindre l’accès SSH selon le pays sur Debian GNU/Linux ”

Pbranly

Bonjour
J’ai installé ce système sur mon NUC avec Ubuntu et cela fonctionne très bien ( restriction FR uniquement )
Je vois bien que tout ce qui est tentative hors France est bloquee
Par contre j’ai aussi fail2ban qui me prévient par mail quand 3 tentatives de login ssh échouent et bloque l’IP
Ma compréhension était que votre système bloquait avant la tentative de login/password
Pouvez vous m’éclairer ?
Merci d’avance
Cordialement
Philippe

Répondre
    Dominique

    Bonjour,

    En effet, fail2ban intervient après la validation de l’usager et mot de passe qui elle intervient après le mécanisme de GeoIP. À la 3ème connexion échouée, que ce soit à cause d’une erreur de mot de passe ou d’un IP bloqué par le mécanisme de GeoIP, fail2ban appelle le pare-feu de votre serveur et ajoute le IP comme IP bloqué à celui-ci. À partir de ce moment, les tentatives à partir de ce IP ne se rendront plus au mécanisme de GeoIP, elles seront bloquées avant, par le pare-feu.

    J’ai vérifié et les tentatives de connexion rejetées par le mécanisme de GeoIP déclenchent fail2ban donc seront bloquées à deux niveaux une fois le IP fautif ajouté aux règles de pare-feu par fail2ban.

    Répondre
      Branly

      Bonjour,
      Je n’avais pas vu votre réponse .
      Celle ci me surprend car le mécanisme de geoip se situe dans une couche plus basse que celui de fail2ban ( basé sur l’analyse de log)
      Une tentative d’entrée d’une IP étrangère à FR ( dans mon cas) ne devrait plus permettre la tentative de faux login password n SSH. ( et encore moins n fois)
      Merci de m’éclairer
      Philippe

      Répondre
        Dominique

        Vous avez raison que le mécanisme de GeoIP survient avant les tentatives de login et mot de passe. Par contre, ce mécanisme de GeoIP fait afficher dans les logs la chaîne « refused connect from IP (IP) » lorsqu’un ip est refusé à cause de sa localisation. Comme cette chaîne est reconnue par fail2ban (voir le fichier /etc/fail2ban/filter.d/sshd.conf), cela entrainera un blocage par le pare-feu, si vous avez configuré fail2ban pour faire cela. Le pare-feu (ex: iptables) intervient avant tcp_wrappers (utilisé par le mécanisme de GeoIP), d’où ma réponse.

        Répondre
Philippe

Bonjour,
votre systeme fonctionnait correctement mais j’ai du reinstaller Linux.
Depuis, toute tentative de faire fonbctionner le filtre, se traduit par l’impossibilite d’acceder au SSH depuis tout et meme depuis des adresses locales de type 1952.18.1.x
Par chance j’ai Webmin qui inclut un editeur et me permet de retirer les lignes des fichiers hosts.allow et deny.
dans le log je vois les interdictions mais rien indiquant le pays autorisé ou interdit
Jul 12 06:20:03 pbranly-NUC6CAYS sshd[919817]: aclexec returned 1
Jul 12 06:20:03 pbranly-NUC6CAYS sshd[919817]: refused connect from 192.168.1.90 (192.168.1.90)
Jul 12 06:20:13 pbranly-NUC6CAYS sshd[919977]: aclexec returned 1
Jul 12 06:20:13 pbranly-NUC6CAYS sshd[919977]: refused connect from 192.168.1.90 (192.168.1.90)
Jul 12 06:20:26 pbranly-NUC6CAYS sshd[920164]: aclexec returned 1
Jul 12 06:20:26 pbranly-NUC6CAYS sshd[920164]: refused connect from 192.168.1.90 (192.168.1.90)
Jul 12 06:20:27 pbranly-NUC6CAYS sm-mta[920196]: DIGEST-MD5 common mech free
Jul 12 06:20:27 pbranly-NUC6CAYS sm-mta[920196]: DIGEST-MD5 common mech free
Jul 12 06:20:49 pbranly-NUC6CAYS sshd[920515]: aclexec returned 1
Jul 12 06:20:49 pbranly-NUC6CAYS sshd[920515]: refused connect from 112.113.46.58 (112.113.46.58)
Jul 12 06:21:56 pbranly-NUC6CAYS sshd[921500]: aclexec returned 1
Jul 12 06:21:56 pbranly-NUC6CAYS sshd[921500]: refused connect from 209.141.62.234 (209.141.62.234)
Jul 12 06:22:07 pbranly-NUC6CAYS sshd[921655]: aclexec returned 1

je suis sous Ubuntu
une piste ?
merci d’avance
vdlt
Philippe

Répondre
    Dominique

    Est-ce que vous avez la commande « logger » d’installée? Sous Debian, cette commande est incluse dans le paquet « bsdutils ».

    Répondre
      Pbranly

      Oui la commande est disponible

      Répondre
        Dominique

        Si le script ipfilter.sh contient exactement ce qu’il y a d’indiqué dans mon article (à part pour les pays autorisés évidemment), vous devez vous assurer que la commande « mmdblookup » existe et est bien dans /usr/bin ainsi que vous assurer que le fichier de BD existe bien à l’emplacement /var/lib/GeoIP/GeoLite2-Country.mmdb . Si ces deux éléments sont bien là et au bon endroit, je suis bien embêté sur ce qui pourrait être le problème mais je vous conseille de tester en ligne de commande en appelant directement le script ipfilter.sh et en lui passant un IP en argument. Ensuite, vous exécutez « echo $? » sur la ligne d’après pour obtenir son code de retour. Si c’est « 0 » qui s’affiche, le ip est autorisé, si c’est « 1 », il ne l’est pas. Si l’exécution manuelle du script ipfilter.sh en ligne de commande semble marcher correctement, il faudrait peut-être vérifier son appel dans /etc/hosts.allow .

        Répondre
Pbranly

Je viens de tester avec une adresse ip locale quelconque la commande donne cette erreur :
ipfilter.sh 192.168.1.80
-bash: /usr/local/bin/ipfilter.sh : /bin/bash^M : mauvais interpréteur: Aucun fichier ou dossier de ce type

Bizarre ce /bin/bash^M

Ça sent le carriage return erroné
Phil

Répondre
Pbranly

dos2unix infiltre.sh et problème reglé !
Merci je vais rebrancher le programme
Ça devrait fonctionner maintenant
Cordialement depuis la France
Philippe

Répondre

Laisser un commentaire

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