Skip to content

Instantly share code, notes, and snippets.

@klevcsoo
Last active July 3, 2024 11:41
Show Gist options
  • Save klevcsoo/46d6c287bb70a6260f1df61886155fc4 to your computer and use it in GitHub Desktop.
Save klevcsoo/46d6c287bb70a6260f1df61886155fc4 to your computer and use it in GitHub Desktop.
Raspberry PI Secure SSH Setup with ZSH

Raspberry PI Secure SSH Setup with ZSH

In this guide, I'll walk you through setting up a secure SSH environment on a Raspberry Pi using ZSH. The goal is to enhance the security of your Raspberry Pi by configuring SSH, implementing two-factor authentication with Google Authenticator, changing the default SSH port, and setting up a firewall with UFW and Fail2Ban for added protection against brute-force attacks. Additionally, we'll customize your shell experience by installing and configuring ZSH along with Oh-My-ZSH.

Prerequisites

Raspberry PI Configuration Tool

sudo raspi-config
  • Enable console auto login
    • 1 System Options > S5 Boot / Auto Login > B2 Console Autologin

Update System Software

sudo apt update
time sudo apt full-upgrade -y

I like to use time here to monitor the time it took to install the updates, but it's optional.

Install Git and ZSH

sudo apt install -y git zsh

Configure ZSH and Oh-My-ZSH

Set ZSH as default shell for user.

chsh -s /usr/bin/zsh

Then reboot with sudo reboot. After that, install Oh-My-ZSH with the following command.

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

Navigate to the Oh-My-ZSH config directory and install zsh-syntax-highlighting and zsh-autosuggestions.

cd ~/.oh-my-zsh/custom/plugins
git clone https://github.com/zsh-users/zsh-syntax-highlighting
git clone https://github.com/zsh-users/zsh-autosuggestions

Now customise the .zshrc file. This step comes down to personal preference, but a few options are necessary

  • Set the path to the Oh-My-ZSH installation
    • export ZSH="$HOME/.oh-my-zsh"
  • Activate the downloaded plugins
    • plugins=(git zsh-syntax-highlighting zsh-autosuggestions)
  • Source Oh-My-ZSH
    • source $ZSH/oh-my-zsh.sh
  • Set the locale to avoid annoying errors when running commands
    • export LC_ALL=C

Configure SSH and security

First, on remote machine (the initial SSH client), generate an RSA key pair if you haven't one already, using ssh-keygen -t rsa -b 4096. Then, run the following command to copy the public key to the Raspberry PI (the SSH server). If password authentication is disabled later, this part is critical.

ssh-copy-id -i ~/.ssh/id_rsa.pub <raspberry pi IP or hostname>

Disable password authentication (optional)

If you only want to allow a specific set of machines to be able to remotely access your Raspberry PI, disable password authentication after you've copied the public key. Technically, this eliminates the need for Google Authenticator, but I recommend doing every step in the guide, so that if you decide to allow password authentication in the future, it's already well-protected.

Open the SSH configuration file using Nano (or some other editor).

sudo nano /etc/ssh/sshd_config

