Le serveur Linux qui héberge le domaine burdet.cc, est protégé par SSHGuard depuis son installation. Ayant le port SSH ouvert sur Internet, il subit un pillonage quotidien de tentatives d’intrusion.

SSHGuard est un service qui surveille les logs d’authentification SSH et qui détecte les adresses IP ayant un comportement s’apparentant à une tentative d’intrusion. Les IP au comportement suspect sont d’abord bannies quelques minutes automatiquement par une règle de firewall, puis de plus en plus longtemps. Les plus assidus se retrouvent dans une liste noire.

De quel pays viennent en priorité les IP en liste noir ?

Cet article propose d’y répondre … et pour ceux n’ayant pas le temps de lire jusqu’au bout, un indice se trouve dans le titre !

La solution proposée se base sur un script en Python (librairies Pandas et matplotlib) et un peu de plomberie Git, que l’on va exécuter sur une machine Linux, en l’occurence Arch Linux.

Création de la source de données

Pour publier les données du log des adresses IP black-listées en provenance du serveur de manière sécurisée, je vais utiliser le repository Git du présent blog pour y créer une branche Git orpheline dédiée. Une tâche quotidienne sur le serveur mettra à jour le repository avec la donnée source.

La configuration a lieu sur le serveur, dans le compte d’un user disposant des droits d’accès au repository Git ainsi que des droits en lecture sur /var/db/sshguard/blacklist.db

Mise en place interactive initiale :

cd ~/tmp
rm -rf blog
git clone gogs@burdet.cc:jfburdet/blog.git
cd blog
git checkout --orphan stats_orion
git rm -rf .
echo "Orion's stats to be published for https://blog.burdet.cc articles" > README.txt
git add README.txt
git commit -m "stats_orion orpan branch, initial commit"
git push --set-upstream origin stats_orion

Ensuite, sur le serveur, je lance un cron job quotidiennement :

#!/bin/bash

tmpdir=`mktemp -d` && cd $tmpdir

git clone gogs@burdet.cc:jfburdet/blog.git
cd blog
git checkout stats_orion 
mkdir -p ssh
cd ssh
strings /var/db/sshguard/blacklist.db | awk -F '|' '{print $4}' > blacklist.txt
git add blacklist.txt
git commit -m "Daily cron job"
git push --set-upstream origin stats_orion

cd
rm -rf $tmpdir

Du coup, le fichier de données contenant les IP actuellement bloquées est atteignable depuis https://dev.burdet.cc/git/jfburdet/blog/raw/stats_orion/ssh/blacklist.txt et quotidiennement mise à jour. C’est merveilleux.


Génération des statistiques

De retour sur un PC de développement (le vôtre par exemple) on va pouvoir consommer les données disponibles depuis l’URL sus-mentionnée. Premièrement on s’assure que les packages système nécessaires soient présents:

# Installation des package sous ArchLinux.
# Sur une autre distrib, la commande sera différente.
sudo pacman -S geoip geoip-database geoip-database-extra

Puis on crée un répertoire de travail dans ~tmp, et on y configure un environnement python3 avec les packages nécessaires :

mkdir -p ~/tmp/sshguard_stats
cd ~/tmp/sshguard_stats
virtualenv -p python3 venv3
. venv3/bin/activate
echo jupyter >> requirements.txt
echo pandas >> requirements.txt
echo matplotlib >> requirements.txt
echo GeoIP >> requirements.txt
pip install -r requirements.txt --upgrade
jupyter notebook

Cela lance le navigateur pour afficher iPython, on crée un nouveau notebook python3 et on colle le code suivant dedans :

import subprocess
import matplotlib
import pandas
import GeoIP
from io import StringIO
import urllib.request

# Instantiating GeoIP helper
gi = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)

# Fetching data, wich are updated daily
stats = urllib.request.urlopen("https://dev.burdet.cc/git/jfburdet/blog/raw/stats_orion/ssh/blacklist.txt").read().decode("utf-8")

# Doing pandas stuff ;-)
df = pandas.read_table(StringIO("IP\n" + stats)).drop_duplicates()
df["country"] = df["IP"].apply(lambda x: gi.country_name_by_addr(x))
df=df.rename(columns = {'IP':'Unique IP Count'})
sorted_country_group = df.groupby("country").count().sort_values(by='Unique IP Count', ascending=False)

# Showing plot
sorted_country_group.plot(kind='bar', figsize=(12,6), title="Origin of SSH attack on burdet.cc")

On exécute la cellule du notebook iPython (ctrl-enter) et on obtient :

Stats SSH IP Blocked

Sacrés chinois !