Authorizing SSH keys with Ansible is made easy by the authorized_key
module.
Or is it?
In the most simple case, we just want to authorize a single key, say the one of the current user:
- name: Authorize me
authorized_key:
user: "john_doe"
key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}"
state: present
Now, in our project we wanted to authorize multiple team members for a shared account, potentially with differences between hosts. So we put public keys in the playbook repository and did something like this:
- name: Authorize team
authorized_key:
user: "team_doe"
key: "{{ lookup('file', item) }}"
state: present
with_fileglob:
- "config/{{ inventory_dir | basename }}/ssh_keys/*.pub"
This is nice, but has a fatal flaw:
We do not de-authorize keys when we remove them from the playbook!
In case of a single key, we would solve this by adding exclusive: true
but, alas,
this is
not possible here:
This option is not loop aware, so if you use with_
, it will be exclusive per iteration of the loop.
If you want multiple keys in the file you need to pass them all to key in a single batch as mentioned above.
Therefore, we have to concatenate the key files. We could do this up-front and have only a single file with all keys in the playbook, but we prefer one file per key for transparency and maintainability reasons. Lucky for us, there we can also assemble a multi-line string inline:
- name: Authorize team
authorized_key:
user: "team_doe"
key: |
{% for filename in lookup('fileglob', 'config/{{ inventory_dir | basename }}/ssh_keys/*.pub', wantlist=true) -%}
{{ lookup('file', filename) }}
{% endfor %}
state: present
exclusive: true
Note the extra empty line in the for
-loop which translates into a line-break in the resulting string.
The output of ansible-playbook does not seem to indicate that it removed any keys from the server.
You may want to check separately that the desired effect was achieved. |