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.