Here, search for the PasswordAuthentication option, uncomment it (if it isn't already) and set the value to no.

Using Nano, you can save the file with Ctrl+S and exit with Ctrl+X.

Set up Google Authenticator

For this step, you'll need the Google Authenticator or Microsoft Authenticator on your phone. If you have an iPhone, you can use iCloud Passwords in the Settings (Passwords app starting from iOS 18) app instead.

Install the required PAM module.

sudo apt install -y libpam-google-authenticator

Run the Google Authenticator setup, and follow the prompts.

google-authenticator

Follow the instructions. For the prompts, you can go with other options, but here are the ones that I recommend:

  1. Do you want authentication tokens to be time-based? yes
  2. Scan the QR code with the two-factor authentication app of your choice, and be sure to save the backup codes.
  3. Do you want me to update your "/home/pi/.google_authenticator" file? yes
  4. Do you want to disallow multiple uses of the same authentication token? ... yes
  5. By default, a new token is generated every 30 seconds by the mobile app. In order to ... Do you want to do so? no
  6. If the computer that you are logging into isn't hardened against brute-force login attempts, you can enable rate-limiting ... Do you want to enable rate limiting? yes

Now, configure SSH to use Google Authenticator. Open the PAM config file for SSH.

sudo nano /etc/pam.d/sshd

Add the following lines at the top:

# Authentication via Google Authenticator.
auth required pam_google_authenticator.so

Enable Challenge-Response Authentication

Open the SSH configuration file.

sudo nano /etc/ssh/sshd_config

Look for the KbdInteractiveAuthentication line, make sure it's not commented and set the value to yes.

Change the default SSH port

Changing the SSH port from the default (22) can reduce many automated attacks. It's not a significant security measure, but I do recommend setting it to something in the higher range (above 1024, but ideally even higher). Usually, I use 54321. Try to avoid ports such as 2222 or 22222.

Open the SSH configuration file again.

sudo nano /etc/ssh/sshd_config

Look for the Port option and set the value to your chosen port number.

Set up a firewall

I like to use UFW (Uncomplicated Firewall) for the firewall, you can install it with the command below.

sudo apt install -y ufw

You also need to allow trafic through the SSH port and enable the firewall itself. Because I've set the port to 54321 I'll use that, but allow the one you've set in the SSH config file.

sudo ufw allow 54321/tcp
sudo ufw enable

Extra layer of security with Fail2Ban (optional)

I also like to add another layer of security with Fail2Ban. This tool temporarily disables SSH access after a specified number of failed attempts (to further prevent brute-force attacks). You can also blacklist or whitelist specific IP adresses.

First, install the package.

sudo apt install -y fail2ban

Copy the default config file to a local one, to override the settings, then open it for editing.

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Look for the [sshd] section, and change the settings. Not the commented-out example section at the top, but one towards the end of the file.

[sshd]
mode     = normal
enabled  = true
port     = 54321
logpath  = /var/log/auth.log
backend  = systemd
maxretry = 3
bantime  = 900
  • mode: I like to set this option to normal, but feel free to use ddos, extra or aggressive
  • enabled: set this one to true
  • port: use the port that you've used for the SSH
  • logpath: the file, where the SSH attempts are logged. I usually use /var/logs/auth.log, because this is what ChatGPT recommends
  • maxretry: this is the number of allowed failed attempts before the IP address is banned
  • bantime: the duration that the a banned IP is banned for in seconds (in the example above, it's 15 minutes)

Start and enable the Fail2Ban service using systemctl.

sudo systemctl start fail2ban
sudo systemctl enable fail2ban

You can verify that it works by requesting the service status.

sudo systemctl status fail2ban

You should see active (running) in the third line. If you'd like to check the status, you can using this command:

sudo fail2ban-client status sshd

Whitelisting your IP

If you have a static IP address you should whitelist it in the /etc/fail2ban/jail.local file. To do this, open the file and add the IP to the end of the ignoreip option (separated from the over ones by a space).

Verifying and applying the changes

Before applying the changes, make sure that you've copied the public key of your client machine to the Raspberry PI in order to avoid locking yourself out. Don't worry, you can reenable password authentication from the Raspberry PI locally (see above) to copy the key if there's something wrong, just be sure to disable it after.

If everythong's in order, restart the SSH service. I also recommend restarting the PI, just to be sure.

sudo systemctl restart ssh
sudo reboot

Now, try to access the Raspberry PI using SSH, type yes if you're asked to confirm the connection.

If you did not disable password authentication, you should be prompted to enter the code from the authenticatior app on your phone, and then the password.

If you did disabled it, you should not be prompted for anything, but see the remote shell right away.

Combining the public key and 2FA

You can also make it mandatory to enter the 2FA code and the password when using public key authentication. This is another layer of security, because even if the client's public key is in ~/.ssh/authorized_keys, the server will still ask for the 2FA code and the password to open a shell. If the public key is not amongst the authorized ones, the behaviour is unchanged and the connection will be refused.

To do this, open the SSH config file.

sudo nano /etc/ssh/sshd_config

Add the following line: AuthenticationMethods publickey,keyboard-interactive. Then restart the SSH service.

sudo systemctl restart ssh

Now, even when logging in from an authorized client, the server should still ask for the 2FA code and password.

Disclaimer

While these measures provide significant protection against the majority of attacks, no security system is 100% bulletproof. Opening up your Raspberry Pi to the internet, especially if it's on your home network, carries inherent risks. Please consider these risks carefully and implement additional security practices as needed to protect your devices and data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment