Combining 2FA and Public Key Authentication for a better Linux SSH security
Tags: security, ssh, devops, sre
Published: 2023-12-06 10:36:47


## What are we trying to improve?

SSH (Secure Shell Connection) is a secure way to login to a Linux server and remotely work with it. However there might be a good margin of further improvements to secure even more this.

1- **Substitute password authentication with public key authentication**: 
* **Security**: [Public key authentication](https://en.wikipedia.org/wiki/Public-key_cryptography) is more secure than passwords. Passwords can be brute-forced or guessed, while a cryptographic key pair (public and private keys) is practically impossible to crack with current technology. 
* **No Replay Attacks**: Since the private key is never transmitted over the network, there's no risk of interception and replay attacks, unlike passwords. 
* **Automation**: Public keys are great for automated processes. You can use SSH without entering a password each time, which is essential for scripts and system administration tasks. 
* **Management**: It's easier to manage and revoke access with public keys. Instead of changing a password (which might be shared or used on multiple systems), you just remove the public key from the server. 
* **Phishing Resistant**: Users are less susceptible to phishing attacks since they're not entering a password that could be captured.

2- **Disable root login from the SSH configuration**: 
* **Mitigate Brute Force Attacks**: Root is a known username and often targeted by brute force attacks. Disabling root login means attackers can't directly access the most privileged account. 
* **Limit Privilege Escalation**: Even if an attacker compromises a regular user account, they still need to find a way to escalate privileges to root, adding an extra layer of security. 
* **Audit Trails**: Using `sudo` from a regular account to perform administrative tasks leaves a trail, helping in auditing and monitoring activities. 
* **Reduced Risk of Accidental Damage**: Preventing direct root access reduces the risk of accidental high-impact changes by system administrators. 
* **Compliance**: Many security standards and best practices advise against using root accounts for routine administration.

3- **Force 2FA authentication with TOTP**: Adding Time-based One-Time Password (TOTP) as an additional layer to SSH authentication significantly boosts security: 
* **Two-Factor Authentication:** TOTP introduces a second factor of authentication. Even if an attacker steals a user's SSH key or password, they still need the TOTP, which is typically generated on a device the user possesses. 
* **Dynamic Codes**: TOTPs change every 30-60 seconds. This makes stolen codes useless after a very short time, countering replay attacks. 
* **Mitigates Phishing:** TOTPs are harder to phish than static passwords because they are constantly changing and are only valid for a short period. 
* **No Network Dependency:** TOTPs are generated by an algorithm based on time and a secret key, so they don't require network connectivity, making them reliable and efficient. 
* **Harder to Intercept:** Unlike static passwords, intercepting a TOTP doesn't compromise future logins, as each password is valid for only one login session.

4- **Change SSH server port from default 22 to a less common port number** 
* **Reduce Automated Attacks**: Many bots and scripts target port 22 for SSH attacks. Changing the port can reduce the volume of these automated attacks. 
* **Lower Profile**: By not using the default port, your server becomes less obvious to casual scanners who are looking for easy targets. 
* **Filter Out Noise**: Changing the port can reduce log noise from repeated login attempts, making it easier to spot genuine security threats.

## Configuring your local machine

1- **Setup Public Key Authentication**: 
* **Generate Key Pair:** 
* To make the public key authentication work, we need to generate the called "key-pair": the public key which you share with the remote server, and the private key which must be kept **private** and secure on your local machine. 
* For enhancing even more security, you can add a passphrase to your private key so that is won't be possible to use it until the passphrase is entered. 
* Fortunately some commonly used tools you can achieve all the burden above with the simple commands below:

```bash
# replace my_server_name here eventually
# when prompted with passphrase, enter it with keyboard
ssh-keygen -t ed25519 -f ~/.ssh/my_server_name
```

```bash
# replace my_server_name here eventually
ssh-copy-id -i ~/.ssh/my_server_name.pub user@remote_host
```

* If `ssh-copy-id` is not an option, you still can manually copy the public key to the remote server. Follow the steos below **only if** `ssh-copy-id` doesn't work for you: 
* First display and copy your public key: (should be available after the generation steps above)

```bash
cat ~/.ssh/my_server_name.pub
```

* Then open the `authorized_keys` file on the remote server and paste the copied key to its end.

```bash
nano ~/.ssh/authorized_keys
```

* Now an important step, do not forget to apply necessary permissions

```bash
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
```

2- **Add SSH configuration (optional)**: 
* This will improve the UX while logging into your remote server making it more convenient. 
* For this we suppose that your remote host IP is `192.168.12.34` and we will suppose the SSH port has been set on the remote server to, for example, `4898`. 
* Based on these parameters, you should add this section to your `~/.ssh/config` file.

```bash
#~/.ssh/config
Host my_server_name
    HostName 192.168.12.34
    User my_username
    IdentityFile /home/my_username/.ssh/my_server_name
    Port 4898
```

3- **Have a TOTP client**: 
* You will need this to be able to generate the TOTP number once prompted from the remote server. 
* You have couple options here: Mobile apps like [Google Authenticator](play.google.com/store/apps/details?id=com.google.android.apps.authenticator2) or [Authy](https://authy.com/download/), Desktop applications like [Keepass TOTP](https://www.linux.org/threads/in-depth-tutorial-how-to-set-up-2fa-totp-with-keepassxc-aegis-and-authy.36577/) or even more advanced tools like [YubiKey](https://support.yubico.com/hc/en-us/articles/360013789259-Using-Your-YubiKey-with-Authenticator-Codes)

## Configuring your remote server

* Instructions here are tested on Debian based Linux distributions, but should work on other distros as well without "big" differences. 

1- **Install google authenticator**: 
* Here you run the authenticator installation then you execute it, and we will answer "y" for all the prompts.

* **Installation:**

    ```bash
    sudo apt install libpam-google-authenticator
    ...
    ...
    ...

    ```

* **Execution:**

    ```bash
    google-authenticator
    Do you want authentication tokens to be time-based (y/n) y
    █████████████████████████
    ██ ▄▄▄▄▄ █▀▄█▄█ █ ▄▄▄▄▄ ██
    ██ █   █ █▀▄▀█▄█ █   █ ██
    ██ █▄▄▄█ █ ▀ ▀ ▄ █▄▄▄█ ██
    ██▄▄▄▄▄▄▄█▄█ ▀ █▄▄▄▄▄▄▄██
    ██▄▀▄▀▄█ ▀▄█▀▄█▄█▀█▀██▀██
    ██▄█▄██▄▀▄▄▀█▄▀ ▀▄█▄▀▀▄██
    ██▄▄▄▄▄▄▄█▄▄█▄▄███▄█▄▄▄███
    █████████████████████████
    
    Your new secret key is: 4D3FPZ2W6Y7IOMV 
    Your verification code is 123456
    Your emergency scratch codes are:
    81234567
    89123456
    78123456
    69123456
    58123456

    ```
    
* Pay attention to the generated codes: Use The ASCII made QR code (yay, terminal!) to add the TOTP to your smartphone app by adding new entry and scanning the picture, and use the secret key to add a TOTP setup to your keepass application and/or YubiKey. The backup codes should be saved for eventual emergency cases.

2- **Configure SSH server**: 
* Here we will act on two configs: the `SSHD` and the `PAM`. As we explained in the first section, we will edit the SSH daemon config file to disable password authentication and root user, keep interactive keyboard for `TOTP` prompting, enable the Public Key authentication and change the SSH port number. 
* The resulting config file `/etc/ssh/sshd_config` should look like this:

```bash
sudo nano /etc/ssh/sshd_config
```

```bash
# This one we changed from default Port 22
Port 4898

# Add this entry or edit it if it is already existing
PermitRootLogin no

# We can explicitly set this although it should be the default value even if omitted. 
PubkeyAuthentication yes

# This setting will disable the password based authentication. 
PasswordAuthentication no

# This entry will allow both publickey authentication and keyboard-interactive which # we need to enter our TOTP code
AuthenticationMethods publickey,keyboard-interactive

# PAM (Pluggable Authentication Modules) is also required to allow google 
# authenticator #integration
UsePAM yes

# Depending on your Debian version, if you find the entry 
# KbdInteractiveAuthentication existing in the config file, then use it, otherwise you 
# might find the deprecated entry which is ChallengeResponseAuthentication which
# in that case should be set to yes instead.
KbdInteractiveAuthentication yes
```

3- **Configure PAM**: for this we need to enter some changes to the `/etc/pam.d/sshd` file:

```bash
sudo nano /etc/pam.d/sshd
```

* First, find the following three lines and comment them out

```bash
#@include common-auth
# account  required     pam_access.so
#@include common-password
```

* Then add the following line to the end of the file:

```bash
# two-factor authentication via Google Authenticator
auth required pam_google_authenticator.so
```

* Now we only have one last step to go: reststart the SSH server

```plaintext
 sudo systemctl restart sshd
```

**Server setup should be complete ;)**

## SSH like a boss

* Given all the steps above correctly achieved, you should by now be able to ssh to your remote server as easy as the following:
    

```bash
 ssh my_server_name
(my_username@192.168.12.34) Verification code: 
Linux raspberrypi 6.1.0-rpi6-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.58-1+rpt2 (2023-10-27) aarch64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Dec  6 01:16:51 2023 from 192.168.12.33
my_username@my_server_name:~ $
```

* You will be of course prompted for the TOTP code (4 digits) which you should use your smartphone, keepass or YubiKey to get them. 

So that was pretty match it. I will be happy to support for any further questions, rectifications or simple likes :)