How To: Use fail2ban to Protect SSH

fail2ban software

I have a number of servers, including a few on the home office network, that accept SSH connections. Even though they are serving on different (non-standard) SSH ports, there are regular attempts made to break it via brute-force – I can see how some random IP addresses start trying to log in using different standard user names. It’s therefore never too late to use additional software for protecting SSH service, something like fail2ban.

What is fail2ban?

fail2ban is a tool that monitors OS logs, identifies failed connection and authentication (login) attempts and then temporarily bans these IP addresses using IPtables.

The idea is that any IP address that failed to login multiple times within a period of time must be blocked from further attempts to log in on a firewall level. This minimises risks because connections are simply blocked rather than allowed to try another username/password combination.

INTERESTING: fail2ban can do a lot more than just protect your SSH service – it has a growing library of contextual log files knowledge.

Install fail2ban in Ubuntu

Even on my Raspberry system I can just do this to install fail2ban:

$ sudo apt install fail2ban

IMPORTANT: double-check that you have iptables installed – think it would be installed as part of dependencies for fail2ban.

Once installed, this software needs to be activated – so you need to start it using systemctl or service command.

Configure fail2ban

Before we can start, it makes sense to customise fail2ban to make sure it’s going to work properly.

Basic settings I focus on are:

  • SSH port – by default fail2ban will keep blocking standard SSH port 22, which isn’t going to be all that helpful if your SSH service is listening on a different TCP port
  • Configure email – fail2ban will notify you of new bans/unbans

So just edit the /etc/fail2ban/jail.conf file as root. I made the following changes:

Email settings for fail2ban

Specifying custom port 202 for my SSH service

How to Use fail2ban

Start the service:

$ sudo systemctl start fail2ban

and check its log file:

2020-01-09 22:32:55,710 fail2ban.server         [6038]: INFO    --------------------------------------------------  
2020-01-09 22:32:55,712 fail2ban.server         [6038]: INFO    Starting Fail2ban v0.10.2  
2020-01-09 22:32:55,727 fail2ban.database       [6038]: INFO    Connected to fail2ban persistent database '/var/lib/fail2ban/fail2ban.sqlite3'  
2020-01-09 22:32:55,731 fail2ban.jail           [6038]: INFO    Creating new jail 'sshd'  
2020-01-09 22:32:55,779 fail2ban.jail           [6038]: INFO    Jail 'sshd' uses pyinotify {}  
2020-01-09 22:32:55,798 fail2ban.jail           [6038]: INFO    Initiated 'pyinotify' backend  
2020-01-09 22:32:55,801 fail2ban.filter         [6038]: INFO      maxLines: 1  
2020-01-09 22:32:55,934 fail2ban.server         [6038]: INFO    Jail sshd is not a JournalFilter instance  
2020-01-09 22:32:55,936 fail2ban.filter         [6038]: INFO    Added logfile: '/var/log/auth.log' (pos = 385669, hash = 9d2089e21756515d4394ead79bad08c298835101)  
2020-01-09 22:32:55,939 fail2ban.filter         [6038]: INFO      encoding: UTF-8  
2020-01-09 22:32:55,940 fail2ban.filter         [6038]: INFO      maxRetry: 3  
2020-01-09 22:32:55,942 fail2ban.filter         [6038]: INFO      findtime: 600  
2020-01-09 22:32:55,943 fail2ban.actions        [6038]: INFO      banTime: 1800  
2020-01-09 22:32:55,974 fail2ban.jail           [6038]: INFO    Jail 'sshd' started  
2020-01-10 02:46:49,790 fail2ban.filter         [6038]: INFO    [sshd] Found - 2020-01-10 02:46:49  
2020-01-10 02:46:49,825 fail2ban.filter         [6038]: INFO    [sshd] Found - 2020-01-10 02:46:49  
2020-01-10 02:46:51,811 fail2ban.filter         [6038]: INFO    [sshd] Found - 2020-01-10 02:46:51  
2020-01-10 02:46:52,382 fail2ban.actions        [6038]: NOTICE  [sshd] Ban

