Find Unpuppeted SSH Keys

It all started with one of those annoying little items on the todo list - find all the unpuppeted ssh authorized_keys files on a machine and alert on them. On first impressions it was going to be quite manual (always a bad sign), involve digging in to legacy installs and would be something we’d need to re-verify occasionally. It couldn’t be that bad though could it? After all how many places can an unmanaged-by- puppet sshkey live?

Essentially the task can be broken in to three main parts. The first, quite easy part, is to grab a list of all the users (hello /etc/passwd) and look for known key file names in their home directories. The second part, which was a little harder, is to build a list of all the authorized_keys files that puppet knows it’s managing for this host. Lastly once you have the two collections find the differences. Instead of doing static analysis on the puppetmasters classes and modules we’re going to focus on how to do it using the compiled desired state of what the local machine should look like, according to the puppet catalog.

The catalog (which lives at /var/lib/puppet/client_yaml/catalog/$fqdn.yaml in modern puppet) is a yaml-based representation of what puppet knows about how the local system should be configured. It contains details of all the resources to be managed on the local machine and their desired end state; which makes it perfect for our needs. I’m not going to go into the catalog in depth in this post but hopefully this little example will whet your appetite and spark some ideas.

Our example, the audit-sshkey-files nagios check, was actually quite easy to write (after some digging in to puppet and borrowing some code from Puppet Catalog Diff by R.I.Pienaar) and should hopefully show how much you can gain from using the meta-data puppet provides.

While most of the audit-sshkey-files script is boilerplate the most important snippet is below:

  if target.type == "File" and target.title.include? "/authorized_keys"
    @puppet_keys.push target.title
    return target.title

All we’re doing is building a list of any resources that are of type file and include the string “/authorized_keys” in their name (resource title in puppet terms). While this may not seem like much it’s potentially game changing, any resources or relationships that you’ve modelled in puppet can be later mined to add context to your other tools. You can (as we have here) audit security related files or find user ids puppet doesn’t know about and so might be inconsistent over systems. By using the catalog and the relationships and meta-data it provides you can make much more of your investment in deploying systems with puppet, and hopefully this little example presents an easy way to get started.

Now I’ve gushed about what the puppet catalog can do for you there are two caveats, firstly about my example. It isn’t a complete solution, for example it doesn’t look for other allowed “authorized_keys” filenames that are defined in the sshd_config file. But it does the 80% of what I needed in our environment and by managing the sshd_config file in puppet (as you should be) it’s easy for me to double check I’m looking for the correct files. Secondly about the Puppet catalog itself. Harnessing its contents doesn’t exactly have a shallow learning curve and documentation is a little thin on the ground. The original author of puppet Luke Kanies is working on some alternative ways of accessing this kind of information (such as via his Puppet Interfaces project) and as more people build their puppet deployments you can expect so see more and more harnessing of this additional structure.