Wednesday, September 13, 2017

chef-Attributes, Templates and Cookbook dependencies..

Problem: Manager wants a message
displayed when someone logs in

The Problem: We need to see a banner when we login that announces:
§ “<Machine Name> is property of <COMPANY NAME>”
§ “This server is in-scope for PCI Compliance”, but only if it the server
is indeed “in-scope”
Success: We see this message when we login to our test node.

Any ideas?

We could create a cookbook called motd (message of the day) that
defines an attribute for our Company Name and then writes out our
fancy banner.
We’ll only include the PCI line, if we determine the node is in-scope.
§ We can define an attribute as a simple Boolean for the test

Hold on! Too much for one cookbook?

Earlier we said cookbooks are usually a 1:1 mapping of an application
of functionality…
Should a single cookbook manage our banner AND determine if a machine is
in-scope for PCI Compliance? No.
§ Well factored cookbooks only contain information relative to their
domain.
There might be many recipes needed to determine in-scopeyness
PCI Compliance folks and Sysadmins might work in completely different
teams.
§ Best approach is to create a separate cookbook to manage the PCI
Compliance attribute

Roadmap to Success

§ Create a motd cookbook that defines an attribute for our Company
Name and uses that attribute to dynamically write our a /etc/motd
file.
§ Create a pci cookbook that defines an attribute for in-scope. The
motd cookbook will use this attribute to modify the banner.

First let’s make the motd cookbook…


Create the motd cookbook

chef generate cookbook motd


Compiling Cookbooks...
Recipe: code_generator::cookbook
* directory[/home/gaptech/chef-repo/cookbooks/motd] action create
- create new directory /home/gaptech/chef-repo/cookbooks/motd
- restore selinux security context
* template[/home/gaptech/chef-repo/cookbooks/motd/metadata.rb]
action create_if_missing
- create new file /home/gaptech/chef-repo/cookbooks/motd/
metadata.rb
- update content in file /home/gaptech/chef-repo/cookbooks/
motd/metadata.rb from none to c24a89
(diff output suppressed by config)
- restore selinux security context


Create the attributes/default.rb file

(motd/attributes/default.rb)

default['motd']['company'] = 'Gap, Inc.'

§ Creates a new Node Attribute: node['motd']['company']
§ Sets the value to the string ‘Gap, Inc.’
§ The first level of the attribute name (['motd']) should be named for
the cookbook it is defined / managed in.

What resource should we use?

We’ve used both file and cookbook_file, but why might these
resources be cumbersome?
cookbook_file is a static file. We’d have to make one per nodes…
inefficient
file just takes a string? We could use the hostname attribute…but we
wouldn’t be able to test the value of the PCI attribute.
§ Let’s use a template
template is an Embedded Ruby (ERB) template that is used to dynamically
generate static text files.
ERB templates may contain Ruby expressions and statements, and are a
great way to manage configuration files


What should the template resource look
like?

(motd/recipes/default.rb)
We’ve covered a couple of resources so far and the seem to follow a
pattern. Using your experiences so far, what might our template
resource look like?
§ Its going to be a template resource
§ Modifying the /etc/motd file
§ The source for our ERB template will be motd.erb
§ The /etc/motd file should end up with a mode of 0644

What should the template resource look
like?
(motd/recipes/default.rb)
#!
# Cookbook Name:: motd!
# Recipe:: default!
#!
# Copyright (c) 2015 The Authors, All Rights Reserved.!

template '/etc/motd' do!
source 'motd.erb’!
mode '0644’!
end!

Create the motd.erb file

(motd/templates/default/motd.erb)
<%= node['hostname'] %> is property of <%= node['motd']['company']
%>!
<% if node['pci']['in_scope'] -%>!
This server is in-scope for PCI compliance!
<% end -%>!
§ ERB stands for Embedded Ruby
§ <%= %> - Prints value of variable (or attribute) in file
§ <% %> - Ruby code to be executed
§ <% -%> - Execute Ruby code and don’t print a newline in the file



Templates are your best friend when
dealing with configuration files.



§ When you are rendering a file on the filesystem, you will almost
always use templates.
§ The dynamic nature of templates make them ideal for configuration
files.
This is the first example of creating a data-driven cookbook.
Our ERB template will render a different /etc/motd depending on the
state of the PCI attribute.


Best Practice: Recipes contain the pattern,
attributes provide the data

§ Recipes contain the pattern: How do we install apache? Or tomcat?
§ Attributes provide the data: What port should apache listen on? What
should my java_opts contain?
The goal is to write a single tested cookbook to manage an application.
§ It should do something sane if no attributes are overridden
§ But be flexible enough to change its default behavior if different data
is passed to it.

