Ansible

Hardening SSH with Ansible

Introduction

Ansible

This blog post is going to be about hardening your SSH config with Ansible. This guide will be build upon my earlier post about Updating your Homelab with Ansible and Configuring Linux users and SSH keys with Ansible. This post is mainly focused on Ubuntu/Debian systems. For other distro’s the path to the sshd config might be different.

The playbook

With every Ansible configuration we start with the playbook. All these playbooks can be found on my GitHub aswell.

---
 - hosts: ubuntu
   gather_facts: yes
   become: true
   become_method: su
   become_user: root
   tasks:
   - name: Hardening sshd
     block:
       - name: Editing sshd config
         lineinfile:
           dest: "/etc/ssh/sshd_config"
           regexp: "{{ item.regexp | default(omit) }}"
           line: "{{ item.line }}"
           state: "{{ item.state | default('present') }}"
           validate: "sshd -t -f %s"
         with_items:
           - line: "Protocol 2"
           - line: "Protocol 1"
             state: "absent"
           - line: "RSAAuthentication yes"
             state: "absent"
           - regexp: "^Port\ "
             line: "Port {{ ssh_port }}"
           - regexp: "^PermitRootLogin\ "
             line: "PermitRootLogin no"
           - regexp: "^PasswordAuthentication\ "
             line: "PasswordAuthentication no"
           - regexp: "^PermitEmptyPasswords\ "
             line: "PermitEmptyPasswords no"
           - regexp: "^StrictModes\ "
             line: "StrictModes yes"
           - regexp: "^IgnoreRhosts\ "
             line: "IgnoreRhosts yes"
           - regexp: "^RhostsAuthentication\ "
             line: "RhostsAuthentication no"
           - regexp: "^RhostsRSAAuthentication\ "
             line: "RhostsRSAAuthentication no"
           - regexp: "^ClientAliveInterval\ "
             line: "ClientAliveInterval 300"
           - regexp: "^ClientAliveCountMax\ "
             line: "ClientAliveCountMax 0"
           - regexp: "^AllowTcpForwarding\ "
             line: "AllowTcpForwarding no"
           - regexp: "^X11Forwarding\ "
             line: "X11Forwarding no"
           - regexp: "^KexAlgorithms\ "
             line: "KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256"
           - regexp: "^Ciphers\ "
             line: "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr"
           - regexp: "^MACs\ "
             line: "MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com"

       - name: Reload sshd
         service:
           name: sshd
           state: reloaded

This playbook uses the lineinfile module with Ansible variables loaded in from the with_items. It parses through every line in the with_items section and changes the value in the sshd_config according the regular expression. The last step reloads the sshd daemon to finalize the settings.

The settings used are:

  • Change protocol version to 2 to allow better cipher suites.
  • Removes RSAAuthentication which is deprecated.
  • Changes the SSH port to the set variable.
  • Sets PermitRootLogin to no. This disables direct root login via SSH.
  • Sets PasswordAuthentication to no. This only allows key based logins.
  • Sets PermitEmptyPasswords to no. This disables to use of empty passwords.
  • Enables Strictmode. This enables checks on the daemon before starting up SSH. For example file permissions. If the settings are wrong. The SSH services does not start.
  • Disables multiple RHosts ettings. This disables the use of .rhosts file to connect.
  • Sets ClientAlive settings. Drops idle connections after set time.
  • Sets AllowTcpForwarding to no. This disables the use of tcp forwarding via SSH.
  • Sets X11Forwarding to no. This disables to use of X11 forwarding via SSH.
  • Sets optimal cipher suites to use for the connection.

Conclusion

Using this playbook with Ansible makes sure all your servers are using hardened SSH config. Make sure to use it in combination with SSH keys. More info how to deploy the SSH keys with Ansible can be found in my Configuring Linux users and SSH keys with Ansible blogpost.

Feel free to leave a comment.

4 comments

  1. This playbook doesn’t appear to be idempotent and can’t be run on the same system again

    1. As this script is altering the content of the SSH configuration file you can’t rerun the same script as it won’t find the required regex patterns.

Leave a Reply

Your email address will not be published. Required fields are marked *