SSH security

Published

Over the years I'd heard many people share their best practices regarding securing SSH access and have been asked by friends and colleagues how to secure their servers. So here are my opinions and practices regarding SSH security. The main point I try to get across is balance. Balance between security and functionality.

Practices I avoid

First, changing the listening port. The upside is that a high random port is scanned less often and the various script kiddies sometimes fails to notice it, thus reducing the noise in the logs. This however is no real security measure as any capable attacker will quickly spot the daemon listening on a different and all benefits will be lost. The downside is that by not using the default port you need to configure all clients accordingly. So, no substantial wins and minor loss. I pass on this idea.

The second most common is allowing access only from the office IP or a few select IP addresses. The security benefit is high but the risk is also high. I view SSH access to servers as critical and limiting access puts you in risk of locking yourself out in case of trouble/ emergency. Not having management access to your servers is more dangerous to your business than allowing whomever to try (not succeed) to access your servers. Therefore I prefer to limit the access in different ways.

Practices I employ

Most importantly, allow only key-based authentication. This works without relying on 3rd party services and is extremely secure when done properly. The most important issue keeping your private key private. Personally I keep my home directory on a separate partition that is encrypted with LUKS so the keys are encrypted when in rest. If you don't have encrypted storage for your keys, password encrypt the keys (consult the ssh-keygen manual for instructions).

Another measure of security is limiting the number of authentication attempts any single IP can perform in a given time. This is achieved by 2 actions. By ensuring that MaxAuthTries in your sshd config is not set too high (the default is 6 which is damn reasonable) and by limiting the number of TCP connections to port 22 any IP can initiate. With UFW on Linux this is done by running ufw limit ssh and for OpenBSD or FreeBSD I'd refer you to Peter Hansteen's great PF tutorial.

These 2 steps will create a barrier to entry that no brute-force attack will be able to overcome in anyone's lifetime. This is what I do on all of my servers and it has served me well. However, although the commonly used OpenSSH is extensively used, researched and tested, bugs still happen. Keeping current with updates is vital.

Also, some other good practices are disabling root login, SSH protocol version 1 is deprecated, insecure and must be turned off (is off by default, but I felt it's worth mentioning). To further simplify things, here is a short Ansible playbook that covers the actions mentioned above for Debian based systems.

---
- hosts: all
  handlers:
  - name: Restart SSH
    service:
      name: ssh
      state: restarted

  tasks:
  - name: APT install and update
    with_items:
    - openssh-server
    - ufw
    apt:
      name: '{{ item }}'
      state: latest
      update_cache: yes
      cache_valid_time: 3600

  - name: Configure SSHd
    with_dict:
      MaxAuthTries: 10
      PasswordAuthentication: no
      PermitRootLogin: no
      Protocol: 2
    lineinfile:
      dest: /etc/ssh/sshd_config
      regexp: '{{ item.key }}'
      line: '{{ item.key }} {{ item.value }}'
      state: present
    notify:
    - Restart SSH

  - name: Enable UFW
    ufw:
      state: enabled

  - name: Rate limit SSH
    ufw:
      rule: limit
      port: ssh
      protocol: tcp