If you are not familiar with modules, check out Ansible - Getting Started with Modules.
authorized_key can be used to append or remove SSH public certificates from an authorized_keys file.openssh_keypair is in the ansible.posix collection. You may need to install the ansible.posix collection. Check out my article Install a collection using the ansible-galaxy collection install command
--ask-pass flag
When using the ansible-playbook command, assuming the SSH servie on the target server is configured to accept password authentication and the authorized_keys file on the target server does not already contain the users SSH key, you will have to include the --ask-pass flag to connect to the target server. After the users SSH key has been appended to the authorized_keys file on the target server, if the SSH service on the target server is configured to accept publickey authentication, you should then no longer have to use the --ask-pass flag.
Append a certificate
Let's say /home/john.doe/.ssh/id_rsa.pub on localhost (that's the Ansible server) contains the following.
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqlNpcovu736RD0kiDno7+VB7rTS0jWJrbFEazgCKCYPtVb9bJ9A0tX42wQqsuzSqJ/+fkRJZcDapwZffuMEPZ4lLTS6vgyHI2o8NJk/lXggizOsHJFZyd7sGnnk/ynso86Rl4XA/dKkvwBS3ROrD3r8O1sPEoRKEgt5vwlwYMslSsL1qa0lL/20TuWx78GKEX0b9PI7ZRRMvC5wWS5CRwmvaL8U6N/FUcJYFPcNIy0dIh2mfhUwqnqfxEqYsddN1id8fUm46SwsGgWOTjMKA/OHi5y7gHvQ5wnNlk4c+/55cytwm9wBW5taDRc0ndPOPmWb/YMtMHY7obA+wV8/57
When the user parameter is used, the public certificate will be appended to /home/username/.ssh/authorized_keys on the target server. In this example, the public certificate will be appended to /home/john.doe/.ssh/authorized_keys.
---
- hosts: all
tasks:
- name: Append /home/john.doe/.ssh/id_rsa.pub on localhost to /home/john.doe/.ssh/authorized_keys on the target server
ansible.posix.authorized_key:
user: john.doe
key: "ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQCqlNpcovu736RD0kiDno7+VB7rTS0jWJrbFEazgCKCYPtVb9bJ9A0tX42wQqsuzSqJ/+fkRJZcDapwZffuMEPZ4lLTS6vgyHI2o8NJk/lXggizOsHJFZyd7sGnnk/ynso86Rl4XA/dKkvwBS3ROrD3r8O1sPEoRKEgt5vwlwYMslSsL1qa0lL/20TuWx78GKEX0b9PI7ZRRMvC5wWS5CRwmvaL8U6N/FUcJYFPcNIy0dIh2mfhUwqnqfxEqYsddN1id8fUm46SwsGgWOTjMKA/OHi5y7gHvQ5wnNlk4c+/55cytwm9wBW5taDRc0ndPOPmWb/YMtMHY7obA+wV8/57"
...
When the path parameter is used, the public certificate will be appended to whatever file is defined in path. In this example, the public certificate will be appended to /etc/ssh/authorized_keys.
- name: Append /home/john.doe/.ssh/id_rsa.pub on the control node to /etc/ssh/authorized_keys on the managed node
ansible.posix.authorized_key:
path: /etc/ssh/authorized_keys
key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqlNpcovu736RD0kiDno7+VB7rTS0jWJrbFEazgCKCYPtVb9bJ9A0tX42wQqsuzSqJ/+fkRJZcDapwZffuMEPZ4lLTS6vgyHI2o8NJk/lXggizOsHJFZyd7sGnnk/ynso86Rl4XA/dKkvwBS3ROrD3r8O1sPEoRKEgt5vwlwYMslSsL1qa0lL/20TuWx78GKEX0b9PI7ZRRMvC5wWS5CRwmvaL8U6N/FUcJYFPcNIy0dIh2mfhUwqnqfxEqYsddN1id8fUm46SwsGgWOTjMKA/OHi5y7gHvQ5wnNlk4c+/55cytwm9wBW5taDRc0ndPOPmWb/YMtMHY7obA+wV8/57"
Instead of using the actual public certificate string, the lookup plugin can be used.
- name: Append /home/john.doe/.ssh/id_rsa.pub on the control node to /home/john.doe/.ssh/authorized_keys on the managed node
ansible.posix.authorized_key:
user: john.doe
key: "{{ lookup('file', '/home/john.doe/.ssh/id_rsa.pub') }}"
Here is a bit of a more complete example.
- This almost always requires you to set gather_facts: false as this is typically done before you are able to SSH onto target servers
- Let's first use the openssh_keypair module to create the public/private keypair on localhost if they do not exist
- Use the find module to find the SSH public keys on localhost
- Use slurp and set_fact to create a variable named ssh_key_content that contains the content of the SSH key on localhost
- Use authorized_key to append the SSH key to the authorized_key file on the target server
---
- hosts: all
gather_facts: false
vars:
type: id_ed25519
tasks:
- name: create the public/private key pair on localhost if they do not exist
community.crypto.openssh_keypair:
path: /home/{{ lookup('env', 'USER') }}/.ssh/{{ type }}
regenerate: never
comment: "{{ lookup('env', 'USER') }}@{{ inventory_hostname }}"
size: 4096
delegate_to: localhost
delegate_facts: true
register: out
- name: find SSH public keys on localhost
ansible.builtin.find:
paths: /home/{{ lookup('env', 'USER') }}/.ssh
patterns: (?i).*pub
use_regex: true
register: find_results
delegate_to: localhost
delegate_facts: true
- name: set_fact ssh_key
ansible.builtin.set_fact:
ssh_key: "{{ item.path }}"
with_items: "{{ find_results.files }}"
- name: slurp ssh_key
ansible.builtin.slurp:
src: "{{ ssh_key }}"
register: slurp
delegate_to: localhost
delegate_facts: true
- name: set_fact ssh_key_content
ansible.builtin.set_fact:
ssh_key_content: "{{ slurp.content | b64decode | regex_replace('\n$', '') }}"
- name: append SSH key on localhost (that's the Ansible controller) to /home/{{ remote_user }}/.ssh/authorized_keys on {{ inventory_hostname }}
ansible.posix.authorized_key:
user: "{{ remote_user }}"
key: "{{ ssh_key_content }}"
..
Remove a certificate
When the state parameter has a value of absent, the certificate will be removed.
- name: Remove John Doe's public certificate from the authorized_keys file on the managed node
ansible.posix.authorized_key:
path: /etc/ssh/authorized_keys
state: absent
key: "{{ lookup('file', '/home/john.doe/.ssh/id_rsa.pub') }}"
Permission Denied
Let's say the following is returned. Typically, this means you are invoking the playbook as a user that does not have permission to access John Doe's home directory (/home/john.doe).
[WARNING]: Unable to find '/home/john.doe/.ssh/id_rsa.pub' in expected paths.
Register / Debug
The register parameter and debug module can then be used to output the results. In the scenario where id_rsa.pub is successfully appended to the authorized_keys file, something like this should be returned.
TASK [print the 'out' variable]
ok: [server1.example.com => {
"out": {
"changed": true,
"comment": null,
"exclusive": false,
"failed": false,
"follow": false,
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD9Kkwp0hH4dfDrhmyy9aXfpAykF5eZR9DvUpSj7p0cJ89Npul7sBhTE5ILV/cZOXnPcQmRNROIGFczv3Io9qHK/V4jNhGM+W+tL9QhiWF4FTqyO8cE8LyExV2WbmeJRTijY7DSXxL69vczYLhSptEFIh5+uHsdjbLN8AhZJ5mJwLcDA32vwav5X5QaSwUja3gIzLwHDfkREef+53l4B8XSQoVcgmxiAcNcIydfzZGmWHT2icwNujDYWLgyu/uu3HkV1E3ACxJuk8pkh3fPWNCuBu1cincd0z270OlbByj2ocobnUtXKDw8DT34Oz1NHMsnIFuG6ho1inp0eQ1yY7v john.doe@ansible.example.com",
"key_options": null,
"keyfile": "/home/john.doe/.ssh/authorized_keys",
"manage_dir": true,
"path": null,
"state": "present",
"user": "john.doe",
"validate_certs": true
}
}
Did you find this article helpful?
If so, consider buying me a coffee over at