317 changes: 232 additions & 85 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,234 @@
# How to contribute

Third-party patches are essential for keeping puppet great. We simply can't
access the huge number of platforms and myriad configurations for running
puppet. We want to keep it as easy as possible to contribute changes that
get things working in your environment. There are a few guidelines that we
need contributors to follow so that we can have a chance of keeping on
top of things.

## Getting Started

* Make sure you have a [Jira account](http://tickets.puppetlabs.com)
* Make sure you have a [GitHub account](https://github.com/signup/free)
* Submit a ticket for your issue, assuming one does not already exist.
* Clearly describe the issue including steps to reproduce when it is a bug.
* Make sure you fill in the earliest version that you know has the issue.
* Fork the repository on GitHub

## Making Changes

* Create a topic branch from where you want to base your work.
* This is usually the master branch.
* Only target release branches if you are certain your fix must be on that
branch.
* To quickly create a topic branch based on master; `git branch
fix/master/my_contribution master` then checkout the new branch with `git
checkout fix/master/my_contribution`. Please avoid working directly on the
`master` branch.
* Make commits of logical units.
* Check for unnecessary whitespace with `git diff --check` before committing.
* Make sure your commit messages are in the proper format.

````
(MODULES-1234) Make the example in CONTRIBUTING imperative and concrete
Without this patch applied the example commit message in the CONTRIBUTING
document is not a concrete example. This is a problem because the
contributor is left to imagine what the commit message should look like
based on a description rather than an example. This patch fixes the
problem by making the example concrete and imperative.
The first line is a real life imperative statement with a ticket number
from our issue tracker. The body describes the behavior without the patch,
why this is a problem, and how the patch fixes the problem when applied.
````

* Make sure you have added the necessary tests for your changes.
* Run _all_ the tests to assure nothing else was accidentally broken.

## Making Trivial Changes

### Documentation

For changes of a trivial nature to comments and documentation, it is not
always necessary to create a new ticket in Jira. In this case, it is
appropriate to start the first line of a commit with '(doc)' instead of
a ticket number.

````
(doc) Add documentation commit example to CONTRIBUTING
There is no example for contributing a documentation commit
to the Puppet repository. This is a problem because the contributor
is left to assume how a commit of this nature may appear.
The first line is a real life imperative statement with '(doc)' in
place of what would have been the ticket number in a
non-documentation related commit. The body describes the nature of
the new documentation or comments added.
````

## Submitting Changes

* Sign the [Contributor License Agreement](http://links.puppetlabs.com/cla).
* Push your changes to a topic branch in your fork of the repository.
* Submit a pull request to the repository in the puppetlabs organization.
* Update your Jira ticket to mark that you have submitted code and are ready for it to be reviewed (Status: Ready for Merge).
* Include a link to the pull request in the ticket.

# Additional Resources

* [More information on contributing](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing)
* [Bug tracker (Jira)](http://tickets.puppetlabs.com)
* [Contributor License Agreement](http://links.puppetlabs.com/cla)
Checklist (and a short version for the impatient)
=================================================

* Commits:

- Make commits of logical units.

- Check for unnecessary whitespace with "git diff --check" before
committing.

- Commit using Unix line endings (check the settings around "crlf" in
git-config(1)).

- Do not check in commented out code or unneeded files.

- The first line of the commit message should be a short
description (50 characters is the soft limit, excluding ticket
number(s)), and should skip the full stop.

- Associate the issue in the message. The first line should include
the issue number in the form "(#XXXX) Rest of message".

- The body should provide a meaningful commit message, which:

- uses the imperative, present tense: "change", not "changed" or
"changes".

- includes motivation for the change, and contrasts its
implementation with the previous behavior.

- Make sure that you have tests for the bug you are fixing, or
feature you are adding.

- Make sure the test suites passes after your commit:
`bundle exec rspec spec/acceptance` More information on [testing](#Testing) below

- When introducing a new feature, make sure it is properly
documented in the README.md

* Submission:

* Pre-requisites:

- Sign the [Contributor License Agreement](https://cla.puppetlabs.com/)

- Make sure you have a [GitHub account](https://github.com/join)

- [Create a ticket](http://projects.puppetlabs.com/projects/modules/issues/new), or [watch the ticket](http://projects.puppetlabs.com/projects/modules/issues) you are patching for.

* Preferred method:

- Fork the repository on GitHub.

- Push your changes to a topic branch in your fork of the
repository. (the format ticket/1234-short_description_of_change is
usually preferred for this project).

- Submit a pull request to the repository in the puppetlabs
organization.

The long version
================

1. Make separate commits for logically separate changes.

Please break your commits down into logically consistent units
which include new or changed tests relevant to the rest of the
change. The goal of doing this is to make the diff easier to
read for whoever is reviewing your code. In general, the easier
your diff is to read, the more likely someone will be happy to
review it and get it into the code base.

If you are going to refactor a piece of code, please do so as a
separate commit from your feature or bug fix changes.

We also really appreciate changes that include tests to make
sure the bug is not re-introduced, and that the feature is not
accidentally broken.

Describe the technical detail of the change(s). If your
description starts to get too long, that is a good sign that you
probably need to split up your commit into more finely grained
pieces.

Commits which plainly describe the things which help
reviewers check the patch and future developers understand the
code are much more likely to be merged in with a minimum of
bike-shedding or requested changes. Ideally, the commit message
would include information, and be in a form suitable for
inclusion in the release notes for the version of Puppet that
includes them.

Please also check that you are not introducing any trailing
whitespace or other "whitespace errors". You can do this by
running "git diff --check" on your changes before you commit.

2. Sign the Contributor License Agreement

Before we can accept your changes, we do need a signed Puppet
Labs Contributor License Agreement (CLA).

You can access the CLA via the [Contributor License Agreement link](https://cla.puppetlabs.com/)

If you have any questions about the CLA, please feel free to
contact Puppet Labs via email at cla-submissions@puppetlabs.com.

3. Sending your patches

To submit your changes via a GitHub pull request, we _highly_
recommend that you have them on a topic branch, instead of
directly on "master".
It makes things much easier to keep track of, especially if
you decide to work on another thing before your first change
is merged in.

GitHub has some pretty good
[general documentation](http://help.github.com/) on using
their site. They also have documentation on
[creating pull requests](http://help.github.com/send-pull-requests/).

In general, after pushing your topic branch up to your
repository on GitHub, you can switch to the branch in the
GitHub UI and click "Pull Request" towards the top of the page
in order to open a pull request.


4. Update the related GitHub issue.

If there is a GitHub issue associated with the change you
submitted, then you should update the ticket to include the
location of your branch, along with any other commentary you
may wish to make.

Testing
=======

Getting Started
---------------

Our puppet modules provide [`Gemfile`](./Gemfile)s which can tell a ruby
package manager such as [bundler](http://bundler.io/) what Ruby packages,
or Gems, are required to build, develop, and test this software.

Please make sure you have [bundler installed](http://bundler.io/#getting-started)
on your system, then use it to install all dependencies needed for this project,
by running

```shell
% bundle install
Fetching gem metadata from https://rubygems.org/........
Fetching gem metadata from https://rubygems.org/..
Using rake (10.1.0)
Using builder (3.2.2)
-- 8><-- many more --><8 --
Using rspec-system-puppet (2.2.0)
Using serverspec (0.6.3)
Using rspec-system-serverspec (1.0.0)
Using bundler (1.3.5)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
```

NOTE some systems may require you to run this command with sudo.

If you already have those gems installed, make sure they are up-to-date:

```shell
% bundle update
```

With all dependencies in place and up-to-date we can now run the tests:

```shell
% rake spec
```

This will execute all the [rspec tests](http://rspec-puppet.com/) tests
under [spec/defines](./spec/defines), [spec/classes](./spec/classes),
and so on. rspec tests may have the same kind of dependencies as the
module they are testing. While the module defines in its [Modulefile](./Modulefile),
rspec tests define them in [.fixtures.yml](./fixtures.yml).

Some puppet modules also come with [beaker](https://github.com/puppetlabs/beaker)
tests. These tests spin up a virtual machine under
[VirtualBox](https://www.virtualbox.org/)) with, controlling it with
[Vagrant](http://www.vagrantup.com/) to actually simulate scripted test
scenarios. In order to run these, you will need both of those tools
installed on your system.

You can run them by issuing the following command

```shell
% rake spec_clean
% rspec spec/acceptance
```

This will now download a pre-fabricated image configured in the [default node-set](./spec/acceptance/nodesets/default.yml),
install puppet, copy this module and install its dependencies per [spec/spec_helper_acceptance.rb](./spec/spec_helper_acceptance.rb)
and then run all the tests under [spec/acceptance](./spec/acceptance).

Writing Tests
-------------

XXX getting started writing tests.

If you have commit access to the repository
===========================================

Even if you have commit access to the repository, you will still need to
go through the process above, and have someone else review and merge
in your changes. The rule is that all changes must be reviewed by a
developer on the project (that did not write the code) to ensure that
all changes go through a code review process.

Having someone other than the author of the topic branch recorded as
performing the merge is the record that they performed the code
review.


Additional Resources
====================

* [Getting additional help](http://projects.puppetlabs.com/projects/puppet/wiki/Getting_Help)

* [Writing tests](http://projects.puppetlabs.com/projects/puppet/wiki/Development_Writing_Tests)

* [Patchwork](https://patchwork.puppetlabs.com)

* [Contributor License Agreement](https://projects.puppetlabs.com/contributor_licenses/sign)

* [General GitHub documentation](http://help.github.com/)

* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
* #puppet-dev IRC channel on freenode.org

22 changes: 15 additions & 7 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
source ENV['GEM_SOURCE'] || "https://rubygems.org"

group :development, :test do
gem 'puppetlabs_spec_helper', :require => false
gem 'rspec', '~> 2.11', :require => false
gem 'rspec-puppet', :require => false
gem 'serverspec', :require => false
gem 'beaker-rspec', '>= 2.2', :require => false
gem 'puppet-lint', :require => false
gem 'pry', :require => false
gem 'rake', :require => false
gem 'rspec-puppet', :require => false
gem 'puppetlabs_spec_helper', :require => false
gem 'serverspec', :require => false
gem 'puppet-lint', :require => false
gem 'beaker', :require => false
gem 'beaker-rspec', :require => false
gem 'pry', :require => false
gem 'simplecov', :require => false
end

if facterversion = ENV['FACTER_GEM_VERSION']
gem 'facter', facterversion, :require => false
else
gem 'facter', :require => false
end

if puppetversion = ENV['PUPPET_GEM_VERSION']
Expand Down
579 changes: 441 additions & 138 deletions README.markdown

Large diffs are not rendered by default.

18 changes: 7 additions & 11 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
require 'puppetlabs_spec_helper/rake_tasks'

require 'puppet-lint/tasks/puppet-lint'
PuppetLint.configuration.ignore_paths = ['vendor/**/*.pp']

task :default do
sh %{rake -T}
end

desc 'Run reasonably quick tests for CI'
task :ci => [
:lint,
:spec,
]
PuppetLint.configuration.fail_on_warnings
PuppetLint.configuration.send('disable_80chars')
PuppetLint.configuration.send('disable_class_inherits_from_params_class')
PuppetLint.configuration.send('disable_class_parameter_defaults')
PuppetLint.configuration.send('disable_documentation')
PuppetLint.configuration.send('disable_single_quote_string_with_variables')
PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]
86 changes: 45 additions & 41 deletions lib/puppet/provider/firewall/ip6tables.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source => :iptables do
Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source => :ip6tables do
@doc = "Ip6tables type provider"

has_feature :iptables
Expand Down Expand Up @@ -48,46 +48,50 @@ def self.iptables_save(*args)
@protocol = "IPv6"

@resource_map = {
:burst => "--limit-burst",
:connlimit_above => "-m connlimit --connlimit-above",
:connlimit_mask => "--connlimit-mask",
:connmark => "-m connmark --mark",
:ctstate => "-m conntrack --ctstate",
:destination => "-d",
:dport => "-m multiport --dports",
:gid => "-m owner --gid-owner",
:icmp => "-m icmp6 --icmpv6-type",
:iniface => "-i",
:jump => "-j",
:hop_limit => "-m hl --hl-eq",
:limit => "-m limit --limit",
:log_level => "--log-level",
:log_prefix => "--log-prefix",
:name => "-m comment --comment",
:outiface => "-o",
:port => '-m multiport --ports',
:proto => "-p",
:rdest => "--rdest",
:reap => "--reap",
:recent => "-m recent",
:reject => "--reject-with",
:rhitcount => "--hitcount",
:rname => "--name",
:rseconds => "--seconds",
:rsource => "--rsource",
:rttl => "--rttl",
:source => "-s",
:state => "-m state --state",
:sport => "-m multiport --sports",
:table => "-t",
:todest => "--to-destination",
:toports => "--to-ports",
:tosource => "--to-source",
:uid => "-m owner --uid-owner",
:pkttype => "-m pkttype --pkt-type",
:ishasmorefrags => "-m frag --fragid 0 --fragmore",
:islastfrag => "-m frag --fragid 0 --fraglast",
:isfirstfrag => "-m frag --fragid 0 --fragfirst",
:burst => "--limit-burst",
:connlimit_above => "-m connlimit --connlimit-above",
:connlimit_mask => "--connlimit-mask",
:connmark => "-m connmark --mark",
:ctstate => "-m conntrack --ctstate",
:destination => "-d",
:dport => "-m multiport --dports",
:gid => "-m owner --gid-owner",
:hop_limit => "-m hl --hl-eq",
:icmp => "-m icmp6 --icmpv6-type",
:iniface => "-i",
:isfirstfrag => "-m frag --fragid 0 --fragfirst",
:ishasmorefrags => "-m frag --fragid 0 --fragmore",
:islastfrag => "-m frag --fragid 0 --fraglast",
:jump => "-j",
:limit => "-m limit --limit",
:log_level => "--log-level",
:log_prefix => "--log-prefix",
:name => "-m comment --comment",
:outiface => "-o",
:pkttype => "-m pkttype --pkt-type",
:port => '-m multiport --ports',
:proto => "-p",
:rdest => "--rdest",
:reap => "--reap",
:recent => "-m recent",
:reject => "--reject-with",
:rhitcount => "--hitcount",
:rname => "--name",
:rseconds => "--seconds",
:rsource => "--rsource",
:rttl => "--rttl",
:source => "-s",
:sport => "-m multiport --sports",
:stat_every => '--every',
:stat_mode => "-m statistic --mode",
:stat_packet => '--packet',
:stat_probability => '--probability',
:state => "-m state --state",
:table => "-t",
:todest => "--to-destination",
:toports => "--to-ports",
:tosource => "--to-source",
:uid => "-m owner --uid-owner",
}

# These are known booleans that do not take a value, but we want to munge
Expand Down
209 changes: 143 additions & 66 deletions lib/puppet/provider/firewall/iptables.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
has_feature :ipsec_dir
has_feature :ipsec_policy
has_feature :mask
has_feature :ipset

optional_commands({
:iptables => 'iptables',
Expand All @@ -48,54 +49,60 @@
@protocol = "IPv4"

@resource_map = {
:burst => "--limit-burst",
:connlimit_above => "-m connlimit --connlimit-above",
:connlimit_mask => "--connlimit-mask",
:connmark => "-m connmark --mark",
:ctstate => "-m conntrack --ctstate",
:destination => "-d",
:dst_type => "-m addrtype --dst-type",
:dst_range => "-m iprange --dst-range",
:dport => ["-m multiport --dports", "--dport"],
:gid => "-m owner --gid-owner",
:icmp => "-m icmp --icmp-type",
:iniface => "-i",
:jump => "-j",
:limit => "-m limit --limit",
:log_level => "--log-level",
:log_prefix => "--log-prefix",
:name => "-m comment --comment",
:outiface => "-o",
:port => '-m multiport --ports',
:proto => "-p",
:random => "--random",
:rdest => "--rdest",
:reap => "--reap",
:recent => "-m recent",
:reject => "--reject-with",
:rhitcount => "--hitcount",
:rname => "--name",
:rseconds => "--seconds",
:rsource => "--rsource",
:rttl => "--rttl",
:set_mark => mark_flag,
:socket => "-m socket",
:source => "-s",
:src_type => "-m addrtype --src-type",
:src_range => "-m iprange --src-range",
:sport => ["-m multiport --sports", "--sport"],
:state => "-m state --state",
:table => "-t",
:tcp_flags => "-m tcp --tcp-flags",
:todest => "--to-destination",
:toports => "--to-ports",
:tosource => "--to-source",
:uid => "-m owner --uid-owner",
:pkttype => "-m pkttype --pkt-type",
:isfragment => "-f",
:ipsec_dir => "-m policy --dir",
:ipsec_policy => "--pol",
:mask => '--mask',
:burst => "--limit-burst",
:connlimit_above => "-m connlimit --connlimit-above",
:connlimit_mask => "--connlimit-mask",
:connmark => "-m connmark --mark",
:ctstate => "-m conntrack --ctstate",
:destination => "-d",
:dport => ["-m multiport --dports", "--dport"],
:dst_range => "-m iprange --dst-range",
:dst_type => "-m addrtype --dst-type",
:gid => "-m owner --gid-owner",
:icmp => "-m icmp --icmp-type",
:iniface => "-i",
:ipsec_dir => "-m policy --dir",
:ipsec_policy => "--pol",
:ipset => "-m set --match-set",
:isfragment => "-f",
:jump => "-j",
:limit => "-m limit --limit",
:log_level => "--log-level",
:log_prefix => "--log-prefix",
:mac_source => ["-m mac --mac-source", "--mac-source"],
:mask => '--mask',
:name => "-m comment --comment",
:outiface => "-o",
:pkttype => "-m pkttype --pkt-type",
:port => '-m multiport --ports',
:proto => "-p",
:random => "--random",
:rdest => "--rdest",
:reap => "--reap",
:recent => "-m recent",
:reject => "--reject-with",
:rhitcount => "--hitcount",
:rname => "--name",
:rseconds => "--seconds",
:rsource => "--rsource",
:rttl => "--rttl",
:set_mark => mark_flag,
:socket => "-m socket",
:source => "-s",
:sport => ["-m multiport --sports", "--sport"],
:src_range => "-m iprange --src-range",
:src_type => "-m addrtype --src-type",
:stat_every => '--every',
:stat_mode => "-m statistic --mode",
:stat_packet => '--packet',
:stat_probability => '--probability',
:state => "-m state --state",
:table => "-t",
:tcp_flags => "-m tcp --tcp-flags",
:todest => "--to-destination",
:toports => "--to-ports",
:tosource => "--to-source",
:uid => "-m owner --uid-owner",
}

# These are known booleans that do not take a value, but we want to munge
Expand Down Expand Up @@ -144,10 +151,11 @@
# This order can be determined by going through iptables source code or just tweaking and trying manually
@resource_list = [
:table, :source, :destination, :iniface, :outiface, :proto, :isfragment,
:src_range, :dst_range, :tcp_flags, :gid, :uid, :sport, :dport, :port,
:stat_mode, :stat_every, :stat_packet, :stat_probability,
:src_range, :dst_range, :tcp_flags, :gid, :uid, :mac_source, :sport, :dport, :port,
:dst_type, :src_type, :socket, :pkttype, :name, :ipsec_dir, :ipsec_policy,
:state, :ctstate, :icmp, :limit, :burst, :recent, :rseconds, :reap,
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :jump, :todest,
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :jump, :todest,
:tosource, :toports, :random, :log_prefix, :log_level, :reject, :set_mark,
:connlimit_above, :connlimit_mask, :connmark
]
Expand Down Expand Up @@ -215,13 +223,28 @@ def self.rule_to_hash(line, table, counter)

# --tcp-flags takes two values; we cheat by adding " around it
# so it behaves like --comment
values = values.sub(/--tcp-flags (\S*) (\S*)/, '--tcp-flags "\1 \2"')
values = values.gsub(/(!\s+)?--tcp-flags (\S*) (\S*)/, '--tcp-flags "\1\2 \3"')
# ditto for --match-set
values = values.sub(/(!\s+)?--match-set (\S*) (\S*)/, '--match-set "\1\2 \3"')
# we do a similar thing for negated address masks (source and destination).
values = values.sub(/(-\S+) (!)\s?(\S*)/,'\1 "\2 \3"')
values = values.gsub(/(-\S+) (!)\s?(\S*)/,'\1 "\2 \3"')
# the actual rule will have the ! mark before the option.
values = values.sub(/(!)\s*(-\S+)\s*(\S*)/, '\2 "\1 \3"')
values = values.gsub(/(!)\s*(-\S+)\s*(\S*)/, '\2 "\1 \3"')
# The match extension for tcp & udp are optional and throws off the @resource_map.
values = values.sub(/-m (tcp|udp) (--(s|d)port|-m multiport)/, '\2')
values = values.gsub(/-m (tcp|udp) (--(s|d)port|-m multiport)/, '\2')
# '--pol ipsec' takes many optional arguments; we cheat again by adding " around them
values = values.sub(/
--pol\sipsec
(\s--strict)?
(\s--reqid\s\S+)?
(\s--spi\s\S+)?
(\s--proto\s\S+)?
(\s--mode\s\S+)?
(\s--tunnel-dst\s\S+)?
(\s--tunnel-src\s\S+)?
(\s--next)?/x,
'--pol "ipsec\1\2\3\4\5\6\7\8" '
)

# Trick the system for booleans
@known_booleans.each do |bool|
Expand Down Expand Up @@ -277,14 +300,6 @@ def self.rule_to_hash(line, table, counter)
# POST PARSE CLUDGING
#####################

# Normalise all rules to CIDR notation.
[:source, :destination].each do |prop|
next if hash[prop].nil?
m = hash[prop].match(/(!?)\s?(.*)/)
neg = "! " if m[1] == "!"
hash[prop] = "#{neg}#{Puppet::Util::IPCidr.new(m[2]).cidr}"
end

[:dport, :sport, :port, :state, :ctstate].each do |prop|
hash[prop] = hash[prop].split(',') if ! hash[prop].nil?
end
Expand All @@ -308,15 +323,52 @@ def self.rule_to_hash(line, table, counter)
end
end

# Invert any rules that are prefixed with a '!'
[
:connmark,
:ctstate,
:destination,
:dport,
:dst_range,
:dst_type,
:ipset,
:port,
:proto,
:source,
:sport,
:src_range,
:src_type,
:state,
].each do |prop|
if hash[prop] and hash[prop].is_a?(Array)
# find if any are negated, then negate all if so
should_negate = hash[prop].index do |value|
value.match(/^(!)\s+/)
end
hash[prop] = hash[prop].collect { |v|
"! #{v.sub(/^!\s+/,'')}"
} if should_negate
elsif hash[prop]
m = hash[prop].match(/^(!?)\s?(.*)/)
neg = "! " if m[1] == "!"
if [:source,:destination].include?(prop)
# Normalise all rules to CIDR notation.
hash[prop] = "#{neg}#{Puppet::Util::IPCidr.new(m[2]).cidr}"
else
hash[prop] = "#{neg}#{m[2]}"
end
end
end

# States should always be sorted. This ensures that the output from
# iptables-save and user supplied resources is consistent.
hash[:state] = hash[:state].sort unless hash[:state].nil?
hash[:ctstate] = hash[:ctstate].sort unless hash[:ctstate].nil?

# This forces all existing, commentless rules or rules with invalid comments to be moved
# This forces all existing, commentless rules or rules with invalid comments to be moved
# to the bottom of the stack.
# Puppet-firewall requires that all rules have comments (resource names) and match this
# regex and will fail if a rule in iptables does not have a comment. We get around this
# Puppet-firewall requires that all rules have comments (resource names) and match this
# regex and will fail if a rule in iptables does not have a comment. We get around this
# by appending a high level
if ! hash[:name]
num = 9000 + counter
Expand Down Expand Up @@ -410,12 +462,37 @@ def general_args
end

args << [resource_map[res]].flatten.first.split(' ')
args = args.flatten

# On negations, the '!' has to be before the option (eg: "! -d 1.2.3.4")
if resource_value.is_a?(String) and resource_value.sub!(/^!\s*/, '') then
# we do this after adding the 'dash' argument because of ones like "-m multiport --dports", where we want it before the "--dports" but after "-m multiport".
# so we insert before whatever the last argument is
args.insert(-2, '!')
elsif resource_value.is_a?(Symbol) and resource_value.to_s.match(/^!/) then
#ruby 1.8.7 can't .match Symbols ------------------ ^
resource_value = resource_value.to_s.sub!(/^!\s*/, '').to_sym
args.insert(-2, '!')
elsif resource_value.is_a?(Array)
should_negate = resource_value.index do |value|
#ruby 1.8.7 can't .match symbols
value.to_s.match(/^(!)\s+/)
end
if should_negate
resource_value, wrong_values = resource_value.collect do |value|
if value.is_a?(String)
wrong = value if ! value.match(/^!\s+/)
[value.sub(/^!\s*/, ''),wrong]
else
[value,nil]
end
end.transpose
wrong_values = wrong_values.compact
if ! wrong_values.empty?
fail "All values of the '#{res}' property must be prefixed with a '!' when inverting, but '#{wrong_values.join("', '")}' #{wrong_values.length>1?"are":"is"} not prefixed; aborting"
end
args.insert(-2, '!')
end
end


Expand All @@ -430,7 +507,7 @@ def general_args
# our tcp_flags takes a single string with comma lists separated
# by space
# --tcp-flags expects two arguments
if res == :tcp_flags
if res == :tcp_flags or res == :ipset
one, two = resource_value.split(' ')
args << one
args << two
Expand Down
142 changes: 108 additions & 34 deletions lib/puppet/type/firewall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
`chain` or `jump` parameters, the firewall resource will autorequire
those firewallchain resources.
If Puppet is managing the iptables or iptables-persistent packages, and
the provider is iptables or ip6tables, the firewall resource will
If Puppet is managing the iptables, iptables-persistent, or iptables-services packages,
and the provider is iptables or ip6tables, the firewall resource will
autorequire those packages to ensure that any required binaries are
installed.
EOS
Expand Down Expand Up @@ -53,6 +53,8 @@
feature :isfirstfrag, "Match the first fragment of a fragmented ipv6 packet"
feature :ipsec_policy, "Match IPsec policy"
feature :ipsec_dir, "Match IPsec policy direction"
feature :mask, "Ability to match recent rules based on the ipv4 mask"
feature :ipset, "Match against specified ipset list"

# provider specific features
feature :iptables, "The provider provides iptables features."
Expand Down Expand Up @@ -323,7 +325,9 @@ def should_to_s(value)
*tcp*.
EOS

newvalues(:tcp, :udp, :icmp, :"ipv6-icmp", :esp, :ah, :vrrp, :igmp, :ipencap, :ospf, :gre, :all)
newvalues(*[:tcp, :udp, :icmp, :"ipv6-icmp", :esp, :ah, :vrrp, :igmp, :ipencap, :ospf, :gre, :cbt, :all].collect do |proto|
[proto, "! #{proto}".to_sym]
end.flatten)
defaultto "tcp"
end

Expand Down Expand Up @@ -876,27 +880,83 @@ def should_to_s(value)

newproperty(:isfirstfrag, :required_features => :isfirstfrag) do
desc <<-EOS
If true, matches if the packet is the first fragment.
If true, matches if the packet is the first fragment.
Sadly cannot be negated. ipv6.
EOS

newvalues(:true, :false)
end

newproperty(:ipsec_policy, :required_features => :ipsec_policy) do
desc <<-EOS
Sets the ipsec policy type
EOS
desc <<-EOS
Sets the ipsec policy type. May take a combination of arguments for any flags that can be passed to `--pol ipsec` such as: `--strict`, `--reqid 100`, `--next`, `--proto esp`, etc.
EOS

newvalues(:none, :ipsec)
newvalues(:none, :ipsec)
end

newproperty(:ipsec_dir, :required_features => :ipsec_dir) do
desc <<-EOS
Sets the ipsec policy direction
EOS
desc <<-EOS
Sets the ipsec policy direction
EOS

newvalues(:in, :out)
end

newproperty(:stat_mode) do
desc <<-EOS
Set the matching mode for statistic matching. Supported modes are `random` and `nth`.
EOS

newvalues(:nth, :random)
end

newproperty(:stat_every) do
desc <<-EOS
Match one packet every nth packet. Requires `stat_mode => 'nth'`
EOS

validate do |value|
unless value =~ /^\d+$/
raise ArgumentError, <<-EOS
stat_every value must be a digit
EOS
end

unless value.to_i > 0
raise ArgumentError, <<-EOS
stat_every value must be larger than 0
EOS
end
end
end

newproperty(:stat_packet) do
desc <<-EOS
Set the initial counter value for the nth mode. Must be between 0 and the value of `stat_every`. Defaults to 0. Requires `stat_mode => 'nth'`
EOS

newvalues(/^\d+$/)
end

newproperty(:stat_probability) do
desc <<-EOS
Set the probability from 0 to 1 for a packet to be randomly matched. It works only with `stat_mode => 'random'`.
EOS

validate do |value|
unless value =~ /^([01])\.(\d+)$/
raise ArgumentError, <<-EOS
stat_probability must be between 0.0 and 1.0
EOS
end

newvalues(:in, :out)
if $1.to_i == 1 && $2.to_i != 0
raise ArgumentError, <<-EOS
start_probability must be between 0.0 and 1.0
EOS
end
end
end

newproperty(:mask, :required_features => :mask) do
Expand All @@ -905,12 +965,29 @@ def should_to_s(value)
EOS
end

newproperty(:ipset, :required_features => :ipset) do
desc <<-EOS
Matches against the specified ipset list.
Requires ipset kernel module.
The value is the name of the blacklist, followed by a space, and then
'src' and/or 'dst' separated by a comma.
For example: 'blacklist src,dst'
EOS
end

newparam(:line) do
desc <<-EOS
Read-only property for caching the rule line.
EOS
end

newproperty(:mac_source) do
desc <<-EOS
MAC Source
EOS
newvalues(/^([0-9a-f]{2}[:]){5}([0-9a-f]{2})$/i)
end

autorequire(:firewallchain) do
reqs = []
protocol = nil
Expand All @@ -937,7 +1014,7 @@ def should_to_s(value)
autorequire(:package) do
case value(:provider)
when :iptables, :ip6tables
%w{iptables iptables-persistent}
%w{iptables iptables-persistent iptables-services}
else
[]
end
Expand Down Expand Up @@ -973,20 +1050,6 @@ def should_to_s(value)

# Now we analyse the individual properties to make sure they apply to
# the correct combinations.
if value(:iniface)
unless value(:chain).to_s =~ /INPUT|FORWARD|PREROUTING/
self.fail "Parameter iniface only applies to chains " \
"INPUT,FORWARD,PREROUTING"
end
end

if value(:outiface)
unless value(:chain).to_s =~ /OUTPUT|FORWARD|POSTROUTING/
self.fail "Parameter outiface only applies to chains " \
"OUTPUT,FORWARD,POSTROUTING"
end
end

if value(:uid)
unless value(:chain).to_s =~ /OUTPUT|POSTROUTING/
self.fail "Parameter uid only applies to chains " \
Expand Down Expand Up @@ -1038,13 +1101,6 @@ def should_to_s(value)
end
end

if value(:jump).to_s == "REDIRECT"
unless value(:toports)
self.fail "Parameter jump => REDIRECT missing mandatory toports " \
"parameter"
end
end

if value(:jump).to_s == "MASQUERADE"
unless value(:table).to_s =~ /nat/
self.fail "Parameter jump => MASQUERADE only applies to table => nat"
Expand Down Expand Up @@ -1073,5 +1129,23 @@ def should_to_s(value)
self.fail "Mask can only be set if recent is enabled."
end

[:stat_packet, :stat_every, :stat_probability].each do |param|
if value(param) && ! value(:stat_mode)
self.fail "Parameter '#{param.to_s}' requires 'stat_mode' to be set"
end
end

if value(:stat_packet) && value(:stat_mode) != :nth
self.fail "Parameter 'stat_packet' requires 'stat_mode' to be set to 'nth'"
end

if value(:stat_every) && value(:stat_mode) != :nth
self.fail "Parameter 'stat_every' requires 'stat_mode' to be set to 'nth'"
end

if value(:stat_probability) && value(:stat_mode) != :random
self.fail "Parameter 'stat_probability' requires 'stat_mode' to be set to 'random'"
end

end
end
6 changes: 3 additions & 3 deletions lib/puppet/type/firewallchain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
allow it.
**Autorequires:**
If Puppet is managing the iptables or iptables-persistent packages, and
the provider is iptables_chain, the firewall resource will autorequire
If Puppet is managing the iptables, iptables-persistent, or iptables-services packages,
and the provider is iptables_chain, the firewall resource will autorequire
those packages to ensure that any required binaries are installed.
EOS

Expand Down Expand Up @@ -151,7 +151,7 @@
autorequire(:package) do
case value(:provider)
when :iptables_chain
%w{iptables iptables-persistent}
%w{iptables iptables-persistent iptables-services}
else
[]
end
Expand Down
19 changes: 10 additions & 9 deletions lib/puppet/util/firewall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,11 @@ def string_to_port(value, proto)
proto = 'tcp'
end

if value.kind_of?(String)
if value.match(/^\d+(-\d+)?$/)
return value
else
return Socket.getservbyname(value, proto).to_s
end
m = value.to_s.match(/^(!\s+)?(\S+)/)
if m[2].match(/^\d+(-\d+)?$/)
return "#{m[1]}#{m[2]}"
else
Socket.getservbyname(value.to_s, proto).to_s
return "#{m[1]}#{Socket.getservbyname(m[2], proto).to_s}"
end
end

Expand Down Expand Up @@ -172,7 +169,7 @@ def persist_iptables(proto)
end

# RHEL 7 and newer also use systemd to persist iptable rules
if os_key == 'RedHat' && Facter.value(:operatingsystem) == 'RedHat' && Facter.value(:operatingsystemrelease).to_i >= 7
if os_key == 'RedHat' && ['RedHat','CentOS','Scientific','SL','SLC','Ascendos','CloudLinux','PSBM','OracleLinux','OVS','OEL','Amazon','XenServer'].include?(Facter.value(:operatingsystem)) && Facter.value(:operatingsystemrelease).to_i >= 7
os_key = 'Fedora'
end

Expand All @@ -194,7 +191,11 @@ def persist_iptables(proto)
when :Debian
case proto.to_sym
when :IPv4, :IPv6
%w{/usr/sbin/service iptables-persistent save}
if Puppet::Util::Package.versioncmp(persist_ver, '1.0') > 0
%w{/usr/sbin/service netfilter-persistent save}
else
%w{/usr/sbin/service iptables-persistent save}
end
end
when :Debian_manual
case proto.to_sym
Expand Down
22 changes: 16 additions & 6 deletions manifests/linux/redhat.pp
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,31 @@
# RHEL 7 and later and Fedora 15 and later require the iptables-services
# package, which provides the /usr/libexec/iptables/iptables.init used by
# lib/puppet/util/firewall.rb.
if $::operatingsystem == RedHat and $::operatingsystemrelease >= 7 {
package { 'iptables-services':
ensure => present,
if ($::operatingsystem != 'Fedora' and versioncmp($::operatingsystemrelease, '7.0') >= 0)
or ($::operatingsystem == 'Fedora' and versioncmp($::operatingsystemrelease, '15') >= 0) {
service { "firewalld":
ensure => stopped,
enable => false,
before => Package['iptables-services']
}
}

if ($::operatingsystem == 'Fedora' and (( $::operatingsystemrelease =~ /^\d+/ and $::operatingsystemrelease >= 15 ) or $::operatingsystemrelease == "Rawhide")) {
package { 'iptables-services':
ensure => present,
ensure => present,
before => Service['iptables'],
}
}

service { 'iptables':
ensure => $ensure,
enable => $enable,
hasstatus => true,
require => File['/etc/sysconfig/iptables'],
}

file { '/etc/sysconfig/iptables':
ensure => present,
owner => 'root',
group => 'root',
mode => '0600',
}
}
142 changes: 75 additions & 67 deletions metadata.json
Original file line number Diff line number Diff line change
@@ -1,69 +1,77 @@
{
"name": "puppetlabs-firewall",
"version": "1.1.3",
"source": "https://github.com/puppetlabs/puppetlabs-firewall",
"author": "Puppet Labs",
"license": "Apache-2.0",
"project_page": "http://forge.puppetlabs.com/puppetlabs/firewall",
"summary": "Manages Firewalls such as iptable",
"operatingsystem_support": [
{
"operatingsystem": "RedHat",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "CentOS",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "OracleLinux",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "Scientific",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "SLES",
"operatingsystemrelease": [
"11 SP1"
]
},
{
"operatingsystem": "Debian",
"operatingsystemrelease": [
"6",
"7"
]
},
{
"operatingsystem": "Ubuntu",
"operatingsystemrelease": [
"10.04",
"12.04",
"14.04"
]
}
],
"requirements": [
{ "name": "pe", "version_requirement": ">= 3.2.0 < 3.4.0" },
{ "name": "puppet", "version_requirement": "3.x" }
],
"dependencies": []
"name": "puppetlabs-firewall",
"version": "1.2.0",
"author": "Puppet Labs",
"summary": "Manages Firewalls such as iptable",
"license": "Apache-2.0",
"source": "https://github.com/puppetlabs/puppetlabs-firewall",
"project_page": "http://github.com/puppetlabs/puppetlabs-firewall",
"issues_url": "https://tickets.puppetlabs.com/browse/MODULES",
"operatingsystem_support": [
{
"operatingsystem": "RedHat",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "CentOS",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "OracleLinux",
"operatingsystemrelease": [
"6",
"7"
]
},
{
"operatingsystem": "Scientific",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "SLES",
"operatingsystemrelease": [
"11 SP1"
]
},
{
"operatingsystem": "Debian",
"operatingsystemrelease": [
"6",
"7"
]
},
{
"operatingsystem": "Ubuntu",
"operatingsystemrelease": [
"10.04",
"12.04",
"14.04"
]
}
],
"requirements": [
{
"name": "pe",
"version_requirement": "3.x"
},
{
"name": "puppet",
"version_requirement": "3.x"
}
],
"dependencies": [

]
}
56 changes: 49 additions & 7 deletions spec/acceptance/firewall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'should contain the rule' do
Expand All @@ -139,7 +141,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'should contain the rule' do
Expand Down Expand Up @@ -189,7 +193,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'should contain the rule' do
Expand Down Expand Up @@ -239,7 +245,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'should contain the rule' do
Expand All @@ -262,7 +270,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'should contain the rule' do
Expand Down Expand Up @@ -312,7 +322,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'should contain the rule' do
Expand Down Expand Up @@ -839,7 +851,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'should contain the rule' do
Expand Down Expand Up @@ -1608,6 +1622,34 @@ class { '::firewall': }
end
end

describe 'mac_source' do
context '0A:1B:3C:4D:5E:6F' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '610 - test':
ensure => present,
source => '10.1.5.28/32',
mac_source => '0A:1B:3C:4D:5E:6F',
chain => 'INPUT',
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should contain the rule' do
shell('iptables-save') do |r|
if (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5')
expect(r.stdout).to match(/-A INPUT -s 10.1.5.28 -p tcp -m mac --mac-source 0A:1B:3C:4D:5E:6F -m comment --comment "610 - test"/)
else
expect(r.stdout).to match(/-A INPUT -s 10.1.5.28\/(32|255\.255\.255\.255) -p tcp -m mac --mac-source 0A:1B:3C:4D:5E:6F -m comment --comment "610 - test"/)
end
end
end
end
end

describe 'reset' do
it 'deletes all rules' do
shell('ip6tables --flush')
Expand Down
12 changes: 9 additions & 3 deletions spec/acceptance/firewallchain_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'finds the chain' do
Expand All @@ -33,7 +35,9 @@
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'fails to find the chain' do
Expand Down Expand Up @@ -112,7 +116,9 @@
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'finds the chain' do
Expand Down
62 changes: 62 additions & 0 deletions spec/acceptance/invert_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'spec_helper_acceptance'

describe 'firewall type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
before(:all) do
iptables_flush_all_tables
end

context "inverting rules" do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '601 disallow esp protocol':
action => 'accept',
proto => '! esp',
}
firewall { '602 drop NEW external website packets with FIN/RST/ACK set and SYN unset':
chain => 'INPUT',
state => 'NEW',
action => 'drop',
proto => 'tcp',
sport => ['! http', '! 443'],
source => '! 10.0.0.0/8',
tcp_flags => '! FIN,SYN,RST,ACK SYN',
}
EOS

apply_manifest(pp, :catch_failures => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'should contain the rules' do
shell('iptables-save') do |r|
if (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5')
expect(r.stdout).to match(/-A INPUT -p ! esp -m comment --comment "601 disallow esp protocol" -j ACCEPT/)
expect(r.stdout).to match(/-A INPUT -s ! 10\.0\.0\.0\/255\.0\.0\.0 -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m multiport --sports ! 80,443 -m comment --comment "602 drop NEW external website packets with FIN\/RST\/ACK set and SYN unset" -m state --state NEW -j DROP/)
else
expect(r.stdout).to match(/-A INPUT ! -p esp -m comment --comment "601 disallow esp protocol" -j ACCEPT/)
expect(r.stdout).to match(/-A INPUT ! -s 10\.0\.0\.0\/8 -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m multiport ! --sports 80,443 -m comment --comment "602 drop NEW external website packets with FIN\/RST\/ACK set and SYN unset" -m state --state NEW -j DROP/)
end
end
end
end
context "inverting partial array rules" do
it 'raises a failure' do
pp = <<-EOS
class { '::firewall': }
firewall { '603 drop 80,443 traffic':
chain => 'INPUT',
action => 'drop',
proto => 'tcp',
sport => ['! http', '443'],
}
EOS

apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/is not prefixed/)
end
end
end
end
10 changes: 8 additions & 2 deletions spec/acceptance/ip6_fragment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end

shell('ip6tables-save') do |r|
expect(r.stdout).to match(/#{line_match}/)
Expand All @@ -56,7 +58,11 @@ class { '::firewall': }
}
EOS

apply_manifest(pp, :catch_changes => true)
if fact('selinux') == 'true'
apply_manifest(pp, :catch_failures => true)
else
apply_manifest(pp, :catch_changes => true)
end

shell('ip6tables-save') do |r|
expect(r.stdout).to match(/#{line_match}/)
Expand Down
10 changes: 8 additions & 2 deletions spec/acceptance/isfragment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end

shell('iptables-save') do |r|
expect(r.stdout).to match(/#{line_match}/)
Expand All @@ -35,7 +37,11 @@ class { '::firewall': }
}
EOS

apply_manifest(pp, :catch_changes => true)
if fact('selinux') == 'true'
apply_manifest(pp, :catch_failures => true)
else
apply_manifest(pp, :catch_changes => true)
end

shell('iptables-save') do |r|
expect(r.stdout).to match(/#{line_match}/)
Expand Down
2 changes: 1 addition & 1 deletion spec/acceptance/nodesets/centos-59-x64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ HOSTS:
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box
hypervisor : vagrant
CONFIG:
type: foss
type: git
10 changes: 10 additions & 0 deletions spec/acceptance/nodesets/centos-65-x64.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
HOSTS:
centos-65-x64:
roles:
- master
platform: el-6-x86_64
box : centos-65-x64-vbox436-nocm
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-65-x64-virtualbox-nocm.box
hypervisor : vagrant
CONFIG:
type: foss
2 changes: 1 addition & 1 deletion spec/acceptance/nodesets/ubuntu-server-10044-x64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ HOSTS:
box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box
hypervisor : vagrant
CONFIG:
type: git
type: foss
8 changes: 5 additions & 3 deletions spec/acceptance/nodesets/ubuntu-server-1404-x64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ HOSTS:
ubuntu-server-1404-x64:
roles:
- master
platform: ubuntu-14.04-64
box: puppetlabs/ubuntu-14.04-64-nocm
platform: ubuntu-14.04-amd64
box : puppetlabs/ubuntu-14.04-64-nocm
box_url : https://vagrantcloud.com/puppetlabs/ubuntu-14.04-64-nocm
hypervisor : vagrant
CONFIG:
type: foss
log_level : debug
type: git
161 changes: 77 additions & 84 deletions spec/acceptance/params_spec.rb
Original file line number Diff line number Diff line change
@@ -1,35 +1,18 @@
require 'spec_helper_acceptance'

describe "param based tests:", :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
# Takes a hash and converts it into a firewall resource
def pp(params)
name = params.delete('name') || '100 test'
pm = <<-EOS
firewall { '#{name}':
EOS

params.each do |k,v|
pm += <<-EOS
#{k} => #{v},
EOS
end

pm += <<-EOS
}
EOS
pm
end

it 'test various params', :unless => (default['platform'].match(/el-5/) || fact('operatingsystem') == 'SLES') do
iptables_flush_all_tables

ppm = pp({
'table' => "'raw'",
'socket' => 'true',
'chain' => "'PREROUTING'",
'jump' => 'LOG',
'log_level' => 'debug',
})
ppm = <<-EOS
firewall { '100 test':
table => 'raw',
socket => 'true',
chain => 'PREROUTING',
jump => 'LOG',
log_level => 'debug',
}
EOS

expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2)
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero
Expand All @@ -38,38 +21,41 @@ def pp(params)
it 'test log rule' do
iptables_flush_all_tables

ppm = pp({
'name' => '998 log all',
'proto' => 'all',
'jump' => 'LOG',
'log_level' => 'debug',
})
ppm = <<-EOS
firewall { '998 log all':
proto => 'all',
jump => 'LOG',
log_level => 'debug',
}
EOS
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2)
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero
end

it 'test log rule - changing names' do
iptables_flush_all_tables

ppm1 = pp({
'name' => '004 log all INVALID packets',
'chain' => 'INPUT',
'proto' => 'all',
'ctstate' => 'INVALID',
'jump' => 'LOG',
'log_level' => '3',
'log_prefix' => '"IPTABLES dropped invalid: "',
})

ppm2 = pp({
'name' => '003 log all INVALID packets',
'chain' => 'INPUT',
'proto' => 'all',
'ctstate' => 'INVALID',
'jump' => 'LOG',
'log_level' => '3',
'log_prefix' => '"IPTABLES dropped invalid: "',
})
ppm1 = <<-EOS
firewall { '004 log all INVALID packets':
chain => 'INPUT',
proto => 'all',
ctstate => 'INVALID',
jump => 'LOG',
log_level => '3',
log_prefix => 'IPTABLES dropped invalid: ',
}
EOS

ppm2 = <<-EOS
firewall { '003 log all INVALID packets':
chain => 'INPUT',
proto => 'all',
ctstate => 'INVALID',
jump => 'LOG',
log_level => '3',
log_prefix => 'IPTABLES dropped invalid: ',
}
EOS

expect(apply_manifest(ppm1, :catch_failures => true).exit_code).to eq(2)

Expand All @@ -84,17 +70,19 @@ def pp(params)
it 'test chain - changing names' do
iptables_flush_all_tables

ppm1 = pp({
'name' => '004 with a chain',
'chain' => 'INPUT',
'proto' => 'all',
})
ppm1 = <<-EOS
firewall { '004 with a chain':
chain => 'INPUT',
proto => 'all',
}
EOS

ppm2 = pp({
'name' => '004 with a chain',
'chain' => 'OUTPUT',
'proto' => 'all',
})
ppm2 = <<-EOS
firewall { '004 with a chain':
chain => 'OUTPUT',
proto => 'all',
}
EOS

apply_manifest(ppm1, :expect_changes => true)

Expand All @@ -109,15 +97,16 @@ def pp(params)
it 'test log rule - idempotent' do
iptables_flush_all_tables

ppm1 = pp({
'name' => '004 log all INVALID packets',
'chain' => 'INPUT',
'proto' => 'all',
'ctstate' => 'INVALID',
'jump' => 'LOG',
'log_level' => '3',
'log_prefix' => '"IPTABLES dropped invalid: "',
})
ppm1 = <<-EOS
firewall { '004 log all INVALID packets':
chain => 'INPUT',
proto => 'all',
ctstate => 'INVALID',
jump => 'LOG',
log_level => '3',
log_prefix => 'IPTABLES dropped invalid: ',
}
EOS

expect(apply_manifest(ppm1, :catch_failures => true).exit_code).to eq(2)
expect(apply_manifest(ppm1, :catch_failures => true).exit_code).to be_zero
Expand All @@ -126,27 +115,31 @@ def pp(params)
it 'test src_range rule' do
iptables_flush_all_tables

ppm = pp({
'name' => '997 block src ip range',
'chain' => 'INPUT',
'proto' => 'all',
'action' => 'drop',
'src_range' => '"10.0.0.1-10.0.0.10"',
})
ppm = <<-EOS
firewall { '997 block src ip range':
chain => 'INPUT',
proto => 'all',
action => 'drop',
src_range => '10.0.0.1-10.0.0.10',
}
EOS

expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2)
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero
end

it 'test dst_range rule' do
iptables_flush_all_tables

ppm = pp({
'name' => '998 block dst ip range',
'chain' => 'INPUT',
'proto' => 'all',
'action' => 'drop',
'dst_range' => '"10.0.0.2-10.0.0.20"',
})
ppm = <<-EOS
firewall { '998 block dst ip range':
chain => 'INPUT',
proto => 'all',
action => 'drop',
dst_range => '10.0.0.2-10.0.0.20',
}
EOS

expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2)
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero
end
Expand Down
121 changes: 118 additions & 3 deletions spec/acceptance/purge_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ class { 'firewall': }
end
end

context('chain purge') do
context('ipv4 chain purge') do
after(:all) do
iptables_flush_all_tables
end
before(:each) do
iptables_flush_all_tables

Expand Down Expand Up @@ -68,7 +71,9 @@ class { 'firewall': }
}
EOS

apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'ignores specified rules' do
Expand All @@ -82,7 +87,11 @@ class { 'firewall': }
}
EOS

apply_manifest(pp, :catch_changes => true)
if fact('selinux') == 'true'
apply_manifest(pp, :catch_failures => true)
else
apply_manifest(pp, :catch_changes => true)
end
end

it 'adds managed rules with ignored rules' do
Expand Down Expand Up @@ -121,4 +130,110 @@ class { 'firewall': }
expect(shell('iptables-save').stdout).to match(/-A INPUT -s 1\.2\.1\.1(\/32)? -p tcp\s?\n-A INPUT -s 1\.2\.1\.1(\/32)? -p udp/)
end
end
context 'ipv6 chain purge', :unless => (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5') do
after(:all) do
ip6tables_flush_all_tables
end
before(:each) do
ip6tables_flush_all_tables

shell('ip6tables -A INPUT -p tcp -s 1::42')
shell('ip6tables -A INPUT -p udp -s 1::42')
shell('ip6tables -A OUTPUT -s 1::50 -m comment --comment "010 output-1::50"')
end

it 'purges only the specified chain' do
pp = <<-EOS
class { 'firewall': }
firewallchain { 'INPUT:filter:IPv6':
purge => true,
}
EOS

apply_manifest(pp, :expect_changes => true)

shell('ip6tables-save') do |r|
expect(r.stdout).to match(/010 output-1::50/)
expect(r.stdout).to_not match(/1::42/)
expect(r.stderr).to eq("")
end
end

it 'ignores managed rules' do
pp = <<-EOS
class { 'firewall': }
firewallchain { 'OUTPUT:filter:IPv6':
purge => true,
}
firewall { '010 output-1::50':
chain => 'OUTPUT',
proto => 'all',
source => '1::50',
provider => 'ip6tables',
}
EOS

unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'ignores specified rules' do
pp = <<-EOS
class { 'firewall': }
firewallchain { 'INPUT:filter:IPv6':
purge => true,
ignore => [
'-s 1::42',
],
}
EOS

if fact('selinux') == 'true'
apply_manifest(pp, :catch_failures => true)
else
apply_manifest(pp, :catch_changes => true)
end
end

it 'adds managed rules with ignored rules' do
pp = <<-EOS
class { 'firewall': }
firewallchain { 'INPUT:filter:IPv6':
purge => true,
ignore => [
'-s 1::42',
],
}
firewall { '014 input-1::46':
chain => 'INPUT',
proto => 'all',
source => '1::46',
provider => 'ip6tables',
}
-> firewall { '013 input-1::45':
chain => 'INPUT',
proto => 'all',
source => '1::45',
provider => 'ip6tables',
}
-> firewall { '012 input-1::44':
chain => 'INPUT',
proto => 'all',
source => '1::44',
provider => 'ip6tables',
}
-> firewall { '011 input-1::43':
chain => 'INPUT',
proto => 'all',
source => '1::43',
provider => 'ip6tables',
}
EOS

apply_manifest(pp, :catch_failures => true)

expect(shell('ip6tables-save').stdout).to match(/-A INPUT -s 1::42(\/128)? -p tcp\s?\n-A INPUT -s 1::42(\/128)? -p udp/)
end
end
end
37 changes: 37 additions & 0 deletions spec/acceptance/resource_cmd_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,41 @@
end
end
end

context 'accepts rules utilizing the statistic module' do
before :all do
iptables_flush_all_tables
# This command doesn't work with all versions/oses, so let it fail
shell('iptables -t nat -A POSTROUTING -d 1.2.3.4/32 -o eth0 -m statistic --mode nth --every 2 -j SNAT --to-source 2.3.4.5', :acceptable_exit_codes => [0,1,2] )
shell('iptables -t nat -A POSTROUTING -d 1.2.3.4/32 -o eth0 -m statistic --mode nth --every 1 --packet 0 -j SNAT --to-source 2.3.4.6')
shell('iptables -t nat -A POSTROUTING -d 1.2.3.4/32 -o eth0 -m statistic --mode random --probability 0.99 -j SNAT --to-source 2.3.4.7')
end

it do
shell('puppet resource firewall') do |r|
r.exit_code.should be_zero
# don't check stdout, testing preexisting rules, output is normal
# don't check stderr, puppet throws deprecation warnings
end
end
end

context 'accepts rules with negation' do
before :all do
iptables_flush_all_tables
shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 -m policy --dir out --pol ipsec -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.1.0/24 -d 192.168.122.0/24 -i eth0 -m policy --dir in --pol ipsec --reqid 108 --proto esp -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.122.0/24 -d 192.168.1.0/24 -o eth0 -m policy --dir out --pol ipsec --reqid 108 --proto esp -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.201.1/32 -d 192.168.122.0/24 -i eth0 -m policy --dir in --pol ipsec --reqid 107 --proto esp -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.122.0/24 -d 192.168.201.1/32 -o eth0 -m policy --dir out --pol ipsec --reqid 107 --proto esp -j ACCEPT')
end

it do
shell('puppet resource firewall') do |r|
r.exit_code.should be_zero
# don't check stdout, testing preexisting rules, output is normal
# don't check stderr, puppet throws deprecation warnings
end
end
end
end
4 changes: 3 additions & 1 deletion spec/acceptance/rules_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,9 @@ class { '::firewall': }

# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end

it 'contains appropriate rules' do
Expand Down
10 changes: 8 additions & 2 deletions spec/acceptance/socket_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ class { '::firewall': }
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end

shell('iptables-save -t raw') do |r|
expect(r.stdout).to match(/#{line_match}/)
Expand All @@ -40,7 +42,11 @@ class { '::firewall': }
}
EOS

apply_manifest(pp, :catch_changes => true)
if fact('selinux') == 'true'
apply_manifest(pp, :catch_failures => true)
else
apply_manifest(pp, :catch_changes => true)
end

shell('iptables-save -t raw') do |r|
expect(r.stdout).to match(/#{line_match}/)
Expand Down
4 changes: 3 additions & 1 deletion spec/acceptance/standard_usage_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class { 'firewall': }

# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
end
end
79 changes: 79 additions & 0 deletions spec/fixtures/iptables/conversion_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
# This hash is for testing a line conversion to a hash of parameters
# which will be used to create a resource.
ARGS_TO_HASH = {
'mac_source_1' => {
:line => '-A neutron-openvswi-FORWARD -s 1.2.3.4/32 -m mac --mac-source FA:16:00:00:00:00 -j ACCEPT',
:table => 'filter',
:params => {
:chain => 'neutron-openvswi-FORWARD',
:source => '1.2.3.4/32',
:mac_source => 'FA:16:00:00:00:00',
},
},
'dport_and_sport' => {
:line => '-A nova-compute-FORWARD -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp --sport 68 --dport 67 -j ACCEPT',
:table => 'filter',
Expand Down Expand Up @@ -464,6 +473,40 @@
:action => 'reject',
},
},
'disallow_esp_protocol' => {
:line => '-t filter ! -p esp -m comment --comment "063 disallow esp protocol" -j ACCEPT',
:table => 'filter',
:params => {
:name => '063 disallow esp protocol',
:action => 'accept',
:proto => '! esp',
},
},
'drop_new_packets_without_syn' => {
:line => '-t filter ! -s 10.0.0.0/8 ! -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m comment --comment "064 drop NEW non-tcp external packets with FIN/RST/ACK set and SYN unset" -m state --state NEW -j DROP',
:table => 'filter',
:params => {
:name => '064 drop NEW non-tcp external packets with FIN/RST/ACK set and SYN unset',
:state => ['NEW'],
:action => 'drop',
:proto => '! tcp',
:source => '! 10.0.0.0/8',
:tcp_flags => '! FIN,SYN,RST,ACK SYN',
},
},
'negate_dport_and_sport' => {
:line => '-A nova-compute-FORWARD -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ! --sport 68,69 ! --dport 67,66 -j ACCEPT',
:table => 'filter',
:params => {
:action => 'accept',
:chain => 'nova-compute-FORWARD',
:source => '0.0.0.0/32',
:destination => '255.255.255.255/32',
:sport => ['! 68','! 69'],
:dport => ['! 67','! 66'],
:proto => 'udp',
},
},
}

# This hash is for testing converting a hash to an argument line.
Expand Down Expand Up @@ -931,4 +974,40 @@
},
:args => ["-t", :filter, "-p", :all, "-m", "comment", "--comment", "062 REJECT connmark", "-j", "REJECT", "-m", "connmark", "--mark", "0x1"],
},
'disallow_esp_protocol' => {
:params => {
:name => '063 disallow esp protocol',
:table => 'filter',
:action => 'accept',
:proto => '! esp',
},
:args => ["-t", :filter, "!", "-p", :esp, "-m", "comment", "--comment", "063 disallow esp protocol", "-j", "ACCEPT"],
},
'drop_new_packets_without_syn' => {
:params => {
:name => '064 drop NEW non-tcp external packets with FIN/RST/ACK set and SYN unset',
:table => 'filter',
:chain => 'INPUT',
:state => ['NEW'],
:action => 'drop',
:proto => '! tcp',
:source => '! 10.0.0.0/8',
:tcp_flags => '! FIN,SYN,RST,ACK SYN',
},
:args => ["-t", :filter, "!", "-s", "10.0.0.0/8", "!", "-p", :tcp, "-m", "tcp", "!", "--tcp-flags", "FIN,SYN,RST,ACK", "SYN", "-m", "comment", "--comment", "064 drop NEW non-tcp external packets with FIN/RST/ACK set and SYN unset", "-m", "state", "--state", "NEW", "-j", "DROP"]
},
'negate_dport_and_sport' => {
:params => {
:name => '065 negate dport and sport',
:table => 'filter',
:action => 'accept',
:chain => 'nova-compute-FORWARD',
:source => '0.0.0.0/32',
:destination => '255.255.255.255/32',
:sport => ['! 68','! 69'],
:dport => ['! 67','! 66'],
:proto => 'udp',
},
:args => ["-t", :filter, "-s", "0.0.0.0/32", "-d", "255.255.255.255/32", "-p", :udp, "-m", "multiport", "!", "--sports", "68,69", "-m", "multiport", "!", "--dports", "67,66", "-m", "comment", "--comment", "065 negate dport and sport", "-j", "ACCEPT"],
},
}
6 changes: 6 additions & 0 deletions spec/spec.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
--format
s
--colour
--loadby
mtime
--backtrace
17 changes: 9 additions & 8 deletions spec/spec_helper_acceptance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ def ip6tables_flush_all_tables
end

unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no'
if hosts.first.is_pe?
install_pe
else
install_puppet
end
# This will install the latest available package on el and deb based
# systems fail on windows and osx, and install via gem on other *nixes
foss_opts = { :default_action => 'gem_install' }

if default.is_pe?; then install_pe; else install_puppet( foss_opts ); end

hosts.each do |host|
on host, "mkdir -p #{host['distmoduledir']}"
end
Expand All @@ -35,10 +36,10 @@ def ip6tables_flush_all_tables
# Configure all nodes in nodeset
c.before :suite do
# Install module and dependencies
puppet_module_install(:source => proj_root, :module_name => 'firewall')
hosts.each do |host|
shell('/bin/touch /etc/puppet/hiera.yaml')
shell('puppet module install puppetlabs-stdlib --version 3.2.0', { :acceptable_exit_codes => [0,1] })
copy_module_to(host, :source => proj_root, :module_name => 'firewall')
on(host, "/bin/touch #{host['hieraconf']}")
on host, puppet('module install puppetlabs-stdlib --version 3.2.0'), { :acceptable_exit_codes => [0,1] }
end
end
end
69 changes: 54 additions & 15 deletions spec/unit/classes/firewall_linux_redhat_spec.rb
Original file line number Diff line number Diff line change
@@ -1,22 +1,61 @@
require 'spec_helper'

describe 'firewall::linux::redhat', :type => :class do
it { should contain_service('iptables').with(
:ensure => 'running',
:enable => 'true'
)}
%w{RedHat CentOS Fedora}.each do |os|
oldreleases = (os == 'Fedora' ? ['14'] : ['6.5'])
newreleases = (os == 'Fedora' ? ['15','Rawhide'] : ['7.0.1406'])

context 'ensure => stopped' do
let(:params) {{ :ensure => 'stopped' }}
it { should contain_service('iptables').with(
:ensure => 'stopped'
)}
end
oldreleases.each do |osrel|
context "os #{os} and osrel #{osrel}" do
let(:facts) {{
:operatingsystem => os,
:operatingsystemrelease => osrel
}}

it { should_not contain_service('firewalld') }
it { should_not contain_package('iptables-services') }
end
end

newreleases.each do |osrel|
context "os #{os} and osrel #{osrel}" do
let(:facts) {{
:operatingsystem => os,
:operatingsystemrelease => osrel
}}

it { should contain_service('firewalld').with(
:ensure => 'stopped',
:enable => false,
:before => 'Package[iptables-services]'
)}

it { should contain_package('iptables-services').with(
:ensure => 'present',
:before => 'Service[iptables]'
)}
end
end

context 'enable => false' do
let(:params) {{ :enable => 'false' }}
it { should contain_service('iptables').with(
:enable => 'false'
)}
describe 'ensure' do
context 'default' do
it { should contain_service('iptables').with(
:ensure => 'running',
:enable => 'true'
)}
end
context 'ensure => stopped' do
let(:params) {{ :ensure => 'stopped' }}
it { should contain_service('iptables').with(
:ensure => 'stopped'
)}
end
context 'enable => false' do
let(:params) {{ :enable => 'false' }}
it { should contain_service('iptables').with(
:enable => 'false'
)}
end
end
end
end
2 changes: 1 addition & 1 deletion spec/unit/classes/firewall_linux_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
context 'RedHat like' do
%w{RedHat CentOS Fedora}.each do |os|
context "operatingsystem => #{os}" do
releases = (os == 'Fedora' ? [14,15,'Rawhide'] : [6,7])
releases = (os == 'Fedora' ? ['14','15','Rawhide'] : ['6','7'])
releases.each do |osrel|
context "operatingsystemrelease => #{osrel}" do
let(:facts) { facts_default.merge({ :operatingsystem => os,
Expand Down
21 changes: 21 additions & 0 deletions spec/unit/puppet/provider/iptables_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,27 @@
end
end

describe 'when inverting rules' do
let(:resource) {
Puppet::Type.type(:firewall).new({
:name => '040 partial invert',
:table => 'filter',
:action => 'accept',
:chain => 'nova-compute-FORWARD',
:source => '0.0.0.0/32',
:destination => '255.255.255.255/32',
:sport => ['! 78','79','http'],
:dport => ['77','! 76'],
:proto => 'udp',
})
}
let(:instance) { provider.new(resource) }

it 'fails when not all array items are inverted' do
expect { instance.insert }.to raise_error Puppet::Error, /but '79', '80' are not prefixed/
end
end

describe 'when deleting resources' do
let(:sample_rule) {
'-A INPUT -s 1.1.1.1 -d 1.1.1.1 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -j ACCEPT'
Expand Down
5 changes: 3 additions & 2 deletions spec/unit/puppet/type/firewall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -628,12 +628,13 @@
rel.target.ref.should == @resource.ref
end

it "provider #{provider} should autorequire packages iptables and iptables-persistent" do
it "provider #{provider} should autorequire packages iptables, iptables-persistent, and iptables-services" do
@resource[:provider] = provider
@resource[:provider].should == provider
packages = [
Puppet::Type.type(:package).new(:name => 'iptables'),
Puppet::Type.type(:package).new(:name => 'iptables-persistent')
Puppet::Type.type(:package).new(:name => 'iptables-persistent'),
Puppet::Type.type(:package).new(:name => 'iptables-services')
]
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
Expand Down
5 changes: 3 additions & 2 deletions spec/unit/puppet/type/firewallchain_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,12 @@
rel.target.ref.should == resource.ref
end

it "provider iptables_chain should autorequire packages iptables and iptables-persistent" do
it "provider iptables_chain should autorequire packages iptables, iptables-persistent, and iptables-services" do
resource[:provider].should == :iptables_chain
packages = [
Puppet::Type.type(:package).new(:name => 'iptables'),
Puppet::Type.type(:package).new(:name => 'iptables-persistent')
Puppet::Type.type(:package).new(:name => 'iptables-persistent'),
Puppet::Type.type(:package).new(:name => 'iptables-services')
]
catalog = Puppet::Resource::Catalog.new
catalog.add_resource resource
Expand Down
11 changes: 10 additions & 1 deletion spec/unit/puppet/util/firewall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,22 @@
subject.persist_iptables(proto)
end

it 'should exec for CentOS identified from operatingsystem' do
it 'should exec for CentOS 6 identified from operatingsystem and operatingsystemrelease' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS')
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('6.5')
expect(subject).to receive(:execute).with(%w{/sbin/service iptables save})
subject.persist_iptables(proto)
end

it 'should exec for CentOS 7 identified from operatingsystem and operatingsystemrelease' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS')
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('7.0.1406')
expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables/iptables.init save})
subject.persist_iptables(proto)
end

it 'should exec for Archlinux identified from osfamily' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return('Archlinux')
expect(subject).to receive(:execute).with(['/bin/sh', '-c', '/usr/sbin/iptables-save > /etc/iptables/iptables.rules'])
Expand Down