How To Inspect fail2ban Logs

As you can see from the output, the service created a “jail” for SSHd service and started looking at failed SSH login attempts. I started fail2ban at 22:32 last night, and at 2:46am got the first IP address blocked: it found 3 failed logins from and banned it immediately.

You can also check iptables, they might have some IP addresses blocked already:

root@srv:/# iptables -nvL
 Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
  pkts bytes target     prot opt in     out     source               destination
   266 17432 f2b-sshd   tcp  --  *      *              multiport dports 202
 Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
  pkts bytes target     prot opt in     out     source               destination
 Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
  pkts bytes target     prot opt in     out     source               destination
 Chain f2b-sshd (1 references)
  pkts bytes target     prot opt in     out     source               destination
     0     0 REJECT     all  --  *      *            reject-with icmp-port-unreachable
   266 17432 RETURN     all  --  *      *  

That’s it for one day. Hope you’ve learned something new today!

See Also

How To Generate ed25519 SSH Key

Generating ed25519 SSH Key

I’m hoping to reinstall my MacBook Pro 15″ 2017 with a fresh macOS Catalina sometime soon, and part of preparations is testing my install methods (hello, brew!) and configuration files migration. Today I decided to setup a new SSH keypair.

What is ed25519?

ed25519 is a relatively new cryptography solution implementing Edwards-curve Digital Signature Algorithm (EdDSA).

I say relatively, because ed25519 is supported by OpenSSH for about 5 years now – so it wouldn’t be considered a cutting edge. Still, people are such creatures of habits that many IT professionals daily using SSH/SCP haven’t even heard of this key type.

Similarly, not all the software solutions are supporting ed25519 right now – but SSH implementatinos in most modern Operating Systems certainly support it.

Why ed25519 Key is a Good Idea

Compared to the most common type of SSH key – RSA – ed25519 brings a number of cool improvements:

  • it’s faster: to generate and to verify
  • it’s more secure
  • collision resilience – this means that it’s more resilient against hash-function collision attacks (types of attacks where large numbers of keys are generated with the hope of getting two different keys have matching hashes)
  • keys are smaller – this, for instance, means that it’s easier to transfer and to copy/paste them

Generate ed25519 SSH Key

Here’s the command to generate an ed25519 SSH key:

