Ansible’s setup module, which is typically run as the first step
in a playbook, collects information (i.e. “facts”) about the target nodes and
returns that as a JSON object. Contained in that are the public RSA and
DSA SSH host keys. (I’ll show just one here for brevity.):
These facts are made available to templates and modules as so-called hostvars
– an array of facts indexed by the nodename. That’s all very fine and dandy, but how to we convert the SSH public key to a fingerprint ready to be stored in the DNS?
One method is to use an Ansibleaction plugin, which is very much like a module: it’s written in Python, but the difference is that while modules run on nodes, an action plugin runs on the managing server, typically also once per target node configured in the playbook. However, with a bit of magic, the action plugin runs once only. (Thanks to Daniel Hokka Zakrisson for the succinct explanation.)
The shell module will be run on all three nodes just after the setup module. (The latter is implicitly invoked on each node because of gather_facts which defaults to true.) Next up is our action plugin called sshfp_a which is run as a local action, i.e. on the management system, followed by an invocation of template which is also run locally. Look at the output I get when I run the play:
The setup, shell and template modules are each indeed run three times. Our action plugin
is run once only.
The diagram attempts to illustrate what Ansible does.
Getting back to the topic of SSHFP fingerprints, our action plugin (sshfp_a)
obtains all hostvars gathered during the setup-phase and can use the
information contained therein. The two values I’m interested in here are
ansible_ssh_host_key_rsa_public and ansible_ssh_host_key_dsa_public from
which we determine the SSH fingerprint using code I swiped from Paul Wouters
when I wrote facts2sshfp. This is the action plugin:
Note how BYPASS_HOST_LOOP ensures our action plugin is run once per play
only. Further, note where it says “Do something …”. Here’s where we would
process the SSH fingerprints. For example:
Supposing I don’t want to update DNS on the fly, I could (as mentioned above) store the SSHFP records in a file and massage those later. But how about we do that directly, using an Ansible template?
Our action plugin collects and returns the information it has gathered, and makes this
available as zonedata.
Thanks to how Ansible can register variables, we can collect this data in the play and hand it over to the template module as a variable called “dns”. Here’s the relevant portion of the playbook above:
With a template consisting of
we create a lovely zone file we can include in our zone master file in BIND or NSD: