Hardening SSH With Ansible

Table of Contents

I’ve been using root login for Ansible for the first part of this project, in part because this is not a production environment, and in part to avoid too many confounding factors while learning Ansible.

But it started to bug me. Check the relevant git commit here

Overview

This is a straightfoward task, but I broke it down into steps:

  • Add user/key, enable sudo on CentOS machines
  • Add user/key, enable sudo on Debian machines
  • Log in as user, remove root and password login

CentOS

Here is the first play. I exclude the Proxmox hosts and my laptop.

---
- name: Disable root login over SSH
  hosts: all:!tiny:!proxmox
  gather_facts: false
  user: root

  vars:
    user: nc

  tasks:
  - name: import secrets
    include_vars:
      file: "~/0/vault/secrets.yml"

  - name: Add user
    user:
      name: "{{ user }}"
      password: "{{ nc_password_hash }}"
      groups: wheel

  - name: Add authorized key
    authorized_key:
      user: "{{ user }}"
      key: "{{ id_ed25519_pub }}"

  - name: Allow 'wheel' group to have passwordless sudo
    lineinfile:
      dest: /etc/sudoers
      state: present
      regexp: '^%wheel'
      line: '%wheel ALL=(ALL) NOPASSWD: ALL'
      validate: 'visudo -cf %s'

Debian

This is for the Proxmox hosts, which don’t have sudo installed by default, and which have a sudo group instead of a wheel group.

- name: Debian-specific tasks
  hosts: proxmox
  user: root
  gather_facts: false
  tags: proxmox

  vars:
    user: nc

  tasks:
  - name: import secrets
    include_vars:
      file: "~/0/vault/secrets.yml"

  - name: install sudo
    apt:
      name: sudo
      state: present

  - name: Add user
    user:
      name: "{{ user }}"
      password: "{{ nc_password_hash }}"
      groups: sudo

  - name: Add authorized key
    authorized_key:
      user: "{{ user }}"
      key: "{{ id_ed25519_pub }}"

  - name: Allow 'sudo' group to have passwordless sudo
    lineinfile:
      dest: /etc/sudoers
      state: present
      regexp: '^%sudo'
      line: '%sudo ALL=(ALL) NOPASSWD: ALL'
      validate: 'visudo -cf %s'

Disable root

Now that I’ve enabled everything, I log in as the new user for the final, destructive change, to make 100% sure I don’t lock myself out.

- name: Log in as new user to disable root
  hosts: all:!tiny
  user: "{{ user }}"
  gather_facts: false
  become: yes

  tasks:
  - name: Disable root login over SSH
    lineinfile: dest=/etc/ssh/sshd_config regexp="^PermitRootLogin" line="PermitRootLogin no" state=present
    notify:
      - restart sshd

  - name: Disable password login
    lineinfile: dest=/etc/ssh/sshd_config regexp="^PasswordAuthentication" line="PasswordAuthentication no" state=present
    notify:
      - restart sshd

  handlers:
  - name: restart sshd
    service:
      name: sshd
      state: restarted

ansible.cfg

To streamline the rest of this process, I add remote_user=nc to my ansible config. I still have to update my playbooks with a become: true, but I can do that as I go.

Conclusion

I want to adapt this into a server-init role so I can apply it to all my new servers, but for now, it does its job.

Check the relevant git commit here