How to work around lack of array support in puppetlabs-firewall?

After a couple of irritating firewalling oversights I decided to have a go at replacing my hacked-together firewall management scripts with the puppetlabs-firewall module.

It’s going okay, but one thing I’ve found quite irritating is the lack of support for arrays of things such as source IPs or ICMP types.

For example, let’s say I have a sequence of shell commands like this:

#!/bin/bash
 
readonly IPT=/sbin/iptables
 
for icmptype in redirect router-advertisement router-solicitation \
                address-mask-request address-mask-reply; do
    $IPT -A INPUT -p icmp --icmp-type ${icmptype} -j DROP
done

You’d think that with puppetlabs-firewall you could do this:

class bffirewall::prev4 {
    Firewall { require => undef, }
 
    firewall { '00002 Disallow possibly harmful ICMP':
        proto    => 'icmp',
        icmp     => [ 'redirect', 'router-advertisement',
                      'router-solicitation', 'address-mask-request',
                      'address-mask-reply' ],
        action   => 'drop',
        provider => 'iptables',
    }
}

Well it is correct syntax which installs fine on the client, but taking a closer look it hasn’t worked. It’s just applied the first firewall rule out of the array, i.e.:

iptables -A INPUT -p icmp --icmp-type redirect -j DROP

There’s already a bug in Puppet’s JIRA about this.

Similarly, what if you need to add a similar rule for each of a set of source hosts? For example:

readonly MONITORS="192.168.0.244 192.168.0.238 192.168.4.71"
readonly CACTI="192.168.0.246"
readonly ENTROPY="192.168.0.215"
 
# Allow access from:
# - monitoring hosts
# - cacti
# - the entropy VIP
for host in ${MONITORS} ${CACTI} ${ENTROPY}; do
    $IPT -A INPUT -p tcp --dport 8888 -s ${host} -j ACCEPT
done

Again, your assumption about what would work…

    firewall { '08888 Allow egd connections':
        proto    => 'tcp',
        dport    => '8888',
        source   => [ '192.168.0.244', '192.168.0.238', '192.168.4.71',
                      '192.168.0.246', '192.168.0.215' ],
        action   => 'accept',
        provider => 'iptables',
    }

…just results in the inclusion of a rule for only the first source host, with the rest being silently discarded.

This one seems to have an existing bug too; though it has a status of closed/fixed it certainly isn’t working in the most recent release. Maybe I need to be using the head of the repository for that one.

So, what to do?

Duplicating the firewall {} blocks is one option that’s always going to work as a last resort.

Puppet’s DSL doesn’t support any kind of iteration as far as I’m aware, though it will in future — no surprise, as iteration and looping is kind of a glaring omission.

Until then, does anyone know any tricks to cut down on the repetition here?