Mastering SSH Key Generation and Management with Ansible

The orchestration of secure shell (SSH) access is a cornerstone of modern infrastructure management, serving as the primary mechanism for secure communication between management nodes and target endpoints. In the context of Ansible, the ability to automate the generation and distribution of these keys transforms a tedious manual process into a scalable, repeatable, and audit-able workflow. By leveraging the ansible.builtin.user module, administrators can transition from manually executing the ssh-keygen utility on individual servers to a declarative state where the entire fleet's identity management is governed by code. This capability is critical for establishing passwordless SSH, which is the prerequisite for most Ansible operations, as well as for the deployment of service accounts and the provisioning of secure developer workstations. The transition to an automated key lifecycle ensures that security standards—such as the move from RSA to Ed25519—are applied consistently across the entire environment without the risk of human error or forgotten manual steps.

Architectural Overview of the Ansible User Module for SSH

The ansible.builtin.user module is primarily designed for user account management, but it contains integrated functionality to handle the lifecycle of SSH key pairs. When the generatesshkey parameter is set to yes, Ansible triggers a process that mimics the behavior of the ssh-keygen tool but integrates it directly into the task execution flow.

The Mechanism of Key Generation

When a task is executed with generatesshkey: yes, Ansible performs a sequence of checks and actions to ensure the user's identity is established. If the specified key file does not already exist, Ansible generates a new pair consisting of a private key and a public key. If a key already exists, the module defaults to a non-destructive state, reporting "ok" and leaving the existing key untouched. This idempotent behavior prevents the accidental overwrite of existing keys, which would otherwise break existing trust relationships between servers.

Technical Specification of File Permissions

Security in SSH is predicated on strict file permissions. The user module does not simply create files; it enforces the precise Unix permissions required by the SSH daemon to prevent the system from rejecting the keys due to "unprotected private key file" errors.

  • The .ssh directory is created with 0700 permissions (drwx------), ensuring that only the owner can read, write, or enter the directory.
  • The private key file is written with 0600 permissions (-rw-------), restricting access exclusively to the user.
  • The public key file is written with 0644 permissions (-rw-r--r--), allowing the file to be readable by others while remaining writable only by the owner.

These permissions are not optional; they are a fundamental requirement of the SSH protocol to ensure that private keys remain confidential.

Advanced Configuration of SSH Key Parameters

While basic key generation is a simple toggle, the user module provides granular control over the cryptographic properties of the keys generated. This allows organizations to align their infrastructure with specific compliance standards or security mandates.

Cryptographic Algorithm Selection

The choice of algorithm dictates the security strength and performance of the SSH connection. While RSA has been the industry standard for decades, modern deployments are shifting toward Elliptic Curve cryptography.

Type Parameter Technical Notes Recommendation
RSA rsa Rivest-Shamir-Adleman; widely compatible across all legacy systems Use 4096 bits for modern security
Ed25519 ed25519 Edwards-curve Digital Signature Algorithm; faster and more secure Recommended for new deployments
ECDSA ecdsa Elliptic Curve Digital Signature Algorithm; provides high performance Good for smaller key sizes
DSA dsa Digital Signature Algorithm; deprecated and insecure Do not use

The Ed25519 algorithm is specifically highlighted as the modern standard. It is significantly faster than RSA and produces shorter keys that provide a higher level of security per bit. This reduction in key size leads to faster handshake processes and less overhead during the authentication phase of an SSH connection.

Key Size and Bit Depth

For algorithms where the key size can be specified, such as RSA and ECDSA, the sshkeybits parameter is used.

  • For RSA: A value of 4096 is recommended to ensure long-term security against brute-force attacks.
  • For ECDSA: Valid sizes include 256, 384, or 521 bits.
  • For Ed25519: This algorithm has a fixed size, meaning the sshkeybits parameter is irrelevant and ignored.

Identity and Metadata Management

The sshkeycomment parameter allows administrators to append a string to the public key. This is not merely for aesthetics; it is a critical administrative tool. By including the hostname (e.g., deploy@{{ inventoryhostname }}) in the comment, an administrator reviewing an authorizedkeys file on a central server can immediately identify which specific server or user the key belongs to. This prevents "key soup," where a file contains numerous public keys without any identifying labels, making it impossible to know which keys to revoke when a server is decommissioned.

Path and Passphrase Configuration

The sshkeyfile parameter defines the location of the key relative to the user's home directory. By default, for RSA, this is .ssh/idrsa. However, custom paths can be specified to organize keys for different roles (e.g., .ssh/ansiblekey).

Furthermore, the sshkeypassphrase parameter controls the encryption of the private key on disk. - Empty String: Resulting in a key with no passphrase. This is the requirement for service accounts and automated processes, as these entities cannot interactively provide a password during a task. - Defined Passphrase: Used for human users to add a layer of security. When a passphrase is used, it is recommended to utilize an ssh-agent to manage the keys in memory, avoiding the need to type the passphrase for every connection.

Implementation Workflows and Playbook Strategies

The practical application of SSH key generation in Ansible varies depending on whether the target is a human user, a service account, or a group of team members.

Basic Key Generation Workflow

In a basic scenario, a simple task can ensure a user has a key pair:

  • name: Create user with SSH key ansible.builtin.user: name: deploy generatesshkey: yes state: present

This declarative approach ensures that if the user "deploy" exists but lacks a key, one is created. If both exist, no action is taken.