greys@mcfly:~ $ ssh-keygen -t ed25519 -C ""
Generating public/private ed25519 key pair.
Enter file in which to save the key (/Users/greys/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/greys/.ssh/id_ed25519.
Your public key has been saved in /Users/greys/.ssh/
The key fingerprint is:
The key's randomart image is:
+--[ED25519 256]--+
|       */Xoo     |
|  . . .===..o    |
|   + .Eo+.oo     |
|    o ..o.+.     |
|   .   .S  + .   |
|  . .     . *    |
|   . . . + o .   |
|      o O .      |
|     .*Xo=       |

That’s it – this keypair is ready to be deployed to SSH servers, GitHub or any other service that can use them.

Check out how short the public key is:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0wmN/Cr3JXqmLW7u+g9pTh+wyqDHpSQEIQczXkVx9q

See Also

Confirm Firewall Configuration in RHEL 8

List Firewall Rules in RHEL 8

I’m fascinated by the improvements and new features in RHEL 8, plus it’s a primary distro used in most corporate environments – so expect to quite a number of posts related to it in the nearest future.

The default interface for managing firewalls in RHEL 8 is firewalld and specifically firewall-cmd command.

Show Active Zones in RHEL 8

There’s a concept of zones – security domains – in RHEL 8 firewalls. You assign each of available network interfaces on your Red Hat Enterprise Linux system to one or more of these domains.

That’s why the first step is to confirm these zones, to see which ones are actively managing access for each network device:

root@rhel8:~ # firewall-cmd --get-active-zones
interfaces: enp2s0
interfaces: virbr0

List All Rules for Firewall Zone in RHEL 8

I’m interested in the primary physical network interface – enp2s0. It belongs to the home zone as per previous command, so that’s the zone we’ll list all the rules fore:

root@rhel8:~ # firewall-cmd --list-all --zone=home
home (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp2s0
  services: cockpit dhcpv6-client mdns samba-client ssh
  ports: 5901/tcp
  masquerade: no
  rich rules: 

From the output below, I have highlighted additionally enabled ports – 5901 is the one for VNC client that allows me to access graphics desktop session on my RHEL 8 PC remotely.

That’s it for today! Thanks for stopping by and talk soon!

See Also

sudo Meaning: What Does sudo Mean?

sudo allows you to run a Unix command as a different user. Many beginner users are asking for meaning of the sudo command, so here’s my take.

What sudo does

Using /etc/sudoers file to confirm what privileges are available to you, sudo command effectively elevates your access rights, thus allowing you to run commands and access files which would otherwise be not available to you. sudo runs these commands as root by default.

sudo meaning

The meaning of sudo command is:

  • su (switch user)
  • do action (run the specified command under specified user)

Default user in sudo

Unless specified, user is assumed to be root. So when you’re running some sommand using sudo, your specified command is executed as root.

Using one of the most basics examples: id command shows your current username, its user id (UID), group id (GID) and group membership:

greys@s2:~ $ id
uid=1000(greys) gid=1000(greys) groups=1000(greys),989(libvirt)

When we’re running id using sudo, we’re asking sudo to first become user root and to then run the specified command (id) as root:

greys@s2:~ $ sudo id
uid=0(root) gid=0(root) groups=0(root)

As you can see from the output, we get all the root information returned: UID =0, GID=0.

See Also

How To: Setup sudo in Debian

sudo in Debian Linux

Apparently, Debian installer doesn’t install or activate sudo by default. This means that sudo command is not found the only privilege escalation method available is becoming root via su command. Since I like and use sudo daily, I decided to install and setup it on Debian VM.

Install sudo package in Debian

That’s the very first step you’ll need to do: use apt to install sudo. You need to become root before you do it, of course (so you must know root user password for your Debian install):

greys@debian:~$ su -
root@debian:~ # apt install sudo
Reading package lists… Done
Building dependency tree
Reading state information… Done
The following NEW packages will be installed:
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/1,245 kB of archives.
After this operation, 3,886 kB of additional disk space will be used.
Selecting previously unselected package sudo.
(Reading database … 174742 files and directories currently installed.)
Preparing to unpack …/sudo_1.8.27-1_amd64.deb …
Unpacking sudo (1.8.27-1) …
Setting up sudo (1.8.27-1) …
Processing triggers for man-db (2.8.5-2) …
Processing triggers for systemd (241-5) …
root@debian:~ # sudo
usage: sudo -h | -K | -k | -V
usage: sudo -v [-AknS] [-g group] [-h host] [-p prompt] [-u user]
usage: sudo -l [-AknS] [-g group] [-h host] [-p prompt] [-U user] [-u user] [command]
usage: sudo [-AbEHknPS] [-r role] [-t type] [-C num] [-g group] [-h host] [-p prompt] [-T timeout] [-u user] [VAR=value] [-i|-s] []
usage: sudo -e [-AknS] [-r role] [-t type] [-C num] [-g group] [-h host] [-p prompt] [-T timeout] [-u user] file …

Configure /etc/sudoers File

/etc/sudoers is the main configuration file for sudo command. It contains list of users and groups that are allowed to become root (or become other users by invoking su command as root).

Here’s the default file in Debian 10 Buster:

root@debian:~ # cat /etc/sudoers
# This file MUST be edited with the 'visudo' command as root.
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
# See the man page for details on how to write a sudoers file.
Defaults        env_reset
Defaults        mail_badpass
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# Host alias specification
# User alias specification
# Cmnd alias specification
# User privilege specification
root    ALL=(ALL:ALL) ALL
# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL
# See sudoers(5) for more information on "#include" directives:
#includedir /etc/sudoers.d

I’ve highlighted the 3 most important elements of this file at this early stage:

root    ALL=(ALL:ALL) ALL

This is the line that allows you to debug sudo commands as root user.

At this means that any user that belongs to group sudo will also be allowed to use sudo commands:

%sudo   ALL=(ALL:ALL) ALL

Finally, this part includes additional configuration files from /etc/sudoers.d directory:

#includedir /etc/sudoers.d

… this means you don’t have to edit /etc/sudoers file but instead can create a specific file in /etc/sudoers.d and name it self-descriptively, like:


meaning, that this file will contain usernames and privileges required by web-server admins (usually commands like stopping/starting Apache or nginx webserver).

Since this is a very basic tutorial, we don’t have to edit the file at all – just need to add our user (mine is greys, as you remember) to the sudo group and check.

Add user to sudo group

Step 1: let’s make sure sudo is not accessible before we begin

This needs to be run as your regular user, not as root:

greys@debian:~$ sudo -i
[sudo] password for greys:
greys is not in the sudoers file.  This incident will be reported.

Let’s check my groups just to be sure there’s no sudo among them:

greys@debian:~$ id greys
uid=1000(greys) gid=1000(greys) groups=1000(greys),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),112(bluetooth),116(scanner)

Step 2: add user to sudo group

Excellent, now it’s time to add user greys to the group sudo (we must become root again to run usermod command)

root@debian:~ # usermod -a -G sudo greys
root@debian:~ # id greys
uid=1000(greys) gid=1000(greys) groups=1000(greys),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),108(netdev),112(bluetooth),116(scanner)

