468 lines
12 KiB
Markdown
468 lines
12 KiB
Markdown
---
|
|
title: Securing SSH 🤖
|
|
date: 2021-12-07T22:41:02Z
|
|
author: "ahmza"
|
|
draft: false
|
|
weight:
|
|
|
|
tags: ["HomeLab", "SSH", "RemoteAccess"]
|
|
description: "Ever wanted to make your SSH connections more secure. Read this"
|
|
canonicalURL: "https://blog.ahmza.com/Page%28/posts/securing-ssh.md%29"
|
|
|
|
cover:
|
|
image: "/webp/posts/secure-ssh/feature-anim.webp"
|
|
alt: "ssh-alt image" # alt text
|
|
caption: "<text>" # display caption under cover
|
|
relative: false # when using page bundles set this to true
|
|
showtoc: true
|
|
searchHidden: false
|
|
---
|
|
|
|
# **Introduction**
|
|
|
|
SSH is a widely used protocol to access remote devices. The problem with SSH is
|
|
at the authentication stage, Brute force attacks are one of the ways an SSH
|
|
connection is compromised. A malicious actor will constantly hammer the
|
|
remote host with different passwords until it can gain access. With further
|
|
configuration of SSH we can make the service a lot more secure.
|
|
|
|
# **Configure SSH**
|
|
|
|
{{< video label="this is fine" webm="/webm/this-is-fine.webm" >}}
|
|
|
|
> **Famous last words:** Configuring SSH like many things in Linux is as simple
|
|
> as editing a few config files
|
|
|
|
Always **create a backup file**, You can end up locking yourself out of a
|
|
device. Try this on a machine that is not critical.
|
|
|
|
## **Create SSH Keys**
|
|
|
|
SSH keys use public and private key encryption for authentication.
|
|
|
|
To create a set of keys, **on the client do:**
|
|
|
|
```bash
|
|
ssh-keygen -t ecdsa -C <username>@<hostname>
|
|
```
|
|
|
|
- -t ecdsa - sets the key encryption type to _ecdsa_
|
|
- -C - Comment or name of the _SSH_ key. **Replace content in <>**
|
|
|
|
Where you want to save the keys, **I recommend the default location**
|
|
|
|
```bash
|
|
Enter file in which to save the key (/home/user/.ssh/id_rsa):
|
|
```
|
|
|
|
Password for the ssh key, please use a strong password.
|
|
|
|
```bash
|
|
Enter passphrase (empty for no passphrase):
|
|
```
|
|
|
|
Once done you will see a confirmation screen like the one below
|
|
|
|
```
|
|
Output
|
|
Your identification has been saved in /home/user/.ssh/id_rsa
|
|
Your public key has been saved in /home/user/.ssh/id_rsa.pub
|
|
The key fingerprint is:
|
|
SHA256:tIurljnWJRBc1RnHUJQ5GtND7p9HWKnjV6zjE9AFR1Y user@host
|
|
The key's randomart image is:
|
|
+---[RSA 3072]----+
|
|
| . .....oX=o.oE|
|
|
| o =oB oo|
|
|
| . . +.+ o.|
|
|
| . . .... o+ |
|
|
| . S .+. +|
|
|
| .... ..o+.|
|
|
| +.o. .=o.|
|
|
| * .. .oo |
|
|
| o.o. .. |
|
|
+----[SHA256]-----+
|
|
```
|
|
|
|
## **Copy SSH Keys**
|
|
|
|
```bash
|
|
ssh-copy-id user@host
|
|
```
|
|
|
|
> **Quick note:** user@host is the **username** on the **remeote host** you
|
|
> would like to copy your public key to.
|
|
|
|
**If asked to confirm the connection to the remote host answer yes**, this
|
|
happens when a new machine with an unknown identity wants to connect.
|
|
|
|
```
|
|
Output
|
|
The authenticity of host 'host(host)' can't be established.
|
|
ECDSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:xx.
|
|
Are you sure you want to continue connecting (yes/no)? yes
|
|
```
|
|
|
|
**Enter a password** for the user on the remote host
|
|
|
|
```
|
|
Output
|
|
/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
|
|
user@host's password:
|
|
```
|
|
|
|
Access the remote device using the SSH key
|
|
|
|
To verify this, SSH to the device and **it should ask for the Keys password not
|
|
the users**
|
|
|
|
```bash
|
|
Enter passphrase for key '/home/user/.ssh/id_rsa':
|
|
```
|
|
|
|
## **Modify SSH Config**
|
|
|
|
create a backup
|
|
|
|
```bash
|
|
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
|
|
```
|
|
|
|
open the original file in a text editor
|
|
|
|
**NeoVim BTW!!**
|
|
|
|
```bash
|
|
sudo vim /etc/ssh/sshd_config
|
|
```
|
|
|
|
For a new _Linux_ user this might be daunting. Take your time and you will be
|
|
fine 😊
|
|
|
|
{{< video label="thats a big one" webm="/webm/thats-a-big-one.webm" >}}
|
|
|
|
**Lines we want to modify**
|
|
|
|
> **Note:** uncomment lines as well as add missing lines
|
|
|
|
- Protocol 2
|
|
- Port 2222
|
|
- PermitRootLogin no
|
|
- Max Auth Tries 5
|
|
- PasswordAuthentication no
|
|
- PermitEmptyPasswords no
|
|
- AllowUsers <user>
|
|
- ClientAliveInterval 180
|
|
|
|
```
|
|
# $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $
|
|
|
|
# This is the sshd server system-wide configuration file. See
|
|
# sshd_config(5) for more information.
|
|
|
|
# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
|
|
|
# The strategy used for options in the default sshd_config shipped with
|
|
# OpenSSH is to specify options with their default value where
|
|
# possible, but leave them commented. Uncommented options override the
|
|
# default value.
|
|
|
|
Include /etc/ssh/sshd_config.d/*.conf
|
|
|
|
Protocol 2
|
|
|
|
Port 2222
|
|
#AddressFamily any
|
|
#ListenAddress 0.0.0.0
|
|
#ListenAddress ::
|
|
|
|
#HostKey /etc/ssh/ssh_host_rsa_key
|
|
#HostKey /etc/ssh/ssh_host_ecdsa_key
|
|
#HostKey /etc/ssh/ssh_host_ed25519_key
|
|
|
|
# Ciphers and keying
|
|
#RekeyLimit default none
|
|
|
|
# Logging
|
|
#SyslogFacility AUTH
|
|
#LogLevel INFO
|
|
|
|
# Authentication:
|
|
|
|
#LoginGraceTime 2m
|
|
PermitRootLogin no
|
|
#StrictModes yes
|
|
MaxAuthTries 5
|
|
#MaxSessions 10
|
|
|
|
#PubkeyAuthentication yes
|
|
|
|
# Expect .ssh/authorized_keys2 to be disregarded by default in future.
|
|
#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
|
|
|
|
#AuthorizedPrincipalsFile none
|
|
|
|
#AuthorizedKeysCommand none
|
|
#AuthorizedKeysCommandUser nobody
|
|
|
|
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
|
|
#HostbasedAuthentication no
|
|
# Change to yes if you don't trust ~/.ssh/known_hosts for
|
|
# HostbasedAuthentication
|
|
#IgnoreUserKnownHosts no
|
|
# Don't read the user's ~/.rhosts and ~/.shosts files
|
|
#IgnoreRhosts yes
|
|
|
|
# To disable tunneled clear text passwords, change to no here!
|
|
PasswordAuthentication no
|
|
PermitEmptyPasswords no
|
|
|
|
AllowUsers <user>
|
|
|
|
# Change to yes to enable challenge-response passwords (beware issues with
|
|
# some PAM modules and threads)
|
|
ChallengeResponseAuthentication no
|
|
|
|
# Kerberos options
|
|
#KerberosAuthentication no
|
|
#KerberosOrLocalPasswd yes
|
|
#KerberosTicketCleanup yes
|
|
#KerberosGetAFSToken no
|
|
|
|
# GSSAPI options
|
|
#GSSAPIAuthentication no
|
|
#GSSAPICleanupCredentials yes
|
|
#GSSAPIStrictAcceptorCheck yes
|
|
#GSSAPIKeyExchange no
|
|
|
|
# Set this to 'yes' to enable PAM authentication, account processing,
|
|
# and session processing. If this is enabled, PAM authentication will
|
|
# be allowed through the ChallengeResponseAuthentication and
|
|
# PasswordAuthentication. Depending on your PAM configuration,
|
|
# PAM authentication via ChallengeResponseAuthentication may bypass
|
|
# the setting of "PermitRootLogin without-password".
|
|
# If you just want the PAM account and session checks to run without
|
|
# PAM authentication, then enable this but set PasswordAuthentication
|
|
# and ChallengeResponseAuthentication to 'no'.
|
|
UsePAM yes
|
|
|
|
#AllowAgentForwarding yes
|
|
#AllowTcpForwarding yes
|
|
#GatewayPorts no
|
|
X11Forwarding yes
|
|
#X11DisplayOffset 10
|
|
#X11UseLocalhost yes
|
|
#PermitTTY yes
|
|
PrintMotd no
|
|
#PrintLastLog yes
|
|
#TCPKeepAlive yes
|
|
#PermitUserEnvironment no
|
|
#Compression delayed
|
|
ClientAliveInterval 180
|
|
#ClientAliveCountMax 3
|
|
#UseDNS no
|
|
#PidFile /var/run/sshd.pid
|
|
#MaxStartups 10:30:100
|
|
#PermitTunnel no
|
|
#ChrootDirectory none
|
|
#VersionAddendum none
|
|
|
|
# no default banner path
|
|
#Banner none
|
|
|
|
# Allow client to pass locale environment variables
|
|
AcceptEnv LANG LC_*
|
|
|
|
# override default of no subsystems
|
|
Subsystem sftp /usr/lib/openssh/sftp-server
|
|
|
|
# Example of overriding settings on a per-user basis
|
|
#Match User anoncvs
|
|
# X11Forwarding no
|
|
# AllowTcpForwarding no
|
|
# PermitTTY no
|
|
# ForceCommand cvs server
|
|
```
|
|
|
|
{{< video label="not so bad" webm="/webm/wasnt-so-bad.webm" >}}
|
|
|
|
**Now lets explain what we just did**
|
|
|
|
- **Protocol 2** - released in 2006, SSH2 introduced improved encryption
|
|
algorithms.
|
|
- **Port 2222** - The port number can be whatever number you want. Choose
|
|
something other than 22 this will help hide your device from malicious actors
|
|
- **PermitRootLogin no** - If the root account gets compromised then the
|
|
attacker has full control so we stop root from logging in over ssh.
|
|
- **MaxAuthTries 5** - After 5 failed attempts the remote host blocks requests
|
|
from that IP for some of time.
|
|
- **PasswordAuthentication no** - The SSH key is installed, no need to have
|
|
password authentication.
|
|
- **PermitEmptyPasswords no** - Don't allow empty passwords
|
|
- **AllowUsers username** - Add the users you want to connect to the remote
|
|
host, others will be dropped.
|
|
- **Client Alive Interval 180** - Drop the connection after 180s of inactivity
|
|
|
|
## **Check ability to connect**
|
|
|
|
restart sshd service
|
|
|
|
```bash
|
|
sudo systemctl restart sshd
|
|
```
|
|
|
|
**Keep the current SSH connection open** and create a new session.
|
|
|
|
If you connect to the remote host then you're set. If not then you still have
|
|
the initial connection to the remote host to roll back the changes and
|
|
troubleshoot.
|
|
|
|
# **Success**
|
|
|
|
{{< video label="clap" webm="/webm/clap.webm" >}}
|
|
|
|
# **Setting up** **_Fail2Ban_**
|
|
|
|
_Fail2Ban_ is an intrusion prevention tool. Although we slow down attackers with
|
|
with MaxAuthTries they can still carry on brute forcing. Which is what Fail2Ban
|
|
does, it actually blocks the connections from that IP.
|
|
|
|
## **Install** **_Fail2Ban_**
|
|
|
|
**We will install Fail2Ban on the remote host**
|
|
First upgrade any packages, then **install** **_Fail2Ban_**
|
|
|
|
```bash
|
|
sudo apt update && sudo apt upgrade
|
|
sudo apt install fail2ban
|
|
```
|
|
|
|
Now we need to enable the service so it starts when the remote host
|
|
starts
|
|
|
|
```bash
|
|
sudo systemctl enable --now fail2ban
|
|
```
|
|
|
|
## **Configure** **_Fail2Ban_**
|
|
|
|
The config file we want to edit is **jail.local** located in **/etc/fail2ban/**
|
|
open jail.local in your editor of choice
|
|
|
|
```bash
|
|
sudo vim /etc/fail2ban/jail.local
|
|
```
|
|
|
|
The config for _Fail2Ban_ is nice and short
|
|
|
|
```
|
|
[DEFAULT]
|
|
bantime = 8h
|
|
ignoreip = 127.0.0.1 <ip addresses to be ignored>
|
|
ignoreself = true
|
|
|
|
[sshd]
|
|
enabled = true
|
|
port = 2222
|
|
filter = sshd
|
|
logpath = /var/log/auth.log
|
|
maxretry = 3
|
|
findtime = 10m
|
|
```
|
|
|
|
**[Default]** - Settings in here apply to all jails
|
|
|
|
- **bantime** - how long the ban will last
|
|
- **ignoreip** - IP addresses to ignore
|
|
- **ignoreself** - stops from locking itself out
|
|
|
|
**[sshd]** - this is the jail for sshd
|
|
|
|
- **enabled** - decides weather the jail is active or not
|
|
- **port** - the port number the service
|
|
- **filter** - tells the jail which filter to use from /etc/fail2ban/filter.d
|
|
- **logpath** - where the log you want fail2ban to read is
|
|
- **maxretry** - Times the client can attempt a login before being banned
|
|
- **findtime** - An amount of time which maxretry must occur to trigger a ban
|
|
for example in 10 minutes if 3 login attempts fail ban the IP
|
|
|
|
**Restart** **_Fail2Ban_** to load the configuration
|
|
|
|
```bash
|
|
sudo systemctl restart fail2ban
|
|
```
|
|
|
|
## **Testing** **_Fail2Ban_**
|
|
|
|
To test Fail2Ban lets create a test user and set a password for the
|
|
user
|
|
|
|
```bash
|
|
sudo useradd test
|
|
sudo passwd test
|
|
```
|
|
|
|
**open the sshd config**
|
|
|
|
```bash
|
|
sudo vim /etc/ssh/sshd_config
|
|
```
|
|
|
|
Modify the line **PasswordAuthentication** to say **yes** and add the test user
|
|
to **AllowUsers**
|
|
|
|
```bash
|
|
PasswordAuthentication yes
|
|
AllowUsers user test
|
|
```
|
|
|
|
**restart the sshd service**
|
|
|
|
```bash
|
|
sudo systemctl restart sshd
|
|
```
|
|
|
|
Attempt to login to the remote host using the test account using an incorrect
|
|
password. After 3 failed attempts it should say connection refused.
|
|
|
|
```bash
|
|
ssh test@host -p 2222
|
|
```
|
|
|
|
_Fail2Ban_ should do something along the lines of ...
|
|
{{< video label="you shall not pass" webm="/webm/you-shall-not-pass.webm" >}}
|
|
|
|
log back in as your normal user, remove the test user and restore the sshd
|
|
config.
|
|
|
|
**To remove the user**
|
|
|
|
```bash
|
|
sudo userdel test
|
|
```
|
|
|
|
**restore the /etc/ssh/sshd_config file**
|
|
|
|
```bash
|
|
sudo vim /etc/ssh/sshd_config
|
|
```
|
|
|
|
Set **PasswordAuthentication** back to **no** and remove test from **AllowUsers**
|
|
|
|
```bash
|
|
PasswordAuthentication no
|
|
AllowUsers user
|
|
```
|
|
|
|
# **Conclusion**
|
|
|
|
well done, you managed to make it through all that !! At least now you can be
|
|
reassured that if them pesky brute-force attackers decide to target your machine
|
|
they can have a fun time in Jail 🤣 sorry bad joke.
|
|
|
|
By now you have managed to setup an ssh key for a client and copy it to
|
|
a remote host, re-configure the sshd config to lockdown authentication
|
|
and install and configure Fail2Ban to block pesky attackers.
|
|
|
|
{{< video label="proud" webm="/webm/proud.webm" >}}
|