SSH bannissement
Lors de l'ouverture d'une machine sur internet avec le protocole SSH
, les attaques sont nombreuses. Cet article présente une règle personnalisée sous <code<Fail2ban</code> pour banir rapidement les tentatives d'intrusions sur des essais de connexions avec des comptes inexistants.
Sommaire
Votre avis
Nobody voted on this yet
|
|
Etude
Par défaut, Fail2ban
fournit une configuration sur les tentatives d'attaque sur ce protocole.
Dans le fichier jail.conf
, dans le répertoire /etc/fail2ban
pour une installation sous Ubuntu
, les sections suivantes sont disponibles pour une installation en version 0.9.7
.
[sshd]
# To use more aggressive sshd filter (inclusive sshd-ddos failregex):
#filter = sshd-aggressive
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
[sshd-ddos]
# This jail corresponds to the standard configuration in Fail2ban.
# The mail-whois action send a notification e-mail with a whois request
# in the body.
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
Par défaut, les configurations de bannissement sont dans le sous répertoire filter.d
avec des fichiers portant le nom de la section. Pour la configuration sshd
, la configuration est la suivante.
# Fail2Ban filter for openssh
#
# If you want to protect OpenSSH from being bruteforced by password
# authentication then get public key authentication working before disabling
# PasswordAuthentication in sshd_config.
#
#
# "Connection from <HOST> port \d+" requires LogLevel VERBOSE in sshd_config
#
[INCLUDES]
# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf
[DEFAULT]
_daemon = sshd
# optional prefix (logged from several ssh versions) like "error: ", "error: PAM: " or "fatal: "
__pref = (?:(?:error|fatal): (?:PAM: )?)?
# optional suffix (logged from several ssh versions) like " [preauth]"
__suff = (?: \[preauth\])?\s*
__on_port_opt = (?: port \d+)?(?: on \S+(?: port \d+)?)?
# single line prefix:
__prefix_line_sl = %(__prefix_line)s%(__pref)s
# multi line prefixes (for first and second lines):
__prefix_line_ml1 = (?P<__prefix>%(__prefix_line)s)%(__pref)s
__prefix_line_ml2 = %(__suff)s$<SKIPLINES>^(?P=__prefix)%(__pref)s
mode = %(normal)s
normal = ^%(__prefix_line_sl)s[aA]uthentication (?:failure|error|failed) for .* from <HOST>( via \S+)?\s*%(__suff)s$
^%(__prefix_line_sl)sUser not known to the underlying authentication module for .* from <HOST>\s*%(__suff)s$
^%(__prefix_line_sl)sFailed \S+ for (?P<cond_inv>invalid user )?(?P<user>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)) from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
^%(__prefix_line_sl)sROOT LOGIN REFUSED.* FROM <HOST>\s*%(__suff)s$
^%(__prefix_line_sl)s[iI](?:llegal|nvalid) user .*? from <HOST>%(__on_port_opt)s\s*$
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*%(__suff)s$
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*%(__suff)s$
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because not in any group\s*%(__suff)s$
^%(__prefix_line_sl)srefused connect from \S+ \(<HOST>\)\s*%(__suff)s$
^%(__prefix_line_sl)sReceived disconnect from <HOST>%(__on_port_opt)s:\s*3: .*: Auth fail%(__suff)s$
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*%(__suff)s$
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*%(__suff)s$
^%(__prefix_line_sl)spam_unix\(sshd:auth\):\s+authentication failure;\s*logname=\S*\s*uid=\d*\s*euid=\d*\s*tty=\S*\s*ruser=\S*\s*rhost=<HOST>\s.*%(__suff)s$
^%(__prefix_line_sl)s(error: )?maximum authentication attempts exceeded for .* from <HOST>%(__on_port_opt)s(?: ssh\d*)? \[preauth\]$
^%(__prefix_line_ml1)sUser .+ not allowed because account is locked%(__prefix_line_ml2)sReceived disconnect from <HOST>: 11: .+%(__suff)s$
^%(__prefix_line_ml1)sDisconnecting: Too many authentication failures for .+?%(__prefix_line_ml2)sConnection closed by <HOST>%(__suff)s$
^%(__prefix_line_ml1)sConnection from <HOST>%(__on_port_opt)s%(__prefix_line_ml2)sDisconnecting: Too many authentication failures for .+%(__suff)s$
ddos = ^%(__prefix_line_sl)sDid not receive identification string from <HOST>%(__suff)s$
^%(__prefix_line_sl)sReceived disconnect from <HOST>%(__on_port_opt)s:\s*14: No supported authentication methods available%(__suff)s$
^%(__prefix_line_sl)sUnable to negotiate with <HOST>%(__on_port_opt)s: no matching (?:cipher|key exchange method) found.
^%(__prefix_line_ml1)sConnection from <HOST>%(__on_port_opt)s%(__prefix_line_ml2)sUnable to negotiate a (?:cipher|key exchange method)%(__suff)s$
^%(__prefix_line_ml1)sSSH: Server;Ltype: (?:Authname|Version|Kex);Remote: <HOST>-\d+;[A-Z]\w+:.*%(__prefix_line_ml2)sRead from socket failed: Connection reset by peer%(__suff)s$
aggressive = %(normal)s
%(ddos)s
[Definition]
failregex = %(mode)s
ignoreregex =
[Init]
# "maxlines" is number of log lines to buffer for multi-line regex searches
maxlines = 10
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd
# DEV Notes:
#
# "Failed \S+ for .*? from <HOST>..." failregex uses non-greedy catch-all because
# it is coming before use of <HOST> which is not hard-anchored at the end as well,
# and later catch-all's could contain user-provided input, which need to be greedily
# matched away first.
#
# Author: Cyril Jaquier, Yaroslav Halchenko, Petr Voralek, Daniel Black
Pour la configuration sshd-ddos
, celle-ci est une extension de la précédente afin d'activer le mode ddos
.
# Fail2Ban ssh filter for at attempted exploit
#
# The regex here also relates to a exploit:
#
# http://www.securityfocus.com/bid/17958/exploit
# The example code here shows the pushing of the exploit straight after
# reading the server version. This is where the client version string normally
# pushed. As such the server will read this unparsible information as
# "Did not receive identification string".
[INCLUDES]
before = sshd.conf
[Definition]
mode = %(ddos)s
Il suffit de s'inspirer de cette organisation pour créer une règle spécifique.
Configuration
Ajout bannissement
La première étape consiste à référencer le nouveau bannissement. Pour cette règle, il n'est pas souhaité de laisser beaucoup de chance aux attaques et dès la première tentative elle doit se déclencher.
Un nouveau fichier sshd-invalid-user.local
est créé dans le sous répertoire jail.d
avec le contenu suivant.
[sshd-invalid-user]
enabled = true
bantime = 21600
maxretry = 1
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
La durée de bannissement est configurée à 6 heures, valeur 21600
dans bantime
, et la variable sshd_log
est utilisée pour référencer l'emplacement des fichier traces à vérifier comme dans les configurations fournies.
Définition règle
La seconde étape consiste à définir les règles de déclenchement du bannissement. Un fichier est le nom sshd-invalid-user.conf
, contenant le nom du bannissement, est ajouté dans le sous répertoire filter.d
.
Afin d'identifier les tentatives d'intrusion avec des comptes "génériques", il suffit de regarder les message du type Failed password for USER_TEST ...
ou Invalid user USER_TEST
. La syntax de ces règles sont déjà disponibles dans les configurations standards et il suffit de les recopier.
La possibilité d'extension est utilisée pour limiter les configurations à mettre en place. Ainsi, seul un nouveau mode invalid-user
est configuré puis activé dans la configuration fournie par sshd.conf
en spécifiant la valeur de mode
. Le contenu du fichier est le suivant.
# Fail2Ban ssh filter for invalid user.
#
[INCLUDES]
before = sshd.conf
[DEFAULT]
invalid-user = ^%(__prefix_line_sl)sFailed \S+ for (?P<cond_inv>invalid user )?(?P<user>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)) from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
^%(__prefix_line_sl)s[iI](?:llegal|nvalid) user .*? from <HOST>%(__on_port_opt)s\s*$
[Definition]
mode = %(invalid-user)s
Test
Avant d'activer la nouvelle règle, il est possible de la tester à l'aide de la commande fail2ban-regex
.
#sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd-invalid-user.conf Running tests ============= Use failregex filter file : sshd-invalid-user, basedir: /etc/fail2ban Use maxlines : 10 Use log file : /var/log/auth.log Use encoding : UTF-8 Results ======= Failregex: 2043 total |- #) [# of hits] regular expression | 1) [1666] ^(?:\[\])?\s*(?:<[^.]+\.[^.]+>\s+)?(?:\S+\s+)?(?:kernel: \[ *\d+\.\d+\]\s+)?(?:@vserver_\S+\s+)?(?:(?:(?:\[\d+\])?:\s+[\[\(]?sshd(?:\(\S+\))?[\]\)]?:?|[\[\(]?sshd(?:\ (\S+\))?[\]\)]?:?(?:\[\d+\])?:?)\s+)?(?:\[ID \d+ \S+\]\s+)?(?:(?:error|fatal): (?:PAM: )?)?Failed \S+ for (?P<cond_inv>invalid user )?(?P<user>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)) from <HOST>(?: port \d+)?(?: on \S+(?: port \d+)?)?(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$) | 2) [377] ^(?:\[\])?\s*(?:<[^.]+\.[^.]+>\s+)?(?:\S+\s+)?(?:kernel: \[ *\d+\.\d+\]\s+)?(?:@vserver_\S+\s+)?(?:(?:(?:\[\d+\])?:\s+[\[\(]?sshd(?:\(\S+\))?[\]\)]?:?|[\[\(]?sshd(?:\(\S+\))? [\]\)]?:?(?:\[\d+\])?:?)\s+)?(?:\[ID \d+ \S+\]\s+)?(?:(?:error|fatal): (?:PAM: )?)?[iI](?:llegal|nvalid) user .*? from <HOST>(?: port \d+)?(?: on \S+(?: port \d+)?)?\s*$ `- Ignoreregex: 0 total Date template hits: |- [# of hits] date format | [9603] (?:DAY )?MON Day 24hour:Minute:Second(?:\.Microseconds)?(?: Year)? `- Lines: 9603 lines, 0 ignored, 2043 matched, 7560 missed [processed in 3.09 sec] Missed line(s): too many to print. Use --print-all-missed to print all 7560 lines
Activation
Une fois validée, il suffit d'activer la règle à l'aide de la commande fail2ban-client
avec l'argument reload
.
#sudo fail2ban-client reload