Bloquer des tentatives d'accès ssh non autorisées

Suite à deux fils concernant des attaques de force brute sur le port ssh, j’ai fait un petit script qui analyse la fréquence des tentatives de connexions infructueuses qui sont journalisées dans /var/log/auth.log

Instructions:
[ul]
[li]dans le fichier /etc/hosts.deny, ajouter une ligne:

[li]mettre le script ci-dessous en cron: toutes les 5 minutes par exemple.[/li]
[li]le UID du script doit avoir les droits d’écriture sur le fichier /home/user/mon-fichier-ip-ban bien sûr.[/li]
[li]C’est tout. Le deamon tcpd, un wrapper de connexions tcp se charge du reste.[/li][/ul]

Contrairement à iptables, le deamon tcpd ne coupe pas une connexion ssh déjà établie.Il n’y a donc pas de danger de faire des bêtises du genre couper toute connexion ssh sur un serveur distant tant qu’on laisse une session ssh active pour réparer ses bêtises.

[code]#!/bin/bash

Utilisation:

$ ipban -m 15

extraction des IP ayant tenté plus de 15 connexions par minute

$ ipban -h 5

extraction des IP ayant tenté plus de 5 connexions par heure

Instructions:

dans /etc/hosts.deny ajouter

sshd: /tmp/ipban-ssh # voir fichier $DENY_FILE

test des paramètres positionnels