Upload our new motd cookbook

$ knife cookbook upload motd!



Add the recipe to our node’s run list

$ knife node run_list add node1 'recipe[motd]'


Run chef-client
[

sudo chef-client!

[2015-08-14T21:09:38+00:00] ERROR: Exception handlers complete!
Chef Client failed. 0 resources updated in 5.255172095 seconds!
[2015-08-14T21:09:39+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chefstacktrace.
out!
[2015-08-14T21:09:39+00:00] ERROR:!
Chef::Mixin::Template::TemplateError (undefined method `[]' for nil:NilClass)
on line #2:!
!
1: <%= node['hostname'] %> is property of <%= node['motd']['company'] %>!
2: <% if node['pci']['in_scope'] -%>!
3: This server is in-scope for PCI compliance!
4: <% end -%>!
!
(erubis):2:in `block in evaluate’!
/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/erubis-2.7.0/lib/erubis/
evaluator.rb:74:in `instance_eval’!
/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/erubis-2.7.0/lib/erubis/
evaluator.rb:74:in `evaluate'!

Personal request: READ THE LOGS

If your chef client run fails (which it will), do the following:
§ Review the stack trace chef will provide at the end of the run.
It will always be written out to /var/chef/cache/chef-stacktrace.out
§ Scroll up through the log and look for ERROR: blocks
In our log, we can see:
> Error in the template resource
> Which line the error is on in our ERB template
Chef client logs are detailed, but they contain a lot of data.
They will (almost) always point you in the right direction to solve the
issue.


nil:Nilclass, what does it mean?

When you see the dreaded nil:Nilclass error:
undefined method `[]' for nil:NilClass!
It usually means that you are trying to dereference an attribute
that does not exist!
In our case, we got this error because we have never defined the

node[‘pci’][‘in_scope’] attribute.

Create the pci cookbook

chef generate cookbook pci

Compiling Cookbooks...
Recipe: code_generator::cookbook
* directory[/home/gaptech/chef-repo/cookbooks/pci] action create
- create new directory /home/gaptech/chef-repo/cookbooks/pci
- restore selinux security context
* template[/home/gaptech/chef-repo/cookbooks/pci/metadata.rb]
action create_if_missing
- create new file /home/gaptech/chef-repo/cookbooks/pci/
metadata.rb
- update content in file /home/gaptech/chef-repo/cookbooks/pci/

metadata.rb from none to 21c066


Create the attributes/default.rb file

(pci/attributes/default.rb)
defautl['pci']['in_scope'] = true
§ Creates a new Node Attribute: node['pci']['in_scope']
§ Sets the value to the boolean ‘true’
§ The default in front of the attribute name sets its precedence type



There are 6 different types of precedence
(we only use 3)

(pci/attributes/default.rb)
The only 3 types of precedence that we should use are:
§ Automatic attributes – THE TRUTH as discovered by Ohai. These
attributes cannot be overridden. CPU, Memory, OS, etc…
§ Override attributes – Strongest way to set an attribute. (Use lightly)
§ Default attributes – Sane value used most of the time. Usually set in
the cookbook’s attributes file.
§ If you need to use a different precedent type, you’re probably doing
it wrong.

Best Practice: Use default attributes in
your cookbooks.
(pci/attributes/default.rb)
§ When setting an attribute in a cookbook, you should (almost) always
set it to default.
§ Just do it. We’ll see why later…

Upload our new pci cookbook

$ knife cookbook upload pci!
Uploading pci [0.1.0]!
Uploaded 1 cookbook.!

Run chef-client

sudo chef-client

[2015-08-14T21:09:38+00:00] ERROR: Exception handlers complete!
Chef Client failed. 0 resources updated in 5.255172095 seconds!
[2015-08-14T21:09:39+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chefstacktrace.
out!
[2015-08-14T21:09:39+00:00] ERROR:!
Chef::Mixin::Template::TemplateError (undefined method `[]' for nil:NilClass)
on line #2:!
!
1: <%= node['hostname'] %> is property of <%= node['motd']['company'] %>!
2: <% if node['pci']['in_scope'] -%>!
3: This server is in-scope for PCI compliance!
4: <% end -%>!
!
(erubis):2:in `block in evaluate’!
/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/erubis-2.7.0/lib/erubis/
evaluator.rb:74:in `instance_eval’!
/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/erubis-2.7.0/lib/erubis/
evaluator.rb:74:in `evaluate'!

Best Practice: Make sure your cookbooks
have (sane) default values.
*** IMPORTANT ***
If your cookbook needs an attribute to exist, it should either
§ Define a default value for it in your attributes file.
OR
§ Depend on another cookbook that will.
Never rely on an attribute ONLY defined in a role or
environment. Set defaults for sanity.

Declare dependency in the motd cookbook

(motd/metadata.rb)
name 'motd' !
maintainer 'The Authors' !
maintainer_email 'you@example.com' !
license 'all_rights' !
description 'Installs/Configures motd'!
long_description 'Installs/Configures motd' version
'0.1.0' !
depends 'pci' !
!
§ Cookbooks listed as a dependency in the metadata will cause the
chef-client to additionally download and evaluate them in the chefclient
run

Attributes for free!

§ Cookbooks declared as dependencies in the metadata file will
automatically have all of their attributes files downloaded and
evaluated.
§ Even if the dependent cookbook is never called in the run_list.

Upload our updated motd cookbook

$ knife cookbook upload motd!
Uploading motd [0.1.0]!
Uploaded 1 cookbook.!

Run chef-client

sudo chef-client

Recipe: motd::default!
* template[/etc/motd] action create!
[2015-08-14T23:58:58+00:00] INFO: Processing template[/etc/motd] action
create (motd::default line 7)!
[2015-08-14T23:58:58+00:00] INFO: template[/etc/motd] backed up to /var/chef/
backup/etc/motd.chef-20150814235858.904901!
[2015-08-14T23:58:58+00:00] INFO: template[/etc/motd] updated file contents /
etc/motd!
- update content in file /etc/motd from e3b0c4 to e33648!
--- /etc/motd!2015-08-14 23:58:50.115000618 +0000!
+++ /tmp/chef-rendered-template20150814-26275-jynj72 !2015-08-14
23:58:58.900000203 +0000!
@@ -1 +1,3 @@!
+node1 is property of Gap, Inc.!
+This server is in-scope for PCI compliance!
- restore selinux security context[!
2015-08-14T23:58:59+00:00] INFO: Chef Run complete in 3.744346794 seconds!

Check your work!

cat /etc/motd!
!!
node1 is property of Gap, Inc.!
This server is in-scope for PCI compliance


We just set some new attributes, can I see
them on the chef server?

knife node show node1 –l | less!

Node Name: node1!
Environment: _default!
FQDN: node1.novalocal!
IP: 172.16.232.5!
Run List: recipe[mysite], recipe[motd]!
Roles:!
Recipes: mysite::default, motd::default!
Platform: centos 6.6!
Tags:!
Attributes:!
tags:!
Default Attributes:!
motd:!
company: Gap, Inc.!
pci:!
in_scope: true!

Set in_scope to false
(pci/attributes/default.rb)
defautl['pci']['in_scope'] = false
§ We shouldn’t make every machine automatically in scope for PCI
compliance.
§ A sane default for this is false
§ In reality, we’d probably have a series of tests to see if the machine
was actually in scope instead of just setting a Boolean. But this is a
chef class, not a security class.

Upload the updated pci cookbook
$ knife cookbook upload pci!
Uploading pci [0.1.0]!
Uploaded 1 cookbook.!

Run chef-client

sudo chef-client!

Recipe: motd::default!
* template[/etc/motd] action create!
[2015-08-15T00:21:23+00:00] INFO: Processing template[/etc/motd] action
create (motd::default line 7)!
[2015-08-15T00:21:23+00:00] INFO: template[/etc/motd] backed up to /var/chef/
backup/etc/motd.chef-20150815002123.462803!
[2015-08-15T00:21:23+00:00] INFO: template[/etc/motd] updated file contents /
etc/motd!
- update content in file /etc/motd from e33648 to 06f5c0!
--- /etc/motd!2015-08-14 23:58:58.900000203 +0000!
+++ /tmp/chef-rendered-template20150815-26524-vg3bhr !2015-08-15
00:21:23.458000203 +0000!
@@ -1,3 +1,2 @@!
node1 is property of Gap, Inc.!
-This server is in-scope for PCI compliance!
- restore selinux security context!
[2015-08-15T00:21:24+00:00] INFO: Chef Run complete in 4.026022141 seconds

Check your work!
[
gaptech@node1 ~]$ cat /etc/motd!
!!
node1 is property of Gap, Inc.

Check the pci value again on the Chef
Server

knife node show node1 -a pci!

node1:!
pci:!
in_scope: false

The automation trinity

You have now learned the 3 most important resources in automation
§ Package
§ Service
§ Template
You will find that almost every cookbook will contain these resources in
one form or another.







No comments:

Post a Comment