fiddyspence's blog

How I Learned to Love My ENC

Facts in Puppet, depending on your point of view, are merely a thing, the absolute best thing, or why god why facts why. For some applications the latter is very much the case, take for example a hiera hierarchy.

This is a post that considers security of various approaches to designing Puppet code. We know already that facts are mere assertions, that are potentially subject to being overridden in various ways (FACTER_hostname=notthethingyoufirstthoughtof puppet agent -t).

Consider this hierarchy:

  - "%{identity}"
  - "%{isyouisorisyouaintasheep}"
  - global

In this case, it’s clear to me at least that $identity within Puppet’s scope should be a thing not overridden by the agent. We have a way of doing this already - see Trusted facts introduced in Puppet open source 3.4. There will be other cases where I want to set data that’s not overridable that can be used in the hierarchy. Enter the ENC.

If I set a variable in my ENC, I happen to be using the Puppet Enterprise console here however it should work for other ENCs, then the node object is initially created using the data returned from it.

name: training.puppetlabs.vm
  puppet_enterprise::license: {}

  isyouisorisyouaintasheep: i am a sheeple

Because this parameter is effectively now a top scope variable, Let’s see what happens when I do a Puppet run and attempt to spoof that data - I am expecting a Notify resource with the value of $isyouisorisyouaintasheep at top scope.

[root@training ~]# FACTER_isyouisorisyouaintasheep=followtheherd puppet agent -t
Info: Retrieving plugin
Info: Caching catalog for training.puppetlabs.vm
Info: Applying configuration version '1396371852'
Notice: i am a sheeple
Notice: /Stage[main]/Main/Notify[i am a sheeple]/message: defined 'message' as 'i am a sheeple'
Notice: Finished catalog run in 7.02 seconds

So, what has happened here is that the node object has been created with the variable $isyouisorisyouaintasheep from the parameters returned by the ENC. When the merge_facts method is called on the resultant object, it skips any already existing members of the hash (here, isyouisorisyouaintasheep) and leaves the value from the ENC. This avoids the overwriting of the existing values, and means that we carry on and get a catalog. Note - the value of the fact in the inventory service will still be set to whatever the agent asserted, but the top scope puppet variable given by the ENC will win and my hierarchy is driven by the correct value of my variable.

Alternatively, if you want to fail catalog compile, then you could set $isyouisorisyouaintasheep=$theencvariable from another value given by the ENC and then if the agent attempts to override it you explode. Literally. Wafer thin mint style.