As you can see, I’m now a member of the sudo group!

Step 3: Log out and log back in for group membership to be recognised

Now you need to disconnect from your server or desktop session and log in again, so that your group membersip is recognised. One reconnected, check your groups with id command and try sudo again:

greys@debian9:~$ id
uid=1000(greys) gid=1000(greys) groups=1000(greys),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),108(netdev),112(bluetooth),116(scanner)

so yes, we’re a member of sudo group now… This is the moment of truth! Let’s try to become root:

greys@debian:~$ sudo -i
root@debian:~ # id
uid=0(root) gid=0(root) groups=0(root)

Tha’ts it for today!

See Also

Installing Kali Linux 2019.2

It appears that Kali Linux is supported on the latest Raspberry Pi model, RPi 4. So I thought I’d get familiar with the install process and Kali Linux basics by trying it out in a VirtualBox VM.

What is Kali Linux?

Kali Linux is a Debian-based Linux distro made by security professionals (Offensive Security) for security assessments, penetration testing and digital forensics. It comes with hundreds of open-source tools grouped for effective use and optimized for speedy network discovery and vulnerability assessment.

How To Download Kali Linux

I downloaded a normal ISO image to see how installer works, but it’s also possible to just get the VirtualBox VM image with Kali Linux.

Kali Linux Installation Screenshots

Here are the screens of my install process – nothing unusual and a fairly smooth install process.

I think I got them here in the right order, but if something’s off and not straightforward – let me know!

As always, I had to do install more than once because disk size is suggested as 8GB in VirtualBox which is not enough for modern Debian derived installs – so installer fails halfway and you need to expand the disk – I’ve seen the same issues trying to install Debian 10 Buster.

Will try and install it onto Raspberry Pi 4 as one of the upcoming Unix Tutorial projects.

See Also

Deploy Your SSH key To Remote Server

Adding SSH key to remote server

One of the greatest improvements introduced by the SSH protocol is key-based authentication – meaning your client and SSH server establish validity of your SSH keypair and let you gain remote SSH access without asking for your password.

SSH Authentication with Passwords

By default, SSH server will ask for your password when you’re trying to connect. Unless you specify a username, your SSH client will set it automatically to your username on local (client) system:

Connecting to remote server using SSH

In this example above, I’m running command line (iTerm2) session on my Macbook. greys is my local username, maverick is the hostname on my Macbook. I’m typing ssh command and specifying the server to connect to – with hostname becky.