Service Account Provisioning

Service accounts require specific configurations to ensure they operate with the least privilege and in the correct locations. For instance, a backup agent should be a system account with a dedicated home directory.

The following configuration demonstrates a hardened service account: - name: Create backup user with SSH key ansible.builtin.user: name: backupagent system: yes shell: /bin/bash home: /var/lib/backupagent createhome: yes generatesshkey: yes sshkeytype: ed25519 sshkeycomment: "backupagent@{{ inventoryhostname }}" state: present register: backupuser

By registering the result in the backupuser variable, the public key (contained in backupuser.sshpublickey) can be captured and stored in a local file for later distribution to other servers using the ansible.builtin.copy module.

Bulk Generation for Teams

When managing a team of developers, using a loop allows for the simultaneous creation of users with varying requirements. This is achieved by defining a list of team members and their desired key types:

  • name: Create users with SSH keys ansible.builtin.user: name: "{{ item.name }}" generatesshkey: yes sshkeytype: "{{ item.keytype }}" sshkeybits: "{{ item.keybits | default(omit) }}" sshkeycomment: "{{ item.name }}@{{ inventoryhostname }}" state: present loop: "{{ teammembers }}" register: team_keys

This method allows the flexibility to assign RSA 4096 to some users while assigning Ed25519 to others, all within a single task.

The SSH Key Lifecycle: Overwriting and Rotation

A critical aspect of the user module is the handling of existing keys and the potential for rotation.

The Danger of Forceful Regeneration

The user module includes a force parameter. When set to yes, Ansible will regenerate the SSH key even if one already exists. This should be used with extreme caution. Regenerating a key is a destructive action in terms of trust; any server that previously trusted the old public key will reject the new one. This effectively breaks all existing trust relationships and can lock an administrator out of a system if the new public key is not immediately and correctly distributed.

Key Rotation Strategies

Security best practices dictate that keys should be rotated periodically. While the user module makes generation easy, rotation requires a coordinated effort: 1. Generate a new key pair. 2. Distribute the new public key to all authorized hosts. 3. Verify connectivity with the new key. 4. Remove the old public key from the authorized_keys files.

Ansible can automate this by using the fetch module to gather public keys from various endpoints and the authorized_key module to deploy them across the infrastructure.

Manual Key Generation and Remote Installation

While the user module is the preferred method for automation, there are scenarios where keys must be generated manually or installed via the command line.

Manual Generation via ssh-keygen

The standard utility for key generation is ssh-keygen. To generate a key with a specific algorithm and path, the following command is used: ssh-keygen -f ~/.ssh/ansible -t ed25519

This command specifies the file path (-f) and the type (-t). Users are then prompted for a passphrase; leaving this empty allows for automated use, while providing one secures the key.

Distribution via ssh-copy-id

Once a key is generated manually, it must be installed on the remote server to enable passwordless access. The ssh-copy-id utility streamlines this process: ssh-copy-id -i ~/.ssh/ansible.pub [email protected]

This command takes the public key file and the connection string (username@IP) and automatically appends the public key to the remote user's authorized_keys file, ensuring the correct permissions are set on the remote end.

Automated Collection of Host Keys

In large-scale environments, managing the known_hosts file is a significant challenge. When connecting to a new server, SSH typically prompts the user to verify the host key fingerprint. To automate this and prevent interactive prompts during Ansible runs, host keys can be collected and stored centrally.

The ssh-keyscan Method

The ssh-keyscan utility can be used to retrieve the public host key of a remote server without needing to establish a full session. An Ansible playbook can automate this by scanning all hosts in the inventory.

The process involves: 1. Using the command module to run "ssh-keyscan {{ansiblehost|default(inventoryhostname)}}". 2. Registering the output of the scan for each host. 3. Using the file module to ensure the ~/.ssh/knownhosts file exists (state=touch). 4. Utilizing the blockinfile module to write all gathered host keys into the knownhosts file.

This ensures that the management node trusts all target devices, allowing for a seamless, non-interactive connection process. The use of the runonce: true parameter in these tasks prevents the playbook from redundantly writing to the knownhosts file for every single host in the inventory.

Summary Analysis of SSH Key Management Logic

The transition from manual key management to Ansible-driven orchestration represents a shift from imperative to declarative administration. In a manual environment, the administrator focuses on the action: "run ssh-keygen, then run ssh-copy-id." In the Ansible environment, the administrator focuses on the state: "the user deploy must have an Ed25519 key with a specific comment."

The technical superiority of the user module lies in its ability to manage the entire environment surrounding the key. By controlling the directory permissions (0700) and file permissions (0600/0644), Ansible eliminates the most common cause of SSH authentication failure. Furthermore, the integration of the register keyword allows the public key to be treated as a variable, enabling a dynamic pipeline where a key is generated on one host and immediately deployed to another.

The strategic move toward Ed25519, combined with the ability to loop user creation, allows for the rapid scaling of secure environments. Whether provisioning a single service account for backups or an entire engineering team's access, the consistency provided by the user module ensures that no security holes are left open due to inconsistent key types or overly permissive file settings.

Sources

  1. OneUptime: How to Generate SSH Keys for Users with the Ansible User Module
  2. Dev.to: Ansible Playbook and SSH Keys
  3. ipSpace: Collect SSH Keys Ansible

Related Posts