Protection anti-brute-force sur OmniOS CE
Introduction
OmniOS CE utilise IPFilter (ipf) comme pare-feu, qui ne dispose pas de mécanisme natif de rate-limiting comme PF sur FreeBSD. De plus, fail2ban n'est pas disponible dans les dépôts officiels OmniOS.
Cet article présente un script shell léger qui reproduit les fonctionnalités essentielles de fail2ban : surveiller les logs d'authentification, détecter les tentatives d'intrusion, et bloquer automatiquement les adresses IP malveillantes via IPFilter.
Prérequis
- Logs d'authentification activés : Le fichier /var/log/authlog doit exister et être alimenté par rsyslog
- IPFilter actif : Le pare-feu ipf doit être en cours d'exécution
Vérification des prérequis :
# Vérifier rsyslog
svcs system-log:rsyslog
ls -la /var/log/authlog
# Vérifier IPFilter
svcs ipfilter
ipfstat -io
Fonctionnement
Le script fonctionne selon le principe suivant :
- Surveillance : Analyse du fichier /var/log/authlog à la recherche des échecs d'authentification SSH
- Comptage : Décompte du nombre d'échecs par adresse IP sur la journée en cours
- Seuil : Si une IP dépasse le seuil configuré (par défaut 5 tentatives), elle est bloquée
- Blocage : Ajout d'une règle IPFilter pour bloquer l'IP
- Expiration : Les blocages expirent automatiquement après un délai configurable (par défaut 1 heure)
- Whitelist : Les adresses IP locales sont protégées contre le blocage accidentel
Installation
Étape 1 : Créer le répertoire de données
mkdir -p /var/db/bruteforce
touch /var/log/bruteforce.log
Étape 2 : Script principal de blocage
cat << 'EOF' > /opt/local/sbin/block-bruteforce
#!/bin/sh
LOGFILE="/var/log/authlog"
THRESHOLD=5
BLOCK_TIME=3600 # 1 heure en secondes
BLOCKLIST="/var/db/bruteforce/blocked-ips"
WHITELIST="127.0.0.1 192.168.25." # Adapter à votre réseau
# Créer les fichiers si nécessaires
mkdir -p /var/db/bruteforce
touch "$BLOCKLIST"
# Fonction pour vérifier si une IP est en whitelist
is_whitelisted() {
ip="$1"
for w in $WHITELIST; do
case "$ip" in
${w}*) return 0 ;;
esac
done
return 1
}
# Nettoyer les blocages expirés
NOW=$(date +%s)
TEMP="/tmp/blocklist.$$"
> "$TEMP"
while IFS=' ' read -r ip timestamp; do
[ -z "$ip" ] && continue
EXPIRE=$((timestamp + BLOCK_TIME))
if [ "$NOW" -lt "$EXPIRE" ]; then
echo "$ip $timestamp" >> "$TEMP"
else
# Débloquer l'IP via ipf
echo "block in quick from $ip to any" | ipf -rf -
echo "$(date '+%Y-%m-%d %H:%M:%S'): Débloqué $ip (expiration)" >> /var/log/bruteforce.log
fi
done < "$BLOCKLIST"
mv "$TEMP" "$BLOCKLIST"
# Analyser les échecs récents
grep -E "Failed password|Invalid user" "$LOGFILE" 2>/dev/null | \
grep "$(date '+%b %d')" | \
grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | \
sort | uniq -c | sort -rn | \
while read count ip; do
[ -z "$ip" ] && continue
# Vérifier si déjà bloqué
grep -q "^$ip " "$BLOCKLIST" 2>/dev/null && continue
# Vérifier whitelist
is_whitelisted "$ip" && continue
# Bloquer si seuil dépassé
if [ "$count" -ge "$THRESHOLD" ]; then
echo "$ip $(date +%s)" >> "$BLOCKLIST"
echo "block in quick from $ip to any" | ipf -f -
echo "$(date '+%Y-%m-%d %H:%M:%S'): Bloqué $ip ($count tentatives)" >> /var/log/bruteforce.log
fi
done
EOF
chmod 700 /opt/local/sbin/block-bruteforce
Étape 3 : Script de statut
cat << 'EOF' > /opt/local/sbin/bruteforce-status
#!/bin/sh
BLOCKLIST="/var/db/bruteforce/blocked-ips"
BLOCK_TIME=3600
echo "=== IPs actuellement bloquées ==="
if [ -s "$BLOCKLIST" ]; then
NOW=$(date +%s)
while IFS=' ' read -r ip timestamp; do
[ -z "$ip" ] && continue
REMAIN=$(( (timestamp + BLOCK_TIME - NOW) / 60 ))
echo "$ip (expire dans ${REMAIN} min)"
done < "$BLOCKLIST"
else
echo "(aucune)"
fi
echo ""
echo "=== Dernières entrées du log ==="
tail -10 /var/log/bruteforce.log 2>/dev/null || echo "(aucun log)"
echo ""
echo "=== Règles IPFilter actives (block) ==="
ipfstat -io 2>/dev/null | grep "block in quick from" || echo "(aucune règle de blocage dynamique)"
EOF
chmod 700 /opt/local/sbin/bruteforce-status
Étape 4 : Script de déblocage manuel
cat << 'EOF' > /opt/local/sbin/bruteforce-unblock
#!/bin/sh
if [ -z "$1" ]; then
echo "Usage: bruteforce-unblock <IP>"
exit 1
fi
IP="$1"
BLOCKLIST="/var/db/bruteforce/blocked-ips"
# Retirer de la liste
grep -v "^$IP " "$BLOCKLIST" > "/tmp/blocklist.$$"
mv "/tmp/blocklist.$$" "$BLOCKLIST"
# Retirer la règle IPFilter
echo "block in quick from $IP to any" | ipf -rf -
echo "$(date '+%Y-%m-%d %H:%M:%S'): Débloqué manuellement $IP" >> /var/log/bruteforce.log
echo "IP $IP débloquée"
EOF
chmod 700 /opt/local/sbin/bruteforce-unblock
Étape 5 : Planification cron
# Ajouter au crontab (exécution toutes les minutes)
crontab -l > /tmp/crontab.tmp
echo "" >> /tmp/crontab.tmp
echo "# Anti-brute-force (toutes les min)" >> /tmp/crontab.tmp
echo "1 * * * * /opt/local/sbin/block-bruteforce" >> /tmp/crontab.tmp
crontab /tmp/crontab.tmp
rm /tmp/crontab.tmp
# Vérifier
crontab -l | grep bruteforce
Configuration
Les paramètres sont définis en haut du script /opt/local/sbin/block-bruteforce :
- THRESHOLD=5 : Nombre d'échecs avant blocage
- BLOCK_TIME=3600 : Durée du blocage en secondes (1 heure par défaut)
- WHITELIST="127.0.0.1 192.168.25." : Préfixes IP à ne jamais bloquer
(Adapter la whitelist à votre réseau local pour éviter de vous bloquer vous-même)
Utilisation
Voir le statut
bruteforce-status
# Exemple de sortie :
=== IPs actuellement bloquées ===
203.0.113.45 (expire dans 42 min)
198.51.100.23 (expire dans 17 min)
=== Dernières entrées du log ===
2025-12-30 16:22:00: Bloqué 203.0.113.45 (15 tentatives)
2025-12-30 16:35:00: Bloqué 198.51.100.23 (8 tentatives)
=== Règles IPFilter actives (block) ===
block in quick from 203.0.113.45/32 to any
block in quick from 198.51.100.23/32 to any
Débloquer une IP manuellement
bruteforce-unblock 203.0.113.45
# Sortie :
IP 203.0.113.45 débloquée
Exécution manuelle
# Forcer une analyse immédiate
/opt/local/sbin/block-bruteforce
Consulter les logs
# Log du script anti-brute-force
cat /var/log/bruteforce.log
# Log des authentifications (source)
tail -50 /var/log/authlog | grep -E "Failed|Invalid"
Fichiers et emplacements
- Script principal :
/opt/local/sbin/block-bruteforce - Script de statut :
/opt/local/sbin/bruteforce-status - Script de déblocage :
/opt/local/sbin/bruteforce-unblock - Liste des IPs bloquées :
/var/db/bruteforce/blocked-ips - Log des actions :
/var/log/bruteforce.log - Source des authentifications :
/var/log/authlog
Comparaison avec fail2ban
| Aspect | Ce script | fail2ban |
|---|---|---|
| Dépendances | Aucune (shell POSIX) | Python |
| Installation | Copier les scripts | pkgin |
| Configuration | Simple (variables) | Fichiers jail.conf |
| Services supportés | SSH uniquement | Multiples |
| Réactivité | Cron (1 min) | Temps réel |
| Ressources | Très légères | Modérées |
| Disponibilité OmniOS | Natif | Via pkgsrc |
Dépannage
Le script ne bloque pas les IPs
- Vérifier que /var/log/authlog contient des entrées récentes
- Vérifier que rsyslog est configuré pour les logs auth
- Vérifier que l'IP n'est pas dans la whitelist
Je me suis bloqué moi-même
- Accéder via la console physique ou IPMI
- Exécuter :
bruteforce-unblock VOTRE_IP - Ajouter votre réseau à la whitelist
Les blocages ne persistent pas après reboot
C'est le comportement attendu. Les règles IPFilter dynamiques sont perdues au redémarrage. La liste /var/db/bruteforce/blocked-ips est conservée mais les règles doivent être réappliquées. Pour une persistance complète, ajouter dans /etc/ipf/ipf.conf les IPs à bloquer définitivement.
Améliorations possibles
- Blocage progressif : Augmenter la durée de blocage à chaque récidive
- Persistance au reboot : Réappliquer les blocages actifs au démarrage
- Support multi-services : Étendre à SMB, FTP, etc.
- Base de données : Historique des blocages pour analyse
- Géolocalisation : Bloquer par pays d'origine
Notifications
Il est aisé d'ajouter des notifications lors du blocage d'une IP en utilisant un service comme ntfy.sh. Il suffit d'ajouter un appel curl dans le script principal, juste après la ligne de log du blocage :
curl -s -d "IP $ip bloquée ($count tentatives)" https://ntfy.sh/VOTRE_TOPIC
Conclusion
Ce script offre une protection efficace contre les attaques par brute-force SSH sur OmniOS CE, sans nécessiter de dépendances externes. Sa légèreté et sa simplicité en font une solution idéale pour les serveurs NAS et les environnements où les ressources sont limitées.
↑ Haut de page