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.

  One Response to “Array mapping in Puppet”

  1. […] has been that users would write their own defined type wrapper to handle such arrays, e.g. here.  However, I believe there should be standard solutions to standard problems, and I […]

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>