if [ $# -lt 2 ]; then echo “Usage: ipban -m 15 ou ipban -h 5”; exit 1; fi
if [ $1 != “-m” ] && [ $1 != “-h” ];then echo “Usage: ipban -m 15 ou ipban -h 5”; exit 1; fi
if [ $2 -le 0 ]; then echo “Vous devez donner un seuil de tentatives par min. ou par heure.”; exit 1; fi

Paramètres config

LOG_FILE=’/var/log/auth.log’ # emplacement fichier auth.log
DENY_FILE=’/tmp/ipban-ssh’ # emplacement fichier de ip bannies

Initialisation variables

DTE_FILTER=$(LC_ALL=C date +’%b %d’)
[ $1 = “-m” ] && SUBSTR=5 || SUBSTR=2
echo “# fichier des ip ayant tenté plus de $2 connections ssh - $(date)” > $DENY_FILE

calcul de la fréquence de connexions par minute

et stockage dans le fichier $DENY_FILE

awk "/^$DTE_FILTER.+sshd.+Invalid user/ "’{
# tableau associatif indice: [M D HH:MM ip] ou [M D HH ip]
tab[$1 $2 substr($3, 1, ‘$SUBSTR’) $10]++
if (tab[$1 $2 substr($3, 1, ‘$SUBSTR’) $10] >= ‘$2’){
print $10
}
}
’ $LOG_FILE | sort -u >> $DENY_FILE

cat $DENY_FILE
exit 0
[/code]

Si on veut, en plus, bloquer les IP dans iptables, il suffit de récupérer le fichier $DENY_FILE et, en bouclant dessus, d’ajouter des règles dans netfilter. Le problème est qu’il faudra bien un jour nettoyer ces règles qui s’accumulent toutes les x minutes, à chaque tour de cron du script. Dans le cas contraire, une IP bannie peut un jour vous être attribuée si vous êtes en IP dynamique. Je réfléchis à une méthode propre. Si quelqu’un a une idée…

Le script ci -dessus est basé sur un format de log standard qui contient des lignes telles comme:

Apr 26 20:18:15 monserveur sshd[5159]: Invalid user wwwrun from 210.243.170.181 Apr 28 02:58:04 monserveur sshd[20403]: Invalid user xfs from 72.93.200.84 Apr 26 20:22:06 monserveur sshd[5341]: Invalid user xgridagent from 210.243.170.181 Apr 26 20:22:16 monserveur sshd[5349]: Invalid user xgridcontroller from 210.243.170.181 Apr 28 03:02:39 monserveur sshd[20691]: Invalid user xxx from 72.93.200.84 Apr 28 03:03:22 monserveur sshd[20735]: Invalid user year from 72.93.200.84 Apr 28 03:02:18 monserveur sshd[20669]: Invalid user yellow from 72.93.200.84 Apr 28 14:17:14 monserveur sshd[6611]: Invalid user za from 88.191.46.60 Apr 26 16:00:10 monserveur sshd[30986]: Invalid user zachary from 89.110.150.203 Apr 28 02:58:10 monserveur sshd[20409]: Invalid user zephyr from 72.93.200.84 Apr 28 14:16:32 monserveur sshd[6556]: Invalid user Zmeu from 88.191.46.60 Apr 26 15:56:53 monserveur sshd[30750]: Invalid user zoe from 89.110.150.203 Apr 26 20:23:43 monserveur sshd[5419]: Invalid user zzz from 210.243.170.181

Si votre format diffère il faudra adapter les instructions awk.

Edit:

  • Optimisation
  • corrections diverses

Il y a fail2ban

[code]Description : bans IPs that cause multiple authentication errors
Monitors log files (e.g. /var/log/auth.log, /var/log/apache/access.log) and temporarily or persistently bans failure-prone addresses by updating existing
firewall rules. The software was completely rewritten at version 0.7.0 and now allows easy specification of different actions to be taken such as to ban an IP
using iptables or hostsdeny rules, or simply to send a notification email. Currently, by default, supports ssh/apache/vsftpd but configuration can be easily
extended for monitoring any other ASCII file. All filters and actions are given in the config files, thus fail2ban can be adopted to be used with a variety of
files and firewalls.

Homepage: http://www.sourceforge.net/projects/fail2ban
[/code]

Oui. Mais ceci est très léger à mettre en place. Et ce T&A fait suite à une demande alors…

Tu passe par un script sh pour lancer appliquer les rêgles qu’il faut au démarrage. Il gêre un certains nombre de règles, puis il va chercher dans un fichier dans /var par exemple qui est conçu comme ça :

0123456789 127.0.0.1 ...

Il vérifie que la date (au format unix) a plus 15 jours si c’est le cas tu supprime sinon tu applique une règle pour cette IP.

Ou un truc du genre.

Merci ripat pour la diffusion.

Je vais essayer tout ça.
Une idée qui me vient : cela peut venir en complément d’une protection par le module ipt_recent qui droppe p.ex une ip qui a fait 5 nouvelles connexions en moins de 5mn et qui se retrouve bannie pendant 5mn. Dans ce cas, la chaine utilisateur qui gère ipt_recent log quelque chose dans le syslog ton script peut venir regarder ça une ou deux fois par jour et bannir pour de bon et pour une durée à déterminer l’ip en question, soit avec host.deny soit en ajoutant une règle iptables.
Donc dans ce cas ipt_recent empêche trop de connexions en peu de temps et ton script en crontab banni à long terme ceux qui sont revenus.
Et comme le propose MisterFreez un autre mecanisme (ou lancé par la même crontab) fait le ménage.

Pour ne pas bannir sa propre IP quand on est en dynamique on peut envisager une petite page php posée sur un serveur quelque part (p.ex une page perso free). Un script vient l’interroger régulièrement et l’écrit dans un fichier.
Un ami avait fait ça à une époque : son système lui envoyait son ip par mail 2 fois par jour et du coup il pouvait se logger en ssh depuis son bureau.
S’il est d’accord je peux poser son code php et son script de récupération, mais c’est assez simple à faire.

Pour ma part :

  • un firewall iptables : il fait firewall pas IDS mais reste très modulaire pour permettre l’ajout/le retrait de règles. J’ai pendant un temps utilisé le match recent, mais après l’avoir fait souffrir un peu en effectuant des tests, on voit bien que ce n’est pas fait pour faire de la détection. Je l’utilisais plutôt pour décrypter une séquence de port knocking après.

  • psad pour ajouter/retirer les règles iptables, détecter des scans, s’interfacer avec des softs tels que swatch ou de petits scripts perl pour parcourir les logs et mettre en évidence des erreurs d’authentification typiquement ssh ou ftp. Avec un petit graph au final, un listing des ips bannies, une configuration importante …

  • fwnsort pour sniffer tous les paquets et matcher ceux qui sont litigieux par rapport aux règles snort.

  • et pour finir fwknop pour verrouillage du démon ssh.

[quote=“antalgeek”]Pour ne pas bannir sa propre IP quand on est en dynamique on peut envisager une petite page php posée sur un serveur quelque part (p.ex une page perso free). Un script vient l’interroger régulièrement et l’écrit dans un fichier.
Un ami avait fait ça à une époque : son système lui envoyait son ip par mail 2 fois par jour et du coup il pouvait se logger en ssh depuis son bureau.[/quote]

Ça marche très bien et c’est exactement ce que je fais pour protéger mes serveurs. Double protection: tcpd et iptables. Sur un site perso, j’ai une page PHP protégée qui lit l’IP appelante et la stocke dans un fichier texte sur le serveur web du site perso. Le script qui s’éxécute (par cron) sur la machine à protéger va lire régulièrement ce fichier texte. S’il y trouve une IP valide, il ouvre les portes.

Pour le tcp wraper (tcpd) je bloque tout trafic ssh dans /etc/hosts.deny (sshd: ALL) et dans /etc/hosts.allow j’ouvre uniquement pour les adresses autorisées (LAN, serveurs de confiance et… les IP générées par le script plus bas). Le /etc/hosts.allow ressemble à ceci:

[code]# liste des IP amies
sshd: /home/xxxxxxxxj/yyyyyyyy/my-hosts-fixed

liste des IP invitées (généré par script bash plus bas)

sshd: /home/xxxxxxxxj/yyyyyyyy/my-hosts-guest

autorisation pour toute IP du LAN

sshd: LOCAL
[/code]

Pour iptables, du classique. Des règles par défaut stockées dans un fichier par défaut et restaurées à la demande par iptables-restore (remise à zéro). Ensuite on ouvre pour l’IP autorisée.

Le script PHP du site web:
http://site-perso.be/azertyyuiop-poiuytreza.php

[code]<?php
/**
*

  • Script pour activer l’IP guest sur le serveur maison

*/

if (isset($_POST[‘sw’])) {
if ($_POST[‘sw’] == ‘UPDATE’) {
$ip = $_SERVER[‘REMOTE_ADDR’];
} elseif ($_POST[‘sw’] == ‘OFF’){
$ip = ‘OFF’; // pour mise à off de l’ouverture du port
}
$fp = fopen(‘TTD46SGWD1S8SNND887DWMQGE’, ‘w’);
fwrite ($fp, $ip);
fclose($fp);
} else {
$ip = file_get_contents(‘TTD46SGWD1S8SNND887DWMQGE’);
}

?>

IP guest actuelle: <?php echo $ip; ?>

?> [/code]

Protéger cette page PHP par un mot de passe dans un .htaccess et lui donner un nom non trivial tel que : azertyyuiop-poiuytreza.php

Côté serveur à protéger:
script bash à mettre en cron

[code]#! /bin/sh

Script pour aller chercher une adresse IP distante autorisée à accéder au serveur

par le port ssh/22 ouvert mais filtré par iptables

USAGE : activer ce script dans cron pour lancer l’écoute ou faire iptables-guest --clean

pour remettre les règles par défaut.

variables de configuration

    # fichier distant contenant l'IP invitée
    URL="http://www.site-perso.be/yxyxyxyxyxy/TTD46SGWD1S8SNND887DWMQGE"

    # chaîne vide si pas de protection .htaccess
    URL_AUTH="--user user:motdepasse"

    # ip du serveur/pc cible
    DEST_IP="192.168.0.xxx"

    # path vers le fichier save.default (au format iptables-save)
    PATH_CONFIG="/etc/iptables/"

    # fichier inclus dans /etc/hosts.allow (tcp-wrapper)
    ALLOW_FILE="/home/xxxxxxxxj/yyyyyyyy/my-hosts-guest"

AJOUT IP INVITEE

on va chercher la valeur enregistrée sur le site distant

GUEST_IP=curl --connect-timeout 5 --max-time 10 --silent --fail $URL_AUTH $URL

on teste si le fichier distant ne contient bien qu’une IP et rien d’autre (OFF)

on peut ainsi à distance, par web, désactiver l’ouverture du port 22/ssh pour guest

Si argument --clean -> restauration des iptables par défaut.

if ! echo $GUEST_IP | grep -Eqs ‘^[0-9]{1,3}(.[0-9]{1,3}){3}$’ || [ “$1” = “–clean” ]
then
sudo iptables-restore < ${PATH_CONFIG}save.default
sudo iptables -nL INPUT
cp /dev/null $ALLOW_FILE
exit 1
fi

insertion dans le fichier des IP autorisées du tcp wrapper (hosts.allow)

echo $GUEST_IP > $ALLOW_FILE

restaurer les valeurs par défaut pour nettoyer les tables des IP invitées précédentes

sudo iptables-restore < ${PATH_CONFIG}save.default

suppression temporaire du filet LOG (obligatoirement dernière règle de la chaîne)

sudo iptables -D INPUT -j LOG --log-prefix ‘[FW DROP]’

insertion nouvelle règle pour IP invitée (ssh et http et postgres)

sudo iptables -A INPUT -s $GUEST_IP -d $DEST_IP -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -s $GUEST_IP -d $DEST_IP -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -s $GUEST_IP -d $DEST_IP -p tcp --dport 5432 -j ACCEPT

remise du filet LOG

sudo iptables -A INPUT -j LOG --log-prefix ‘[FW DROP]’

Affichage de contrôle des nouvelles règles

sudo iptables -nL INPUT
exit 0
[/code]

Voilà, brut de décoffrage. A adapter à vos besoins.