puppet

Nov 052012
 

The Puppet language is well documented here, but to really understand its idioms takes practise and a lot of good working examples. While Puppet uses arrays as types, it doesn’t give a lot of operations for working with them. So for example, many resources take arrays as variables, but to try and apply a class to every element in an array individually, this is not so simple. A solution is presented below.

In this particular example, I’m also using the Puppet Firewall module, which you can get here. I wholeheartedly recommend using this module for managing iptables, as this is notoriously difficult to do with any granularity or flexibility on Puppet straight out of the box.

You’d configure it like this, to permit connections to the Apache HTTPD daemon, listening on port 80, from the client 10.10.10.10 only.

class iptables::httpd-server {    
   firewall { "500 allow connection to httpd:80 and https:443 from 10.10.10.10":
      state => ['NEW'],
      dport => [ '80', '443' ]
      proto => 'tcp',
      action  => 'accept',
      source  => '10.10.10.10',
   }
 }

But if, for example, you wanted to configure the firewall module to configure iptables for two or more source ports, you can’t just submit an array value to the “source” variable, as you can with the “dport” variable, because the syntax of the firewall resource won’t permit it (this is actually because you can’t do it in plain old iptables). You need to instead call the firewall class for each source address. An ugly way, would be like this:

class iptables::httpd-server {    
   firewall { "500 allow connection to httpd:80 and https:443 from 10.10.10.10":
      state => ['NEW'],
      dport => [ '80', '443' ]
      proto => 'tcp',
      action  => 'accept',
      source  => '10.10.10.10',
   }
   firewall { "500 allow connection to httpd:80 and https:443 from 10.10.10.11":
      state => ['NEW'],
      dport => [ '80', '443' ]
      proto => 'tcp',
      action  => 'accept',
      source  => '10.10.10.11',
   }
 }

Solution

But in the more likely scenario that you needed to generalise this module, and you effectively wanted to include the class iptables::httpd-server and have it utilise an array variable for the “source” resource, this is perhaps a more effective way of doing this – using a “define”.

class iptables::httpd-server {    
    define allow_http_client {
    	firewall { "500 allow connection to httpd:80 from $name":
        	state => ['NEW'],
        	dport => ['80','443'],
        	proto => 'tcp',
        	action  => 'accept',
        	source => $name,
    	}
    }
    if $incomingAcl {
      	$source = $incomingAcl
    } else {
    	$source = '0.0.0.0/0'
    }
    allow_http_client { $source: }
}

In this case, “define allow_http_client” sets a function which wraps the firewall{} resource. The variable “$name” is actually a reserved variable and is set to whatever is passed to the define when it’s called, in this case each element of “$source”. The variable “$incomingAcl” gets set somewhere outside, in the node definition perhaps.

Ths if conditional statement is just to implicitly set the source address to “0.0.0.0/0” (anything) as a default if the $incomingAcl variable isn’t set.

This may also be as useful illustration for other similar cases in Puppet where an array is needed, but cannot be used directly.


Matt Parsons is a freelance Linux specialist who has designed, built and supported Unix and Linux systems in the finance, telecommunications and media industries.

He lives and works in London.

May 022012
 

There’s an annoying and confusing error that can come up from time to time when performing a Puppet update from the client. In particular when running the update for the first time.

It looks like this:

# puppetd --test
err: Could not retrieve catalog from remote server: 
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: 
certificate verify failed
warning: Not using cache on failed catalog
err: Could not retrieve catalog; skipping run
 

This is saying that the verification check of the certificate against the keys has failed.

Solution

This could mean one of two things. The most common reason, particularly with a newly kickstarted host is that the discrepancy is too large between the time on the client and Puppet server. Or, the certificate on the client just needs to be regenerated.

Check the Date

Simply confirm this with the date command on both :

  # date
  Wed May 2 12:34:00 BST 2012

And either update manually, or using the ntpdate command.

The second reason is that the certificate on the client doesn’t match that on the server. The easiest way to remedy this is to clear both certificates and start again like this:

Remove client certificate

Remove all SSL information from the Puppet client configuration:

  # find /var/lib/puppet -type f -print0 |xargs -0r rm

Clean from server the client certificate

Where the fully-qualified domain name of the problematic client is “client.example.com”:

  # puppetca --clean 
 

Re-execute client Puppet run

Rerun the Puppet client update:

  # puppetd --test

If all goes well, the Puppet client should successfully verify its certificate and accept the updates, as it should.


Matt Parsons is a freelance Linux specialist who has designed, built and supported Unix and Linux systems in the finance, telecommunications and media industries.

He lives and works in London.