Dualstack puppet firewall IPv4 and IPv6 with hiera

As IPv6 deployment grows around the world, we decided to prepare our infrastructure as well. Besides the fact that we have to buy a lot of new hardware as Nehalem Supermicro motherboards don’t support IPv6 on IPMI and IPv6 PXE boot, we also have to care about software layer of our servers.

All our servers are managed only and we are responsible for their availability and correct configuration. We use puppet with hiera to keep at least basic server configuration (such as firewall, sssd, installed basic packages, sysctl tunning, …) managed the same everywhere. And of course we use the great puppetlabs firewall module.

However, to achieve simple IPv6 and IPv4 firewall configuration we had to write a wrapper around it. I am going to share simplified version of our configuration.


  - "data/9_local/%{module_name}"
  - "data/0_default/%{module_name}"

Directory 0_default is used for default configuration (such as allow icmp, ssh, loopback, … which we want to have everywhere the same. Directory 9_local is used for local server exceptions (external mysql connections, …)


include service_firewall


# == Class: Service_firewall
# Firewall configuration service
# If enabled is 0, all rules are removed and server accepts everything
class service_firewall (
) {
  # Load iptables resource
  resources { 'firewall': purge => true }
  # Set up firewall when enabled
  if $enabled == 1 {
    # Load the firewall
    Firewall {
      require => Class['service_firewall::include::pre'],
      before => Class['service_firewall::include::post'],
    class { '::firewall': }
    # Run additional rules - dualstack (IPv4 and IPv6 shared rules)
    $dualstack = hiera_hash('service_firewall::dualstack')
    service_firewall::dualstack { 'dualstack_hiera': hash => $dualstack }
    # Run additional rules - IPv4 specific
    $ipv4 = suffix_hash_title(hiera_hash('service_firewall::ipv4', {}), 'IPv4 only')
    create_resources('firewall', $ipv4)
    # Run additional rules - IPv6 specific
    $ipv6 = suffix_hash_title(hiera_hash('service_firewall::ipv6', {}), 'IPv6 only')
    create_resources('firewall', $ipv6, { 'provider' => 'ip6tables' })
    # Pre and post
    class { 'service_firewall::include::pre': }
    class { 'service_firewall::include::post': }


define service_firewall::dualstack (
) {
  $ipv4 = suffix_hash_title($hash, ' IPv4 dualstack')
  $ipv6 = suffix_hash_title($hash, ' IPv6 dualstack')
  create_resources('firewall', $ipv4)
  create_resources('firewall', $ipv6, { 'provider' => 'ip6tables' })

I think there is nothing exceptional except the suffix_hash_title function, which I had to write in order to prevent 2 the same titles to occure. It only takes the hash and adds suffix to every key.


module Puppet::Parser::Functions
  newfunction(:suffix_hash_title, :type => :rvalue) do |args|
    result = {}
    if args[0].class == Hash and args[1].class == String
      args[0].each do |title, values|
        result[title + args[1]] = values
    return result

And that’s it. service_firewall::include::pre and post are the same as in the puppetlabs example.

Let’s now take a look at yaml files.


service_firewall::enabled: 1
  '800 allow SSH connections':
    action: 'accept'
    proto: 'tcp'
    port: 22
service_firewall::ipv6: {}
service_firewall::ipv4: {}


  '500 http and https':
    action: 'accept'
    proto: 'tcp'
    port: [ 80, 443 ]
  '600 allow access from the office':
    action: 'accept'
    source: ''

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s