Tom Wilson
Tom Wilson

Reputation: 837

Chef: How to test that a node attribute is not stored on Chef Server?

After some chef zero runs (which save the node state as local .json files) I was dismayed to find this in the node files: ... "ssmtp": { "auth_username": "secret user name", "auth_password": "even-more-secret password" } The same runs on Chef Server would save the node data on the server. That's is a problem of course, and I'll have to replace credentials, modify the recipes, etc. I am still investigating what caused this in the first place but my question is:

How I can create an rspec/chefspec test for a recipe to verify that a particular node attribute is NOT saved persistently in the node's .json file or on Chef Server?

I'd like to add this to my specs to make sure it never happens again.

Epilogue

The big lesson here is that anything finding its way into any node attribute winds up being saved in the node object representation.

Upvotes: 0

Views: 2660

Answers (2)

Matt Barlow
Matt Barlow

Reputation: 165

Chef Server will save all attributes, even if they are normal or default or another precedence level. You can get around this by using node.run_state which can be used to "stash transient data during a chef-client run."

You can see this at work in the elkstack community cookbook, most notably in the _lumberjack_secrets.rb recipe.

node.run_state being set:

if <CONDITION ABBREVIATED>
  node.run_state['lumberjack_decoded_key'] = Base64.decode64(lumberjack_secrets['key'])
  ....
end

node.run_state being used:

lumberjack_keypair = node.run_state['lumberjack_decoded_key'] && node.run_state['lumberjack_decoded_certificate']

As for the test, I haven't "tested" this myself, but you would be testing to make sure the attribute is not set, like this:

it 'should not have secret attributes set' do
  node = chef_run.node
  expect (node['my_secret']).to be_nil
end

Upvotes: 2

Tom Wilson
Tom Wilson

Reputation: 837

The key is in the comment from the Chef docs, "A normal attribute is a setting that persists in the node object. A normal attribute has a higher attribute precedence than a default attribute." That breaks the problem down into testing if an attribute is a "normal" attribute.

Digging into the Chef::Node and Chef::Node::Attribute class code, I found you can call node.attributes.normal to get just the the normal attributes. So this concise test correctly fails, indicating that my secret info is going to be stored persistently in the node object!

it 'does not have a normal attribute node[ssmtp][auth_password]' do expect(subject.node.attributes.normal['ssmtp'].keys).to_not include 'auth_password' end

resulting in this decent error message:

Failure/Error: expect(subject.node.attributes.normal['ssmtp'].keys).to_not include 'auth_password' expected ["auth_username", "auth_password"] not to include "auth_password"

You could also test to that if a specific value is not set, but I think testing that the key does not exist is more to the point.

Somehow I always get new insights from simply formulating a question to post here. So it goes.

Upvotes: 0

Related Questions