Enhance SSH protection with additional Fail2Ban filter patterns on Debian 8 (Jessie)

botond published March 2019, 05, Thu - 09:23 time

Content

 

Introductory

On our Debian 8 (Jesssie) based server, the Fail2Ban log entries that are not recognized by Fail0.8.13Ban's 2 filters on the system slip when you use it, so your system is less protected against attacks that cause these entries.

In this description, SSH we will solve the blocking of attempts with random usernames to our server by expanding the Fail2Ban filter patterns.

I have made this description for readers who run a Debian 8 (Jessie) based server and use Fail2Ban on it, which they would love to increase its efficiency by setting up another filter pattern.

 

 

Detecting the problem

We need to be able to spot the attacks first, because they usually take place hidden in the background. Therefore, it is not enough to rely on the effective operation of our installed protection tools, but we need to beetle the log files regularly and get more information about the security status of our system by running the appropriate query commands.

The attacks that are the subject of this description are first a lastb command to observe it as root:

lastb

The command lists unsuccessful user login attempts at / var / log / wtmp file, which is not a text file, so only this command can read properly.

At the output of this you get only one line, for example:

btmp begins Wed May  1 00:06:02 2019

In this case, there was no unsuccessful login attempt from the indicated time.

Or we get a shorter or longer list of login attempts. For example:

ftptest  ssh:notty    <kliens IP-címe>   Wed May  8 16:30 - 16:30  (00:00)    
ftptest  ssh:notty    <kliens IP-címe>   Wed May  8 16:30 - 16:30  (00:00)    
apagar   ssh:notty    <kliens IP-címe>   Tue May  7 15:33 - 15:33  (00:00)    
apagar   ssh:notty    <kliens IP-címe>   Tue May  7 15:33 - 15:33  (00:00)    
user01   ssh:notty    <kliens IP-címe>   Mon May  6 15:20 - 15:20  (00:00)    
user01   ssh:notty    <kliens IP-címe>   Mon May  6 15:20 - 15:20  (00:00)    
root     ssh:notty    <kliens IP-címe>   Sun May  5 15:20 - 15:20  (00:00)    
root     ssh:notty    <kliens IP-címe>   Sun May  5 14:53 - 14:53  (00:00)    

btmp begins Sun May  5 14:53:32 2019

Where the first column contains the usernames they are trying to access, then display the console or remote SSH login. This example is about remote attempts. This is followed by the client trying to log on IP addresses, and then the date of the event.

This is a short list that I listed days after setting up the filter, so there was only one attempt left per day, but one such attempt arrived about every minute before that.

About these /var/log/auth.log you can also get information in a file by searching for it as follows:

cat /var/log/auth.log | grep -i "invalid user"
May  6 15:20:15 <saját hosztnevünk> sshd[xxx]: Invalid user user01 from <próbálkozó IP-címe>
May  6 15:20:15 <saját hosztnevünk> sshd[xxx]: input_userauth_request: invalid user user01 [preauth]
May  6 15:20:17 <saját hosztnevünk> sshd[xxx]: Failed password for invalid user user01 from <próbálkozó IP-címe> port xxx ssh2
[...]

(All attempts here result in 3 rows)

Larger amounts of these may pose a risk over time, because with an existing user and a poorly set password, there is a good chance they can be hit by the law of big numbers. Of course, the chances are still very low, but if we can defend ourselves effectively, why not?

The reason for the lack of protection

This problem occurs because Debian 8 (Jessie) can be installed from the official repository Fail0.8.13Ban package for 2 version does not include the filter sample for this case. If you use the Fail2Ban client program to get the status of your SSH jail, you will not find much:

fail2ban-client status ssh
Status for the jail: ssh
|- filter
|  |- File list:	/var/log/auth.log 
|  |- Currently failed:	0
|  `- Total failed:	0
`- action
   |- Currently banned:	0
   |  `- IP list:	
   `- Total banned:	0

So in vain the a /var/log/auth.log "invalid user" entries in our file, but they are not blocked by Fail2B.

 

 

The solution

Here, I would suggest that you switch the SSH daemon to authentication key login only if you can. In case you still need to log in with passwords (e.g. need more clients, need to log in from changing locations, etc.), continue with this guide.

The solution is simpler than you think: in the Debian 9 (Stretch) repository Fail2Ban 0.9.6 version already contains the appropriate filter pattern we need here. Here you do not have to install the newer version of Debian 8 from source, but simply apply the necessary filter.

Migrate filter sample from Debian 9

If you also have a Debian 9 (Stretch) installation at hand, and it has Fail2Ban, or has it installed, just list the appropriate filter file:

cat /etc/fail2ban/filter.d/sshd.conf

The output of this, which contains filter samples for version 0.9.6:

[...]
failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error|failed) for .* from <HOST>( via \S+)?\s*$
            ^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
            ^%(__prefix_line)sFailed \S+ for (?P<cond_inv>invalid user )?(?P<user>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)) from <HOST>(?: port \d+)?(?: ssh\d*)?(?(cond_user):|(?:(?:(?! from ).)*)$)
            ^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
            ^%(__prefix_line)s[iI](?:llegal|nvalid) user .*? from <HOST>(?: port \d+)?\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because not in any group\s*$
            ^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
            ^%(__prefix_line)s(?:error: )?Received disconnect from <HOST>: 3: .*: Auth fail(?: \[preauth\])?$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
            ^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked<SKIPLINES>(?P=__prefix)(?:error: )?Received disconnect from <HOST>: 11: .+ \[preauth\]$
            ^(?P<__prefix>%(__prefix_line)s)Disconnecting: Too many authentication failures for .+? \[preauth\]<SKIPLINES>(?P=__prefix)(?:error: )?Connection closed by <HOST> \[preauth\]$
            ^(?P<__prefix>%(__prefix_line)s)Connection from <HOST> port \d+(?: on \S+ port \d+)?<SKIPLINES>(?P=__prefix)Disconnecting: Too many authentication failures for .+? \[preauth\]$
            ^%(__prefix_line)s(error: )?maximum authentication attempts exceeded for .* from <HOST>(?: port \d*)?(?: ssh\d*)? \[preauth\]$
            ^%(__prefix_line)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.*$
[...]

From here we need one line, the one I highlighted in green. This filter pattern fits perfectly with the "invalid user" error messages in the above auth.log file. If you do not have a Debian 9 installation at hand or an 0.9 Fail2 filter in another distribution, use the pattern here.

Copy this line and then paste the Located on Debian 8 (Jessie) sshd.conf:

nano /etc/fail2ban/filter.d/sshd.conf

Similarly we can do 3 here. line to look like our file failregex part of:

[...]
failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error) for .* from <HOST>( via \S+)?\s*$
            ^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
            ^%(__prefix_line)sFailed \S+ for (?P<cond_inv>invalid user )?(?P<user>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)) from <HOST>(?: port \d+)?(?: ssh\d*)?(?(cond_user):|(?:(?:(?! from ).)*)$)
            ^%(__prefix_line)sFailed \S+ for .*? from <HOST>(?: port \d*)?(?: ssh\d*)?(: (ruser .*|(\S+ ID \S+ \(serial \d+\) CA )?\S+ %(__md5hex)s(, client user ".*", client host ".*")?))?\s*$
            ^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
            ^%(__prefix_line)s[iI](?:llegal|nvalid) user .* from <HOST>\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because not in any group\s*$
            ^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
            ^%(__prefix_line)sReceived disconnect from <HOST>: 3: \S+: Auth fail$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
[...]

Let's save it.

Check and fine-tune jail configuration

Next, open the jail.conf file to check your SSH settings:

nano /etc/fail2ban/jail.conf

Find the "[ssh]" jail here:

[...]
[ssh]

enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 2
findtime = 3600
[...]

And let's check the settings:

  • enabled: true. Keep the jail on.
  • port: ssh. If you use a port other than the default 22 SSH port number, overwrite it with the correct port here
  • filter: sshd. Here the ssh jail refers to the appropriate filter file.
  • logpath: this log file is being monitored by Fail2
  • maxretry: Here you can control the severity of the blocking. After you have forgotten your password, it will allow you so many more attempts.
  • findtime: And here seconds determine how much time slots the system will monitor for failed login attempts with the same IP address

Set these according to your needs. In the example above, I let 2 try again in 3600 seconds, so this is a pretty strict setting.

If so, save the jail.conf file as well.

Restart Fail2Ban

After that, there was only a restart in Fail2:

systemctl restart fail2ban.service

A systemctl after the reboot command, let's make sure the jails really started because they can't start them with a bad character in the filter:

cat /var/log/fail2ban.log | tail -20

And in this we have to see that all the jails started nicely:

[...]
2019-05-09 23:15:29,356 fail2ban.jail   [31572]: INFO    Jail 'ssh' started
2019-05-09 23:15:29,358 fail2ban.jail   [31572]: INFO    Jail 'apache' started
2019-05-09 23:15:29,359 fail2ban.jail   [31572]: INFO    Jail 'apache-multiport' started
2019-05-09 23:15:29,360 fail2ban.jail   [31572]: INFO    Jail 'apache-noscript' started
2019-05-09 23:15:29,361 fail2ban.jail   [31572]: INFO    Jail 'pureftpd' started
2019-05-09 23:15:29,363 fail2ban.jail   [31572]: INFO    Jail 'dovecot-pop3imap' started
2019-05-09 23:15:29,364 fail2ban.jail   [31572]: INFO    Jail 'postfix-sasl' started

If there is an error here, you need to go back to the filter file and look for any extra characters when inserting the new line, etc.

 

 

Control

After a few hours or days, we can check the fruits of our work, depending on how often this type of unauthorized access attempt has occurred on our server:

fail2ban-client status ssh

And if there were any attempts during this time, we can already see how Fail2B looks beautifully like green paint:

Status for the jail: ssh
|- filter
|  |- File list:	/var/log/auth.log 
|  |- Currently failed:	5
|  `- Total failed:	32
`- action
   |- Currently banned:	2
   |  `- IP list:	xxx.xxx.xxx.xxx, xxx.xxx.xxx.xxx
   `- Total banned:	9

 

Conclusion

With this small simple filter add-on, we have enhanced the efficiency of the Fail2Ban program, which in this case can significantly slow down the rate of entry attempts. We can't stop it completely with this method, but we can drastically reduce the number of attempts from the same IP address.