Combining 2FA and Public Key Authentication for a better Linux SSH security


7 min read

Combining 2FA and Public Key Authentication for a better Linux SSH security

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 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. Thanks some commonly used tools you can achieve all the burden above with the simple commands below:

# replace my_server_name here eventually
# when prompted with passphrase, enter it with keyboard
ssh-keygen -t ed25519 -f ~/.ssh/my_server_name
# replace my_server_name here eventually
ssh-copy-id -i ~/.ssh/ user@remote_host

If ssh-copy-id is not an option, you still can manually copy the public key to the remote server. First display and copy your public key: (should be available after the generation steps above)

cat ~/.ssh/

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

nano ~/.ssh/authorized_keys

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

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 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.

Host my_server_name
    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 or Authy, Desktop applications like Keepass TOTP or even more advanced tools like YubiKey

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.

sudo apt install libpam-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:

Here, 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:

sudo nano /etc/ssh/sshd_config
# 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:

sudo nano /etc/pam.d/sshd

First, find the following three lines and comment them out

#@include common-auth
# account  required
#@include common-password

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

# two-factor authentication via Google Authenticator
auth required

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

 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:
 ssh my_server_name
(my_username@ 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
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 :)