puppetlabs-firewall stages and persistence

1,473 views
Skip to first unread message

Christian McHugh

unread,
Feb 15, 2012, 1:00:14 PM2/15/12
to Puppet Users
Hi all,

I'm attempting to use the puppetlabs-firewall module. In testing,
rules are enabled in a random order, so it seems necessary to utilize
puppet stages to guarantee proper ordering.

I created a module to organize my firewalling. It consists of
localfw::pre to open the INPUT chain for established and related
connections, localfw::default for most normal rules, and localfw::post
to block everything else.

I run localfw::pre before stage[main] and localfw::post after. This
has fixed my firewall rules ordering issue, yay. However, rules are
now not being saved :(

I tried adding include localfw::config to ::pre, ::post, and ::default
which consisted of the persistence definitions:
exec { "persist-firewall":
command => "/sbin/iptables-save > /var/lib/iptables/rules.v4",
require => File ["/var/lib/iptables"],
refreshonly => true,
}
Firewall {
notify => Exec["persist-firewall"]
}


and while I don't get any errors, I also don't get any firewall rules
saved. It appears that Firewall never kicks to run the exec. If I add
these bits to localfw::pre, then the pre rules get saved. If I add to
localfw::post then all get saved, as expected. But in that case,
normal firewall changes to a node don't cause localfw::post to run
again, and thus aren't saved.

What is the recommended way to save iptables rules for persistence
when using puppet stages? Has anyone made this work?

Thanks

Christian McHugh

unread,
Feb 15, 2012, 3:36:42 PM2/15/12
to Puppet Users
I've got slightly more info. In trying to figure this out I ran across
http://projects.puppetlabs.com/issues/10665 where it was suggested
that the persist-firewall bits (already shown in the previous message)
get placed into site.pp. This almost worked perfectly.

I've placed the following inside a node definition.
class { "localfw::pre": stage => "pre" }
class { "localfw::post": stage => "post" }
include localfw

If I keep localfw::post empty of firewall definitions, everything
works fine. However, once I place anything in there (such as an empty
test: firewall { "999 testing": ; } I get an error about cyclic
dependencies.

# puppet agent -v --no-daemonize --onetime
info: Retrieving plugin
info: Loading facts in iptables
info: Loading facts in sshkeys
info: Loading facts in etc_facts
info: Loading facts in iptables
info: Loading facts in sshkeys
info: Loading facts in etc_facts
info: Caching catalog for testhost
err: Could not apply complete catalog: Found dependency cycles in the
following relationships: Firewall[999 drop all] => Exec[persist-
firewall], Exec[persist-firewall] => Firewall[999 drop all]; try using
the '--graph' option and open the '.dot' files in OmniGraffle or
GraphViz
notice: Finished catalog run in 0.65 seconds

Is this a bug, or am I doing something wrong? In trying to figure that
out it looks like it may be related to puppet bug #5349? Any thoughts?

The puppetlabs firewall module seems so close to being usable. Saving
the firewall to enable on boot is the last missing bit in my
checklist. Thanks much!

Mohamed Lrhazi

unread,
Mar 9, 2012, 9:47:13 AM3/9/12
to puppet...@googlegroups.com
Hi Christian,

I am running into this same issue... Did you resolve it?

Thanks a lot,
Mohamed.

> --
> You received this message because you are subscribed to the Google Groups "Puppet Users" group.
> To post to this group, send email to puppet...@googlegroups.com.
> To unsubscribe from this group, send email to puppet-users...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/puppet-users?hl=en.
>

Mohamed Lrhazi

unread,
Mar 9, 2012, 9:55:10 AM3/9/12
to puppet...@googlegroups.com
I just tried something which seems to workaround the cyclic dependency issue:

- put the exec definition in the class that runs in the post stage.

- in site.pp put the default:
Firewall {
notify => Exec["persist-firewall"],
}


Thanks,
Mohamed.

Christian McHugh

unread,
Mar 9, 2012, 9:43:14 PM3/9/12
to puppet...@googlegroups.com

Thus far I've only been able to get puppet to run without making the firewall persistent.


In the case of running the exec save-rules in the post: it's no good if your hosts are at all dynamic since it only runs after the main stage. So if you have an existing host, add another normal firewall rule, that rule will get added on the next puppet run. But since the firewall drop rule that exists in the post stage has already been pushed out, the post bits never get called, and thus the firewall rules are not saved and your update will be lost at boot.

I'm hoping something happens in development since there has not been a new revision in a little while and the github patches are stacking up.

Cheers

tujwww

unread,
Mar 10, 2012, 2:11:02 AM3/10/12
to puppet...@googlegroups.com
Looks like you are applying the rules in Pre, Main and Post stage using firewall, i wonder what could be the requirement to apply the rules in different stages instead of creating a File resource, Service notify trigger using Exec iptables-restore, if you don't mind giving a little elaboration. 

-Thanks

Christian McHugh

unread,
Mar 10, 2012, 2:14:33 PM3/10/12
to puppet...@googlegroups.com
Sounds interesting. As far as I've seen, the puppetlabs-firewall resource activates instantly. I've not tried to have them all write out to a file and trigger an exec iptables-restore. 

If the firewall resource kicks the only way I think it can, then we had an issue of firewall ordering. While rules are defined as "100 open port" and "999 drop all" the numbering did not seem to make any difference. We ended up in situations where the drop rules would kick before the allow established rules, and thus kill the puppet run. Our workaround was to run our base open ports rules in a pre stage, normal service stuff in main, and the drop in post.

If you have any recommendations for a better way to handle the fireall, I'd love to hear about it.

jcbollinger

unread,
Mar 12, 2012, 8:49:18 AM3/12/12
to Puppet Users


On Feb 15, 1:00 pm, Christian McHugh <christian.mch...@gmail.com>
wrote:
> Hi all,
>
> I'm attempting to use the puppetlabs-firewall module. In testing,
> rules are enabled in a random order, so it seems necessary to utilize
> puppet stages to guarantee proper ordering.


It is incorrect that you must use run stages to achieve your desired
ordering. In fact, it is *never* the case that run stages are the
only solution to ordering issues in Puppet, because there is nothing
you can do with them that you cannot also do with ordinary resource
relationships.

In many cases, solving an ordering problem by use of run stages is
like putting in a tack with a sledgehammer: not only is it overkill,
it also doesn't afford much precision or finesse.

I have no experience with the module in question, so I have no
specific suggestions to offer, but if you find run stages too crude a
tool for your task then I can advise you about how to achieve your
ordering requirements otherwise.


John

Mohamed Lrhazi

unread,
Mar 13, 2012, 5:16:07 PM3/13/12
to puppet...@googlegroups.com
The numbering in the firewall resource names is not meant for ordering
their executing, but for guaranteeing their uniqueness.

I too found that using stages is the only usable way out of this.

Just out of curiosity, what do you mean by:

> We ended
> up in situations where the drop rules would kick before the allow
> established rules, and thus kill the puppet run

In my experience, what breaks is the reporting attempt puppet clients
makes to the master, not the puppet run itself.

Mohamed.

> --
> You received this message because you are subscribed to the Google Groups
> "Puppet Users" group.

> To view this discussion on the web visit
> https://groups.google.com/d/msg/puppet-users/-/_GIF40iCIRYJ.

Christian McHugh

unread,
Mar 13, 2012, 5:24:20 PM3/13/12
to puppet...@googlegroups.com
I appreciate the interest but I don't understand how you can tell me you don't have any experience with the module but yet know that I'm doing it wrong. The puppetlabs firewall module does not have classes or anything else to base a dependency on. I agree, I would rather not use stages, which is why I originally posted this to see how folks were making it go.

If you do find a way to order rules without stages I'd love to hear about it.

Christian McHugh

unread,
Mar 13, 2012, 5:27:30 PM3/13/12
to puppet...@googlegroups.com
In the pre main stage I have defined rules to allow outbound and allow related and established. In the post main stage, it does a drop all. Before this was organized into stages, occasionally the drop all would get applied before keep established and allow outbound, and thus the client could lose its connection to the puppet master mid run.

Ken Barber

unread,
Mar 13, 2012, 6:24:50 PM3/13/12
to puppet...@googlegroups.com
This ordering behaviour is as you state, and the numbers in the
namevar are ultimately for how they get ordered in the file ruleset as
you state - but not what order they are _inserted_. Ideally it would
be great to have insertion order and order in the firewall list to be
the same - but this doesn't work yet, and there are reasons why this
isn't always desirable. Some people have suggested modifying the rule
file, instead of changing the rule directly to work around this - and
there are certainly merits in that approach (and drawbacks).

So I think though the documentation needs updating. This is what I use
in top scope, and I've included the pre/post classes that belong in
their respective module ultimately:

https://gist.github.com/2032141

You'll notice I ultimately don't use stages here, to avoid the problem
some people have with the exec being in the main stage.

If people can try this methodology and see if it works that would be
much appreciated, then the documentation can be updated to reflect
this pattern instead.

ken.

> --
> You received this message because you are subscribed to the Google Groups
> "Puppet Users" group.
> To view this discussion on the web visit

> https://groups.google.com/d/msg/puppet-users/-/xBTznk59RKkJ.

jcbollinger

unread,
Mar 14, 2012, 9:26:07 AM3/14/12
to Puppet Users


On Mar 13, 4:24 pm, Christian McHugh <christian.mch...@gmail.com>
wrote:
> I appreciate the interest but I don't understand how you can tell me you
> don't have any experience with the module but yet know that I'm doing it
> wrong.


I didn't say you were doing it wrong. I said you were incorrect that
run stages are the only way to address your ordering issue. I can be
completely confident in that assessment because, as I said, there is
NOTHING you can do with run stages that you cannot also do with
ordinary resource relationships.

Run stages are basically an abstraction layer on top of ordinary
relationships, with their own support in the DSL, but the price for
their greater abstraction is less precision. I state simple facts
when I say that run stages are a crude tool relative to ordinary
resource relationships, and that they have caused problems for some
people.

If you solve your problem via run stages then great, but I don't need
to know any details about the module to recommend that you consider a
more precise tool.


> The puppetlabs firewall module does not have classes or anything
> else to base a dependency on.


Clearly you have a misapprehension. Puppet allows you to assign
classes to nodes, and it allows you to directly assign resources to
nodes, but it provides no other mechanism for managing nodes. Any
module must provide one or the other to have any (direct) use at all.

Perhaps you fail to recognize that defined type instances are
resources just like any other. I mean, I can't imagine what else you
might be using that you could think was neither a class nor a
resource. But defined type instances are resources, so you can use
all the ordinary metaparameters with them (including requires /
before / subscribe / notify), you can use them in resource chaining
expressions, you can declare them abstractly or export them, you can
express references to them, you can collect them, etc. just like any
other resource.


> I agree, I would rather not use stages, which
> is why I originally posted this to see how folks were making it go.
>
> If you do find a way to order rules without stages I'd love to hear about
> it.


No "finding" is required. If you can achieve the order you want with
stages then you can achieve it without. I am willing to assist you
with that if you wish, but you'll need to specify to me the resources
and order you want. Other than that, there is nothing specific to the
firewall module here.


John

Christian McHugh

unread,
Mar 14, 2012, 12:05:34 PM3/14/12
to puppet...@googlegroups.com
Great! ... almost?

The Firewall notify dependency check almost covers everything. I really like its elegance.

The one problem I can still think of is that the firewall module is not the only one setting firewall rules. In the puppetlabs/apache module, for example, it attempts to open up port 80. Since there is no guarantee when a module is applied it is possible the firewall module will kick, followed by apache. Since the last rule in the firewall module is to drop all, it will match before the apache open port 80.

It is a little bit difficult to test module ordering aside from restarting the puppet master and just trying it out on a test node for about an hour. So I haven't tested this today.
You said:
the numbers in the namevar are ultimately for how they get 
ordered in the file ruleset as you state - but not what order
they are _inserted_.

Which makes me still think that the order various modules kick can affect the firewall rules. Thus, a stage after main is still needed to guarantee that the drop happens last. I hope I'm wrong, is there any alternative?

Ken Barber

unread,
Mar 14, 2012, 12:53:31 PM3/14/12
to puppet...@googlegroups.com
> You said:
>>
>> the numbers in the namevar are ultimately for how they get
>> ordered in the file ruleset as you state - but not what order
>> they are _inserted_.
>
> Which makes me still think that the order various modules kick can affect
> the firewall rules. Thus, a stage after main is still needed to guarantee
> that the drop happens last. I hope I'm wrong, is there any alternative?

If you look at my example in the gist:

Firewall {
notify => Exec["persist-firewall"],

before => Class["my_soe::fwpost"],
require => Class["my_soe::fwpre"],
}

I'm setting it so that by default, every rule firewall resource runs
'before' Class["my_soe::fwpost"], and it requires
Class["my_soe::fwpre"]. So in this example it doesn't need stages -
just put your pre & post in those classes.

ken.

Christian McHugh

unread,
Mar 14, 2012, 2:14:28 PM3/14/12
to puppet...@googlegroups.com
Super, it all works great!

Since the whole fwpre class is run before everything else, is it necessary to define each resource with dependencies with firewall {"002 testing": ...}->firewall {... as in your gist?

Anyway, works great for us now. Thanks much!

All that remains is waiting for a new release to get firewall rules at boot on debian, and then some magic work yet to be done for not stomping on custom chains like fail2ban.

Ken Barber

unread,
Mar 14, 2012, 2:21:14 PM3/14/12
to puppet...@googlegroups.com
> Since the whole fwpre class is run before everything else, is it necessary
> to define each resource with dependencies with firewall {"002 testing":
> ...}->firewall {... as in your gist?

No its not.

> Anyway, works great for us now. Thanks much!

Good to hear - I'll get the documentation fixed then.

> All that remains is waiting for a new release to get firewall rules at boot
> on debian, and then some magic work yet to be done for not stomping on
> custom chains like fail2ban.

Indeed.

ken.

jcbollinger

unread,
Jun 19, 2012, 9:56:05 AM6/19/12
to puppet...@googlegroups.com


On Tuesday, June 19, 2012 4:30:38 AM UTC-5, Ioannis wrote:
[...]

Stages generate cycles.

Yes, stages are prone to that.

Your solution tells me it cannot find the pre class:

No, your application of Ken's example tells you that.

Tue Jun 19 05:20:23 -0400 2012 Puppet (err): Failed to apply catalog: Could not find dependency Class[Fw::Pre] for Firewall[100 accept http connections] at

It definitely does not like:


Firewall {
  notify => Exec["persist-firewall"],
  before => Class["my_soe::fwpost"],
  require => Class["my_soe::fwpre"],
}
Ken's example is not a drop-in solution.  It works in appropriate context; especially, it requires your node declaration or ENC to declare the needed classes in an appropriate order.  Alternatively -- perhaps easier -- you could wrap parts of it in a class that provides the needed ordering.  Better yet, study the example to understand what it's doing, why it works when it does work, and what's needed to make it work.

We may be able to help you more concretely if you present your own manifests.


John

Ioannis Aslanidis

unread,
Jun 19, 2012, 10:07:11 AM6/19/12
to puppet...@googlegroups.com
Hello,

Thanks for the reply. I hope this is really not something related to
an incompatibility with puppet 2.7. Did anyone get this working using
puppet 2.7?

I have tried this with stages and I get the cyclic dependency problem.
Then I tried this solution using require => Class["fw::pre"] and
before => Class["fw::post"], but then puppet complains that it cannot
find fw::pre and fw::post.

The classes are defined in there. I even tried adding an import to the
file with these classes just before the global scope part with no
effect.

The message from puppet when using this example:

Tue Jun 19 09:56:56 -0400 2012 Puppet (err): Failed to apply catalog:
Could not find dependency Class[Fw::Pre] for Firewall[100 accept http
connections] at
/etc/puppet/env/production/manifests/classes/internals/fw.pp:61

Please, let me know how to properly put order to make this work.

Thanks!

Here my simple manifests:

# site.pp

import "nodes.pp"
import "classes/*/*.pp"


# nodes.pp

node basenode {
notify { "Base node configuration.": }
include fw
}

node 'hosting00' inherits basenode {
include hosting
}


# hosting.pp
class hosting {
include fw::http
}


# fw.pp

# global scope (same result here and in site.pp)
exec { 'persist-firewall':
command => $operatingsystem ? {
'debian' => '/sbin/iptables-save > /etc/iptables/rules.v4',
/(RedHat|CentOS)/ => '/sbin/iptables-save > /etc/sysconfig/iptables',
},
refreshonly => true,
}
Firewall {
notify => Exec["persist-firewall"],
require => Class["fw::pre"],
before => Class["fw::post"],
}
Firewallchain {
notify => Exec["persist-firewall"],
}
resources { "firewall": purge => true }

class fw {
firewall { "100 no op":
}
}

class fw::pre {
Firewall {
require => undef,
}
firewall { "000 accept all icmp requests":
proto => "icmp",
action => "accept",
}->
firewall { "001 allow packets with valid state":
state => ["RELATED", "ESTABLISHED"],
action => "accept",
}->
firewall { "002 allow all traffic to loopback":
iniface => "lo",
action => "accept",
}->
firewall { "010 accept ssh connections":
proto => "tcp",
port => "22",
action => "accept",
}
}

class fw::post {
firewall { "998 reject forwarding":
chain => "FORWARD",
action => "reject",
reject => "icmp-host-prohibited",
}
}

class fw::http {
firewall { "100 accept http connections":
proto => "tcp",
port => "80",
action => "accept",
> --
> You received this message because you are subscribed to the Google Groups
> "Puppet Users" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/puppet-users/-/1-6XzB2ulvgJ.
>
> To post to this group, send email to puppet...@googlegroups.com.
> To unsubscribe from this group, send email to
> puppet-users...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/puppet-users?hl=en.



--
Ioannis Aslanidis

Christian McHugh

unread,
Jun 19, 2012, 10:18:23 AM6/19/12
to puppet...@googlegroups.com
I have this working in our environment as a module, which I will attempt to describe.

module: casfirewall
init.pp
class casfirewall {
  include casfirewall::default, casfirewall::fwpre, casfirewall::fwpost

  file {"/etc/iptables":
    ensure => "directory",
    owner => "root",
    group => "root",
    mode => 700,
  }

  # Always persist firewall rules

  exec { "persist-firewall":
    command => $operatingsystem ? {
      "debian" => "/sbin/iptables-save > /etc/iptables/rules.v4",
      /(RedHat|CentOS)/ => "/sbin/iptables-save > /etc/sysconfig/iptables",
    },
    refreshonly => true,
    require => File["/etc/iptables"],

  }
  Firewall {
    notify => Exec["persist-firewall"],
    before => Class["casfirewall::fwpost"],
    require => Class["casfirewall::fwpre"],
  }

  # Setup firewall resource

  resources { "firewall": purge => true }
}


As you can see, this holds the meat and potatoes by including the Firewall notify, before, and require bits.
The fwpre class contains the initial firewall settings (abbreviated here)
class casfirewall::fwpre {
  Firewall {
    require => undef,
  }

  firewall { "000 allow outbound":
    proto => "all",
    chain => "OUTPUT",
    action => accept,
  }...

The fwpost class contains the drop everything else rule. Because of the before ordering in init.pp this rule gets applied last (and was the reason for starting this thread in the first place)
class casfirewall::fwpost {
  firewall {"999 drop all":
    proto => "all",
    action => drop,
    before => undef,
  }
}

In our init.pp we also have defined a default class. This contains all the rules to open ports to our monitoring servers or backup servers. These get applied after the initial pre class, and before the post as you would expect.

I hope that helps. The suggestions given in this thread about firewall ordering very much helped us. I look forward to seeing the firewall module get another release and more user uptake.

Ioannis Aslanidis

unread,
Jun 19, 2012, 10:32:32 AM6/19/12
to puppet...@googlegroups.com
Thanks a lot for the example. I managed to make it work. Turns out
that in fw::post my firewall rules need to have the parameter before
=> undef in order to work.
> --
> You received this message because you are subscribed to the Google Groups
> "Puppet Users" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/puppet-users/-/-B3-kjpoFvYJ.
Reply all
Reply to author
Forward
0 new messages