The Fact Is…
One of the major interfaces to extend functionality with Puppet is the use of Facter and custom facts. A fact is a key/value data pair that represents some aspect of node state, such as its IP address, uptime, operating system, or whether it's a virtual machine. Custom facts are able to provide catalog compile time data to the puppet master to customise the configurations for a specific node (using PuppetDB, this data becomes available even when you're doing things elsewhere). Facts are provided by Facter, an independent, cross-platform Ruby library designed to gather information on all the nodes you will be managing with Puppet. For an example of using a custom fact within Puppet, you can use this data in the context of a catalog compile to make a decision about the catalog that you sent back to the node. A fact that tells us the node's operating system could cause some conditional logic in a class that tells the node the right set of packages to use to configure ntp on that particular system. Because the package names differ between Linuxes (let alone between Linux and Windows), this fact simplifies ntp configuration. Alternatively, you could use the $::ipaddress fact to customise the appropriate listen directive in Apache or SSH. You can provide Puppet custom facts by:
- Extending Puppet using Ruby
- Using the facter.d library shipped with the Puppet Labs stdlib module
- Simply setting an environment variable available during a Puppet agent run
Basing conditional decisions on your PKI bases that decision on the SSL certificate that the node used to authenticate (so interpolating and traversing your Hiera hierarchy should probably use data provided from the function above *not* $hostname or $fqdn). If you completely trust data supplied via facts, then including data, settings, or anything else in a catalog where that information might be used to perform a privilege escalation where a node has spoofed fact data, you've been hacked. The true danger here is that the escalation you achieve is to get root on something else. If your organisation has different sets of users root on different sets of boxes, your module design philosophy could lead to leakage across node sets. If a user has root and they have access to the private key, they can run Puppet and and spoof facts because Puppet runs as root. The spoofed facts may result in a catalog being returned that leaks potentially security impactful data, configurations and information that might be used to perform mischief elsewhere. Also, quick side note: if users have root on multiple boxes, there's nothing to stop them moving SSL certs around the place and putting incorrect configurations on the wrong boxes. Don't give people root if you don't have to! The alternative to using facts in modules to make decisions, as I mentioned above, is using trustable data. By that, I mean a combination of PKI validated information and the data you have control over on your Puppet Master. It may increase the overhead of the amount of static data you need to manage, but this is a trade off between security, how much you can be bothered, and what the outcome of it all going wrong is. Incidentally, this significantly promotes the idea of encoding useful data in hostnames and therefore Puppet certificate names (see my personal blog post on the simple things — this is an extension of this post). I personally have no problem with using facts to make decisions with low security impact outcomes. Ultimately, however, what all this comes down to is the good old fashioned security principle of not trusting user input and sanitizing the hell out of it before you do anything with it.
module Puppet::Parser::Functions newfunction(:certcheck, :type => :rvalue, :doc => <<-EOS Returns the actual certname EOS ) do |arguments| return host end end