As you can see, next thing that happens is that I get a password prompt.

How Key Based SSH Access Works

Key-based SSH authentication takes an extra step to setup but then saves you tons of time in the future:

  • you deploy your public SSH key to remote server (need to type SSH password for possibly the last time)
  • you start SSH agent to load your private SSH key and to use it for remote connections
  • you connect to the remote SSH server without typing any passwords – still enjoying the same great benefits like encryption and traffic compression that SSH brings

Deploy Your Public SSH Key to Remote Server

You guessed it right! There’s actually a command for that, it’s called ssh-copy-id. What it does is connect to remote SSH server using username and password that you supply and then edit the .ssh/authorized_keys there to include your public key.

When running ssh-copy-id, you need to specify 2 things at a minimum:

  1. The SSH identity (name of a key you want to deploy)
  2. The SSH server name (where you want to add your key to)

Here’s how it works:

greys@maverick:~ $ ssh-copy-id -i .ssh/id_ed25519 becky
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: ".ssh/" /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
greys@'s password:

Number of key(s) added:        1

Now try logging into the machine, with:   "ssh 'becky'"
and check to make sure that only the key(s) you wanted were added.

Next time you attempt to connect, your ssh client will offer remote server a list of SSH identities you have configured on your client. In most recent Unix-like distros you have an SSH agent starting with your graphics login – it’s called GNOME Keyring or or Keychain in MacOS (and there’s plenty of ways to start ssh-agent during startups of sessions like KDE).

The bottom line is that when I try to connect to becky now, my SSH key is offered and, if it’s available (loaded in ssh-agent), I get a passwordless SSH access to remote server:

That’s it for today, have fun!

See Also

How To: Check AppArmor Status


AppArmor is a Linux Kernel security module that implements mandatory access control (MAC) security with per-application profiles in Debian based systems. It’s possible to confirm if AppArmor is enabled  in your Debian or Ubuntu system and to also find out the mode it’s running in.

AppArmor Status with aa-status Command

aa-status command will list the currently loaded AppArmor modules.

For instance, here’s how it looks on a system where AppArmor is inactive (Debian 9 in my case):

root@debian9:~# aa-status 
apparmor module is loaded. 
apparmor filesystem is not mounted.

And here is how AppArmor status is reported on Debian 10 system where it’s activated by default:

root@debian10:~# aa-status 
apparmor module is loaded.
20 profiles are loaded.
18 profiles are in enforce mode.
2 profiles are in complain mode.
0 processes have profiles defined.
0 processes are in enforce mode.
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.

See Also

Cloudflare: Crypto Week 2019


Cloudflare, possibly the best DNS provider (and so much more) available for free, is hosting CryptoWeek 2019 since Monday. I really like this company and host at least 20 DNS zones for my various domains there.

I’m just catching up on reading and thought the Crypto Week 2019 announcement post is a must read for everyone.

While generally the week is spent announcing various improvers around crypto (as in cryptocurrencies), the announcement post talks about broader set of issues with current Internet and about the most recent efforts to vastly improve it.

If TLS, BGP hijacking or DNSSEC mean anything to you (and even more importantly, if they don’t yet!) – please read the Crypto Week 2019 post as you will learn a lot and receive a bunch of great pointers for further reading.


See Also

Test SSHd config on a different SSH port

Screen Shot 2019-05-24 at 16.25.35.png

Sometimes you need to tweak your SSH daemon on an important system and you just don’t know if particular settings will break connectivity to the server or not. In such cases it’s best to test new SSHd config using separate SSH daemon instance and separate SSH port – debug it there and only then apply new configs into your primary SSHd configuration.

Creating New SSHd Config

The easiest is to start by copying /etc/ssh/sshd_config file – you will need sudo/root privileges for that:

greys@s2:~ $ sudo cp /etc/ssh/sshd_config /home/greys

I then just remove everything I don’t need from it, leaving bare minimum. These are the parameters I kept (I ended up renaming my config to /home/greys/sshd_config.minimal after edits)

greys@s2:~ $ grep -v ^# /home/greys/sshd_config.minimal | uniq -u
Port 2222
HostKey /etc/ssh/ssh_host_rsa_key

RSAAuthentication yes
PubkeyAuthentication yes

AuthorizedKeysFile /var/ssh/%u/authorized_keys

PasswordAuthentication no

UsePAM yes

I only updated the SSH Port parameter – you can pick any other number instead of 2222.

Starting SSH daemon with custom config file

There’s a few rules for testing SSH configuration using separate file:

  • you need to have sudo/root privilege (mostly to avoid mess with host SSH keys)
  • it’s better to increase verbosity level to see what’s going on
  • it’s best to run SSHd in foreground (non-daemon) mode

With these principles in mind, here’s the command line to test the config shown above:

greys@s2:~ $ sudo /usr/sbin/sshd -f /home/greys/sshd_config.minimal -ddd -D
debug2: load_server_config: filename /home/greys/sshd_config.minimal
debug2: load_server_config: done config len = 194
debug2: parse_server_config: config /home/greys/sshd_config.minimal len 194
debug3: /home/greys/sshd_config.minimal:1 setting Port 2222
debug3: /home/greys/sshd_config.minimal:10 setting HostKey /home/greys/ssh_host_rsa_key
debug3: /home/greys/sshd_config.minimal:12 setting RSAAuthentication yes
/home/greys/sshd_config.minimal line 12: Deprecated option RSAAuthentication
debug3: /home/greys/sshd_config.minimal:13 setting PubkeyAuthentication yes
debug3: /home/greys/sshd_config.minimal:18 setting AuthorizedKeysFile /var/ssh/%u/authorized_keys
debug3: /home/greys/sshd_config.minimal:20 setting PasswordAuthentication no
debug3: /home/greys/sshd_config.minimal:22 setting UsePAM yes
debug1: sshd version OpenSSH_7.4, OpenSSL 1.0.2k-fips 26 Jan 2017
debug1: private host key #0: ssh-rsa SHA256:g7xhev6zJefXRFc0ClAG4rzpFI1Ts8H7PhQ/h3PTmLM
debug1: rexec_argv[0]=’/usr/sbin/sshd’
debug1: rexec_argv[1]=’-f’
debug1: rexec_argv[2]=’/home/greys/sshd_config.minimal’
debug1: rexec_argv[3]=’-ddd’
debug1: rexec_argv[4]=’-D’
debug3: oom_adjust_setup
debug1: Set /proc/self/oom_score_adj from 0 to -1000
debug2: fd 3 setting O_NONBLOCK
debug1: Bind to port 2222 on
Server listening on port 2222.
debug2: fd 4 setting O_NONBLOCK
debug3: sock_set_v6only: set socket 4 IPV6_V6ONLY
debug1: Bind to port 2222 on ::.
Server listening on :: port 2222.

That’s it, the configuration is ready to be tested (assuming your firewall on server doesn’t block port 2222).

Testing SSH connectivity using Different SSH Port

Here’s my login session in a separate window, connecting from my MacBook Pro to the s2 server on SSH port 2222 (I have masked my static IP with aaa.bbb.ccc.ddd and my s2 server’s IP with eee.fff.ggg.hhh):

greys@MacBook-Pro:~ $ ssh s2 -p 2222
Warning: untrusted X11 forwarding setup failed: xauth key data not generated
Last login: Fri May 24 15:53:59 2019 from aaa.bbb.ccc.ddd
debug3: Copy environment: XDG_SESSION_ID=14813
debug3: Copy environment: XDG_RUNTIME_DIR=/run/user/1000
SSH_CLIENT=aaa.bbb.ccc.ddd 64168 2222
SSH_CONNECTION=aaa.bbb.ccc.ddd 64168 eee.fff.ggg.hhh 2222
greys@s2:~ $ uptime
16:18:08 up 86 days, 17:32, 2 users, load average: 1.00, 1.02, 1.05

Pretty cool, huh?

See Also