Ceci est une ancienne révision du document !
Table des matières
Surveillance de sshd
Dans les journaux
Nous allons voir que le filtre proposé par défaut pour sshd est le suivant:
journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd.
Voyons ce qu'il en sort sur un hôte exposé sur le Net. Nous sommes le 23/06/2025 à 16:00. Combien y a-t-il eu d' «authentication failure» sur sshd depuis le 23/06/2025 00:00:
journalctl _SYSTEMD_UNIT=ssh.service --since "2025-06-23 00:00:00" | grep "authentication failure" Jun 23 00:04:33 vps560672 sshd[302919]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=180.253.165.33 Jun 23 01:04:39 vps560672 sshd[303223]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=114.96.90.14 Jun 23 01:28:23 vps560672 sshd[303369]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=8.217.43.77 Jun 23 02:09:46 vps560672 sshd[303611]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=45.119.86.14 Jun 23 02:12:48 vps560672 sshd[303647]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=51.75.207.206 Jun 23 02:36:14 vps560672 sshd[303739]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=125.164.7.239 Jun 23 02:39:55 vps560672 sshd[303814]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=193.43.72.90 Jun 23 02:58:43 vps560672 sshd[303873]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=180.184.78.162 user=root Jun 23 02:59:22 vps560672 sshd[303876]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=170.254.229.191 Jun 23 03:11:45 vps560672 sshd[304104]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=27.150.188.148 user=root Jun 23 03:19:52 vps560672 sshd[304118]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=27.150.188.148 user=root Jun 23 03:24:36 vps560672 sshd[304159]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=47.236.113.106 Jun 23 05:08:19 vps560672 sshd[304639]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=212.127.78.22 user=root Jun 23 05:18:25 vps560672 sshd[304728]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=212.127.78.22 Jun 23 05:54:58 vps560672 sshd[304923]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=14.103.118.74 user=root Jun 23 06:08:32 vps560672 sshd[304984]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=47.76.51.152 user=root Jun 23 06:12:06 vps560672 sshd[305071]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=14.103.127.230 Jun 23 06:14:46 vps560672 sshd[305090]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=14.103.118.74 Jun 23 06:18:38 vps560672 sshd[305160]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=47.76.51.152 Jun 23 07:08:52 vps560672 sshd[305654]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=47.236.228.168 Jun 23 08:33:36 vps560672 sshd[306127]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=8.217.15.62 Jun 23 14:11:39 vps560672 sshd[307935]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=122.13.25.186 Jun 23 14:28:47 vps560672 sshd[308294]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=14.103.107.21 Jun 23 14:29:46 vps560672 sshd[308313]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=108.196.212.151 Jun 23 14:31:56 vps560672 sshd[308339]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=43.252.229.158 Jun 23 14:33:03 vps560672 sshd[308358]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=194.9.56.139 Jun 23 15:06:32 vps560672 sshd[308550]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=36.50.177.119 user=root Jun 23 15:08:25 vps560672 sshd[308553]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=58.27.134.34 Jun 23 15:16:04 vps560672 sshd[308657]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=180.184.82.249 user=root Jun 23 15:17:18 vps560672 sshd[308663]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=36.50.177.119 Jun 23 15:29:23 vps560672 sshd[308710]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=180.184.82.24931 tentatives en 16 heures. La configuration de fail2ban est telle que des adresses repérées deux fois en moins de 600 secondes sont bannies 1 mois. Il n'est donc pas paranoïaque de protéger sévèrement sshd.
Une configuration possible
En relisant les divers fichiers de configuration, nous voyons que:
Dans jail.conf
:
- le port = ssh (22 par défaut)
- il y a aussi une définition du «backend» = sshd_backend, dont nous savons sa valeur est initialisée dans le fichier
paths-debian.conf
: sshd_backend = systemd. Il y a donc une redite pas forcément nécessaire. - bantime = 10m
- findtime = 10m
- maxretry = 5
- maxmatches = %(maxretry)s
- action = %(action_)s
Dans jail.d
Debian installe un unique fichier dans ce répertoire defaults-debian.conf
qui contient:
[DEFAULT] banaction = nftables banaction_allports = nftables[type=allports] [sshd] backend = systemd journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd enabled = trueDebian protège donc ssh par défaut. Nous allons modifier un peu, mais avant, voyons le principe:
- la source des informations (backend), c'est le journal géré par systemd
- ce qu'il faut observer dans le journal (journalmatch) c'est :\
SYSTEMD_UNIT=ssh.service + _COMM=sshd
que nous avons eu l'occasion de tester plus haut, sur les logs d'un serveur en production. - le filtre est actif (enabled = true)
Toutes les autres directives sont données dans les divers paragraphes [DEFAULT] déjà rencontrés lors de la lecture séquentielle de la configuration.
Nous allons casser le fichier defaults-debian.conf
, renommé en 000-defaults.conf
et qui ne contiendra plus que :
[DEFAULT] banaction = nftables banaction_allports = nftables[type=allports] maxretry = 3 findtime = 10minutes bantime = 15minutes ignoreip = 192.168.60.47, 2a01:e0a:875:b1d0:ac2a:a7ff:fedd:e607Pour les tests, nous restons gentils. Nous accordons par défaut 3 essais infructueux en l'espace de 10 minutes, avec un bannissement de seulement 1/4 d'heure. Notons que ceci peut être suffisant dans la mesure où bien souvent, un attaquant bloqué n'insiste pas, du moins pendant un assez long temps. En revanche, il peut espacer ses tentatives de bien plus de 10 minutes et passerait alors à travers.
Protection des adresses IPv4 et IPv6 de la station de l'administrateur. On ne sait jamais…
Nous créons un fichier 010-sshd.conf
qui va se focaliser sur la protection de ssh:
[sshd] backend = systemd enabled = true journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd maxretry = 2 findtime = 1minute bantime = 5minutesC'est juste une reprise de ce qu'il y avait dans le fichier fourni par défaut, si ce n'est que pour les tests, le nombre d'essais est limité à 2 et que les durées de recherche et de bannissement sont très réduites.
000-defaults.conf
Nous créons ce fichier qui contiendrait pour le moment:
[DEFAULT] banaction = nftables banaction_allports = nftables[type=allports] maxretry = 3 findtime = 10minutes bantime = 15minutes ignoreip = <liste d'adresses à ne pas bannir> # La ou les adresses publiques d'au moins le poste de l'administrateur est une plutôt bonne idée...
Le nom commençant par «00» assure qu'il sera lu le premier1) dans la liste des fichiers présents dans ce répertoire.
010-sshd.conf
Dans ce fichier, uniquement ce qui concerne le filtre sshd. Pour l'instant, nous mettons des durées très courtes pour l'expérimentation:
[sshd] enabled = true # important! journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd maxretry = 2 findtime = 1minute bantime = 5minutes
Nous pouvons désormais supprimer le fichier defaults-debian.conf
.
Tests
Pour rappel, le filtre installé au démarrage du serveur de test est le suivant:
table inet filter { chain input_ipv4 { icmp type echo-request limit rate 5/second burst 5 packets accept } chain input_ipv6 { icmpv6 type { nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept icmpv6 type echo-request limit rate 5/second burst 5 packets accept } chain input { type filter hook input priority filter; policy drop; iifname "lo" accept ct state vmap { invalid : drop, established : accept, related : accept } meta protocol vmap { ip : jump input_ipv4, ip6 : jump input_ipv6 } tcp dport { 22, 80, 443 } accept } }
Un attaquant, lui aussi virtuel, dont la configuration IP est la suivante:
inet 192.168.60.76/24 brd 192.168.60.255 scope global dynamic inet6 2a01:e0a:875:b1d0:f80d:77c3:5e30:243a/64 scope global dynamic
Va essayer (maladroitement) de forcer ssh sur le serveur de test.
Nous observons pendant ce temps les logs au fil de l'eau, tel que les voit fail2ban:
journalctl _SYSTEMD_UNIT=ssh.service + _COMM=sshd -f juin 30 18:19:13 trixie-nft sshd-session[1040]: Invalid user admin from 2a01:e0a:875:b1d0:5054:ff:fefe:c099 port 55938 juin 30 18:19:18 trixie-nft sshd-session[1040]: pam_unix(sshd:auth): check pass; user unknown juin 30 18:19:18 trixie-nft sshd-session[1040]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=2a01:e0a:875:b1d0:5054:ff:fefe:c099 juin 30 18:19:20 trixie-nft sshd-session[1040]: Failed password for invalid user admin from 2a01:e0a:875:b1d0:5054:ff:fefe:c099 port 55938 ssh2 juin 30 18:21:01 trixie-nft sshd-session[1065]: Invalid user admin from 192.168.60.76 port 45056 juin 30 18:21:09 trixie-nft sshd-session[1065]: pam_unix(sshd:auth): check pass; user unknown juin 30 18:21:09 trixie-nft sshd-session[1065]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.60.76 juin 30 18:21:11 trixie-nft sshd-session[1065]: Failed password for invalid user admin from 192.168.60.76 port 45056 ssh2 juin 30 18:21:13 trixie-nft sshd[750]: Timeout before authentication for connection from 2a01:e0a:875:b1d0:5054:ff:fefe:c099 to 2a01:e0a:875:b1d0:5054:ff:fe79:7d6f, pid = 1040 juin 30 18:23:01 trixie-nft sshd[750]: Timeout before authentication for connection from 192.168.60.76 to 192.168.60.5, pid = 1065
table inet filter { chain input_ipv4 { icmp type echo-request limit rate 5/second burst 5 packets accept } chain input_ipv6 { icmpv6 type { nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept icmpv6 type echo-request limit rate 5/second burst 5 packets accept } chain input { type filter hook input priority filter; policy drop; iifname "lo" accept ct state vmap { invalid : drop, established : accept, related : accept } meta protocol vmap { ip : jump input_ipv4, ip6 : jump input_ipv6 } tcp dport { 22, 80, 443 } accept } } table inet f2b-table { set addr6-set-sshd { type ipv6_addr elements = { 2a01:e0a:875:b1d0:5054:ff:fefe:c099 } } set addr-set-sshd { type ipv4_addr elements = { 192.168.60.76 } } chain f2b-chain { type filter hook input priority filter - 1; policy accept; tcp dport 22 ip6 saddr @addr6-set-sshd reject with icmpv6 port-unreachable tcp dport 22 ip saddr @addr-set-sshd reject with icmp port-unreachable } }