How to Regenerate SSH Host Keys on Remote Servers Using Ansible: A Complete Guide


1 views

When managing multiple servers, regenerating SSH host keys is a common security practice, especially when deploying golden images or dealing with potential key compromises. The standard approach using ssh-keygen can be tricky to automate across different Linux distributions.

The initial approach using echo -e 'y\n' piping has several issues:

1. The command output shows the pipe character being interpreted literally
2. No actual key generation occurs despite the "changed" status
3. Permission issues might prevent writing to /etc/ssh/

Here's an improved playbook that properly handles key generation:

---
- hosts: all
  become: true
  gather_facts: false

  tasks:
    - name: Ensure SSH directory exists
      file:
        path: /etc/ssh
        state: directory
        mode: 0755

    - name: Generate RSA host key
      command: ssh-keygen -q -t rsa -f /etc/ssh/ssh_host_rsa_key -C "" -N ""
      args:
        creates: /etc/ssh/ssh_host_rsa_key
      register: rsa_keygen
      changed_when: "'already exists' not in rsa_keygen.stderr"

    - name: Generate ECDSA host key
      command: ssh-keygen -q -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -C "" -N ""
      args:
        creates: /etc/ssh/ssh_host_ecdsa_key
      register: ecdsa_keygen
      changed_when: "'already exists' not in ecdsa_keygen.stderr"

    - name: Generate ED25519 host key (modern systems)
      command: ssh-keygen -q -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -C "" -N ""
      args:
        creates: /etc/ssh/ssh_host_ed25519_key
      register: ed25519_keygen
      changed_when: "'already exists' not in ed25519_keygen.stderr"
      ignore_errors: yes

The improved solution includes several critical enhancements:

  • Explicit directory permission management
  • Proper idempotency checks using creates parameter
  • Accurate change detection via changed_when
  • Support for modern key types (ED25519)
  • Graceful handling of unsupported key types

For legacy systems like Ubuntu 14.04, additional considerations:

- name: Generate DSA host key (legacy systems)
  command: ssh-keygen -q -t dsa -f /etc/ssh/ssh_host_dsa_key -C "" -N ""
  args:
    creates: /etc/ssh/ssh_host_dsa_key
  when: ansible_distribution_version == "14.04"
  register: dsa_keygen
  changed_when: "'already exists' not in dsa_keygen.stderr"

Always verify and restart the SSH service:

- name: Verify key fingerprints
  command: ssh-keygen -lf /etc/ssh/ssh_host_rsa_key
  register: key_fingerprint
  changed_when: false

- debug:
    var: key_fingerprint.stdout

- name: Restart sshd service
  service:
    name: ssh
    state: restarted

Consider these additional security measures:

  • Set strict file permissions (600 for private keys)
  • Rotate keys periodically using this automation
  • Update known_hosts files across your infrastructure
  • Consider using Ansible vault for key distribution

When managing multiple Linux servers, there comes a time when you need to regenerate SSH host keys across your infrastructure. While the manual process using ssh-keygen is straightforward, automating this across dozens or hundreds of servers presents unique challenges.

The example playbook shows several issues that commonly occur:

- The echo -e 'y\n' pipe doesn't work as expected in Ansible
- The command module output shows the pipe character being interpreted literally
- No verification step exists to confirm key generation
- No handling for existing key files

Here's a more robust solution that properly handles key generation:

---
- hosts: all
  become: true
  gather_facts: false

  tasks:
    - name: Remove existing host keys
      file:
        path: "/etc/ssh/ssh_host_{{ item }}_key"
        state: absent
      with_items:
        - rsa
        - dsa
        - ecdsa
        - ed25519
      ignore_errors: yes

    - name: Generate new SSH host keys
      command: ssh-keygen -q -t {{ item }} -f /etc/ssh/ssh_host_{{ item }}_key -C "" -N ""
      with_items:
        - rsa
        - dsa
        - ecdsa
        - ed25519
      when: ansible_ssh_host in groups['all']

    - name: Verify key generation
      stat:
        path: "/etc/ssh/ssh_host_{{ item }}_key"
      register: key_files
      with_items:
        - rsa
        - dsa
        - ecdsa
        - ed25519

    - name: Restart sshd service
      service:
        name: sshd
        state: restarted
      when: key_files.results | selectattr('stat.exists') | list | length > 0
  • Explicit removal of existing keys prevents conflicts
  • Proper looping through key types
  • Verification step to confirm key creation
  • Conditional service restart only when needed
  • Support for modern ed25519 keys

For Ubuntu 14.04 systems specifically, you might need to add:

    - name: Ensure ssh directory exists (Ubuntu 14.04)
      file:
        path: /etc/ssh
        state: directory
        mode: 0755
      when: ansible_distribution == 'Ubuntu' and ansible_distribution_version == '14.04'

After running the playbook, verify the keys exist and have proper permissions:

ls -l /etc/ssh/ssh_host_*

Expected output should show the new keys with root ownership and 600 permissions.