281 changes: 166 additions & 115 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -52,76 +52,113 @@ Persistence of rules between reboots is handled automatically, although there ar

In your `site.pp` (or some similarly top-scope file), set up a metatype to purge unmanaged firewall resources. This will clear any existing rules and make sure that only rules defined in Puppet exist on the machine.

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

You should include the `firewall` class to ensure the correct packages are installed.

```puppet
class { 'firewall': }
```

Next, set up some default rules that allow networking (such as ICMP and TCP), as well as ensure that existing connections are not closed to avoid locking yourself out of your own boxes when Puppet runs.

```puppet
# Default firewall rules
firewall { '000 accept all icmp':
proto => 'icmp',
action => 'accept',
}
firewall { '001 accept all to lo interface':
proto => 'all',
iniface => 'lo',
action => 'accept',
}
firewall { '002 accept related established rules':
proto => 'all',
ctstate => ['RELATED', 'ESTABLISHED'],
action => 'accept',
}
# Big drop all
firewall { '999 drop all':
proto => 'all',
action => 'drop',
}
```

Finally, you should define you own rules.

###Upgrading

Next, set up the default parameters for all of the firewall rules you will be establishing later. These defaults will ensure that the pre and post classes (you will be setting up in just a moment) are run in the correct order to avoid locking you out of your box during the first puppet run.
####Upgrading from version 0.2.0 and newer

Firewall {
before => Class['my_fw::post'],
require => Class['my_fw::pre'],
}
Upgrade the module with the puppet module tool as normal:

You also need to declare the `my_fw::pre` & `my_fw::post` classes so that dependencies are satisfied. This can be achieved using an External Node Classifier or the following
puppet module upgrade puppetlabs/firewall

class { ['my_fw::pre', 'my_fw::post']: }
Previously, you would have required the following in your `site.pp` (or some other global location):

Finally, you should include the `firewall` class to ensure the correct packages are installed.
```puppet
Firewall {
before => Class['my_fw::post'],
require => Class['my_fw::pre'],
}
class { 'firewall': }
class { ['my_fw::pre', 'my_fw::post']: }
Now to create the `my_fw::pre` and `my_fw::post` classes. Firewall acts on your running firewall, making immediate changes as the catalog executes. Defining default pre and post rules allows you provide global defaults for your hosts before and after any custom rules; it is also required to avoid locking yourself out of your own boxes when Puppet runs. This approach employs a whitelist setup, so you can define what rules you want and everything else is ignored rather than removed.
class { 'firewall': }
```

The `pre` class should be located in `my_fw/manifests/pre.pp` and should contain any default rules to be applied first.
and create `pre` class in a `my_fw` module.

class my_fw::pre {
Firewall {
require => undef,
}
```puppet
class my_fw::pre {
Firewall {
require => undef,
}
# Default firewall rules
firewall { '000 accept all icmp':
proto => 'icmp',
action => 'accept',
}->
firewall { '001 accept all to lo interface':
proto => 'all',
iniface => 'lo',
action => 'accept',
}->
firewall { '002 accept related established rules':
proto => 'all',
ctstate => ['RELATED', 'ESTABLISHED'],
action => 'accept',
}
}
# Default firewall rules
firewall { '000 accept all icmp':
proto => 'icmp',
action => 'accept',
}->
firewall { '001 accept all to lo interface':
proto => 'all',
iniface => 'lo',
action => 'accept',
}->
firewall { '002 accept related established rules':
proto => 'all',
ctstate => ['RELATED', 'ESTABLISHED'],
action => 'accept',
}
}
```

The rules in `pre` should allow basic networking (such as ICMP and TCP), as well as ensure that existing connections are not closed.

The `post` class should be located in `my_fw/manifests/post.pp` and include any default rules to be applied last.
and a `post` class that includes any default rules to be applied last:

class my_fw::post {
firewall { '999 drop all':
proto => 'all',
action => 'drop',
before => undef,
}
}
```puppet
class my_fw::post {
firewall { '999 drop all':
proto => 'all',
action => 'drop',
before => undef,
}
}
```

To put it all together: the `require` parameter in `Firewall {}` ensures `my_fw::pre` is run before any other rules and the `before` parameter ensures `my_fw::post` is run after any other rules. So the run order is:

* run the rules in `my_fw::pre`
* run your rules (defined in code)
* run the rules in `my_fw::post`

###Upgrading

####Upgrading from version 0.2.0 and newer

Upgrade the module with the puppet module tool as normal:

puppet module upgrade puppetlabs/firewall
With the latest version, we now have an auto require base on alphabetical order of rules name. So you no longer need to setup & define pre & post rules.

####Upgrading from version 0.1.1 and older

Expand All @@ -131,37 +168,41 @@ Start by upgrading the module using the puppet module tool:

Previously, you would have required the following in your `site.pp` (or some other global location):

# 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,
}
Firewall {
notify => Exec['persist-firewall'],
before => Class['my_fw::post'],
require => Class['my_fw::pre'],
}
Firewallchain {
notify => Exec['persist-firewall'],
}
resources { "firewall":
purge => true
}
```puppet
# 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,
}
Firewall {
notify => Exec['persist-firewall'],
before => Class['my_fw::post'],
require => Class['my_fw::pre'],
}
Firewallchain {
notify => Exec['persist-firewall'],
}
resources { "firewall":
purge => true
}
```

With the latest version, we now have in-built persistence, so this is no longer needed. However, you will still need some basic setup to define pre & post rules.

resources { "firewall":
purge => true
}
Firewall {
before => Class['my_fw::post'],
require => Class['my_fw::pre'],
}
class { ['my_fw::pre', 'my_fw::post']: }
class { 'firewall': }
```puppet
resources { "firewall":
purge => true
}
Firewall {
before => Class['my_fw::post'],
require => Class['my_fw::pre'],
}
class { ['my_fw::pre', 'my_fw::post']: }
class { 'firewall': }
```

Consult the the documentation below for more details around the classes `my_fw::pre` and `my_fw::post`.

Expand All @@ -176,24 +217,26 @@ All rules employ a numbering system in the resource's title that is used for ord

###Default rules

You can place default rules in either `my_fw::pre` or `my_fw::post`, depending on when you would like them to run. Rules placed in the `pre` class will run first, rules in the `post` class, last.

Depending on the provider, the title of the rule can be stored using the comment feature of the underlying firewall subsystem. Values can match `/^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/`.

####Examples of default rules

Basic accept ICMP request example:

firewall { "000 accept all icmp requests":
proto => "icmp",
action => "accept",
}
```puppet
firewall { "000 accept all icmp requests":
proto => "icmp",
action => "accept",
}
```

Drop all:

firewall { "999 drop all other requests":
action => "drop",
}
```puppet
firewall { "999 drop all other requests":
action => "drop",
}
```

###Application-specific rules

Expand Down Expand Up @@ -243,7 +286,7 @@ include profile::apache
Or the module, if you're not using roles and profiles:

```puppet
include ::apache
include ::apache
```

Then they would automatically get appropriate firewall rules.
Expand All @@ -252,39 +295,45 @@ Then they would automatically get appropriate firewall rules.

You can also apply firewall rules to specific nodes. Usually, you will want to put the firewall rule in another class and apply that class to a node. But you can apply a rule to a node.

node 'foo.bar.com' {
firewall { '111 open port 111':
dport => 111
}
}
```puppet
node 'foo.bar.com' {
firewall { '111 open port 111':
dport => 111
}
}
```

You can also do more complex things with the `firewall` resource. Here we are doing some NAT configuration.

firewall { '100 snat for network foo2':
chain => 'POSTROUTING',
jump => 'MASQUERADE',
proto => 'all',
outiface => "eth0",
source => '10.1.2.0/24',
table => 'nat',
}
```puppet
firewall { '100 snat for network foo2':
chain => 'POSTROUTING',
jump => 'MASQUERADE',
proto => 'all',
outiface => "eth0",
source => '10.1.2.0/24',
table => 'nat',
}
```

In the below example, we are creating a new chain and forwarding any port 5000 access to it.

firewall { '100 forward to MY_CHAIN':
chain => 'INPUT',
jump => 'MY_CHAIN',
}
# The namevar here is in the format chain_name:table:protocol
firewallchain { 'MY_CHAIN:filter:IPv4':
ensure => present,
}
firewall { '100 my rule':
chain => 'MY_CHAIN',
action => 'accept',
proto => 'tcp',
dport => 5000,
}
```puppet
firewall { '100 forward to MY_CHAIN':
chain => 'INPUT',
jump => 'MY_CHAIN',
}
# The namevar here is in the format chain_name:table:protocol
firewallchain { 'MY_CHAIN:filter:IPv4':
ensure => present,
}
firewall { '100 my rule':
chain => 'MY_CHAIN',
action => 'accept',
proto => 'tcp',
dport => 5000,
}
```

###Additional Information

Expand Down Expand Up @@ -324,7 +373,9 @@ At the moment this takes care of:

You should include the class for nodes that need to use the resources in this module. For example

class { 'firewall': }
```puppet
class { 'firewall': }
```

####`ensure`

Expand Down
7 changes: 6 additions & 1 deletion lib/puppet/provider/firewall/ip6tables.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@doc = "Ip6tables type provider"

has_feature :iptables
has_feature :connection_limiting
has_feature :hop_limiting
has_feature :rate_limiting
has_feature :recent_limiting
Expand Down Expand Up @@ -46,6 +47,9 @@ def self.iptables_save(*args)

@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",
Expand Down Expand Up @@ -126,6 +130,7 @@ def self.iptables_save(*args)
:proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :gid, :uid, :sport, :dport,
:port, :pkttype, :name, :state, :ctstate, :icmp, :hop_limit, :limit, :burst,
:recent, :rseconds, :reap, :rhitcount, :rttl, :rname, :rsource, :rdest,
:jump, :todest, :tosource, :toports, :log_level, :log_prefix, :reject]
:jump, :todest, :tosource, :toports, :log_level, :log_prefix, :reject,
:connlimit_above, :connlimit_mask, :connmark]

end
99 changes: 53 additions & 46 deletions lib/puppet/provider/firewall/iptables.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@doc = "Iptables type provider"

has_feature :iptables
has_feature :connection_limiting
has_feature :rate_limiting
has_feature :recent_limiting
has_feature :snat
Expand All @@ -27,6 +28,7 @@
has_feature :iprange
has_feature :ipsec_dir
has_feature :ipsec_policy
has_feature :mask

optional_commands({
:iptables => 'iptables',
Expand All @@ -45,50 +47,54 @@
@protocol = "IPv4"

@resource_map = {
:burst => "--limit-burst",
: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",
: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',
}

# These are known booleans that do not take a value, but we want to munge
Expand Down Expand Up @@ -140,8 +146,9 @@
:src_range, :dst_range, :tcp_flags, :gid, :uid, :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, :rsource, :rdest, :jump, :todest, :tosource,
:toports, :random, :log_prefix, :log_level, :reject, :set_mark
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :jump, :todest,
:tosource, :toports, :random, :log_prefix, :log_level, :reject, :set_mark,
:connlimit_above, :connlimit_mask, :connmark
]

def insert
Expand Down
70 changes: 68 additions & 2 deletions lib/puppet/type/firewall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
installed.
EOS

feature :connection_limiting, "Connection limiting features."
feature :hop_limiting, "Hop limiting features."
feature :rate_limiting, "Rate limiting features."
feature :recent_limiting, "The netfilter recent module"
Expand All @@ -40,7 +41,7 @@
feature :reject_type, "The ability to control reject messages"
feature :log_level, "The ability to control the log level"
feature :log_prefix, "The ability to add prefixes to log messages"
feature :mark, "Set the netfilter mark value associated with the packet"
feature :mark, "Match or Set the netfilter mark value associated with the packet"
feature :tcp_flags, "The ability to match on particular TCP flag settings"
feature :pkttype, "Match a packet type"
feature :socket, "Match open sockets"
Expand Down Expand Up @@ -605,6 +606,50 @@ def should_to_s(value)
end


# Connection mark
newproperty(:connmark, :required_features => :mark) do
desc <<-EOS
Match the Netfilter mark value associated with the packet. Accepts either of:
mark/mask or mark. These will be converted to hex if they are not already.
EOS
munge do |value|
int_or_hex = '[a-fA-F0-9x]'
match = value.to_s.match("(#{int_or_hex}+)(/)?(#{int_or_hex}+)?")
mark = @resource.to_hex32(match[1])

# Values that can't be converted to hex.
# Or contain a trailing slash with no mask.
if mark.nil? or (mark and match[2] and match[3].nil?)
raise ArgumentError, "MARK value must be integer or hex between 0 and 0xffffffff"
end

# There should not be a mask on connmark
unless match[3].nil?
raise ArgumentError, "iptables does not support masks on MARK match rules"
end
value = mark

value
end
end

# Connection limiting properties
newproperty(:connlimit_above, :required_features => :connection_limiting) do
desc <<-EOS
Connection limiting value for matched connections above n.
EOS
newvalue(/^\d+$/)
end

newproperty(:connlimit_mask, :required_features => :connection_limiting) do
desc <<-EOS
Connection limiting by subnet mask for matched connections.
IPv4: 0-32
IPv6: 0-128
EOS
newvalue(/^\d+$/)
end

# Hop limiting properties
newproperty(:hop_limit, :required_features => :hop_limiting) do
desc <<-EOS
Expand Down Expand Up @@ -777,6 +822,8 @@ def should_to_s(value)
attribute. When used, this will cause entries older than 'seconds' to be
purged. Must be boolean true.
EOS

newvalues(:true, :false)
end

newproperty(:rhitcount, :required_features => :recent_limiting) do
Expand Down Expand Up @@ -852,6 +899,12 @@ def should_to_s(value)
newvalues(:in, :out)
end

newproperty(:mask, :required_features => :mask) do
desc <<-EOS
Sets the mask to use when `recent` is enabled.
EOS
end

newparam(:line) do
desc <<-EOS
Read-only property for caching the rule line.
Expand Down Expand Up @@ -890,6 +943,10 @@ def should_to_s(value)
end
end

autorequire(:firewall) do
catalog.resources.select { |r| (r.class.to_s == "Puppet::Type::Firewall") and (r.name <=> name) == -1 }.sort.last
end

validate do
debug("[validate]")

Expand Down Expand Up @@ -981,7 +1038,7 @@ def should_to_s(value)
end

unless value(:tosource)
self.fail "Parameter jump => DNAT must have tosource parameter"
self.fail "Parameter jump => SNAT must have tosource parameter"
end
end

Expand Down Expand Up @@ -1011,5 +1068,14 @@ def should_to_s(value)
if value(:action) && value(:jump)
self.fail "Only one of the parameters 'action' and 'jump' can be set"
end

if value(:connlimit_mask) && ! value(:connlimit_above)
self.fail "Parameter 'connlimit_mask' requires 'connlimit_above'"
end

if value(:mask) && ! value(:recent)
self.fail "Mask can only be set if recent is enabled."
end

end
end
11 changes: 8 additions & 3 deletions lib/puppet/util/firewall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,16 @@ def persist_iptables(proto)
end
end

# Fedora 15 and newer use systemd for to persist iptable rules
# Fedora 15 and newer use systemd to persist iptable rules
if os_key == 'RedHat' && Facter.value(:operatingsystem) == 'Fedora' && Facter.value(:operatingsystemrelease).to_i >= 15
os_key = 'Fedora'
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
os_key = 'Fedora'
end

cmd = case os_key.to_sym
when :RedHat
case proto.to_sym
Expand All @@ -182,9 +187,9 @@ def persist_iptables(proto)
when :Fedora
case proto.to_sym
when :IPv4
%w{/usr/libexec/iptables.init save}
%w{/usr/libexec/iptables/iptables.init save}
when :IPv6
%w{/usr/libexec/ip6tables.init save}
%w{/usr/libexec/iptables/ip6tables.init save}
end
when :Debian
case proto.to_sym
Expand Down
16 changes: 16 additions & 0 deletions manifests/linux/redhat.pp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@
$ensure = running,
$enable = true
) {

# 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 (( $::operatingsystemrelease =~ /^\d+/ and $::operatingsystemrelease >= 15 ) or $::operatingsystemrelease == "Rawhide")) {
package { 'iptables-services':
ensure => present,
}
}

service { 'iptables':
ensure => $ensure,
enable => $enable,
Expand Down
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "puppetlabs-firewall",
"version": "1.0.2",
"version": "1.1.0",
"source": "https://github.com/puppetlabs/puppetlabs-firewall",
"author": "Puppet Labs",
"license": "Apache-2.0",
Expand Down
55 changes: 55 additions & 0 deletions spec/acceptance/connlimit_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
require 'spec_helper_acceptance'

describe 'firewall type' do

describe 'connlimit_above' do
context '10' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '500 - test':
proto => tcp,
dport => '22',
connlimit_above => '10',
action => reject,
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should contain the rule' do
shell('iptables-save') do |r|
#connlimit-saddr is added in Ubuntu 14.04.
expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "500 - test" -m connlimit --connlimit-above 10 --connlimit-mask 32 (--connlimit-saddr )?-j REJECT --reject-with icmp-port-unreachable/)
end
end
end
end

describe 'connlimit_mask' do
context '24' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '501 - test':
proto => tcp,
dport => '22',
connlimit_above => '10',
connlimit_mask => '24',
action => reject,
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should contain the rule' do
shell('iptables-save') do |r|
#connlimit-saddr is added in Ubuntu 14.04.
expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "501 - test" -m connlimit --connlimit-above 10 --connlimit-mask 24 (--connlimit-saddr )?-j REJECT --reject-with icmp-port-unreachable/)
end
end
end
end
end
27 changes: 27 additions & 0 deletions spec/acceptance/connmark_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require 'spec_helper_acceptance'

describe 'firewall type' do

describe 'connmark' do
context '50' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '502 - test':
proto => 'all',
connmark => '0x1',
action => reject,
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A INPUT -m comment --comment "502 - test" -m connmark --mark 0x1 -j REJECT --reject-with icmp-port-unreachable/)
end
end
end
end
end
5 changes: 3 additions & 2 deletions spec/acceptance/firewall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1524,7 +1524,8 @@ class { '::firewall': }

it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A INPUT -d 30.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "597 - test" -m recent --set --name list1 --rdest/)
# Mask added as of Ubuntu 14.04.
expect(r.stdout).to match(/-A INPUT -d 30.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "597 - test" -m recent --set --name list1 (--mask 255.255.255.255 )?--rdest/)
end
end
end
Expand Down Expand Up @@ -1553,7 +1554,7 @@ class { '::firewall': }

it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A INPUT -d 30.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "598 - test" -m recent --rcheck --seconds 60 --hitcount 5 --rttl --name list1 --rsource/)
expect(r.stdout).to match(/-A INPUT -d 30.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "598 - test" -m recent --rcheck --seconds 60 --hitcount 5 --rttl --name list1 (--mask 255.255.255.255 )?--rsource/)
end
end
end
Expand Down
9 changes: 9 additions & 0 deletions spec/acceptance/nodesets/ubuntu-server-1404-x64.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
HOSTS:
ubuntu-server-1404-x64:
roles:
- master
platform: ubuntu-14.04-64
box: puppetlabs/ubuntu-14.04-64-nocm
hypervisor : vagrant
CONFIG:
type: foss
63 changes: 63 additions & 0 deletions spec/fixtures/iptables/conversion_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,36 @@
:dport => ["20443"],
},
},
'connlimit_above' => {
:line => '-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "061 REJECT connlimit_above 10" -m connlimit --connlimit-above 10 --connlimit-mask 32 -j REJECT --reject-with icmp-port-unreachable',
:table => 'filter',
:params => {
:proto => 'tcp',
:dport => ["22"],
:connlimit_above => '10',
:action => 'reject',
},
},
'connlimit_above_with_connlimit_mask' => {
:line => '-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "061 REJECT connlimit_above 10 with mask 24" -m connlimit --connlimit-above 10 --connlimit-mask 24 -j REJECT --reject-with icmp-port-unreachable',
:table => 'filter',
:params => {
:proto => 'tcp',
:dport => ["22"],
:connlimit_above => '10',
:connlimit_mask => '24',
:action => 'reject',
},
},
'connmark' => {
:line => '-A INPUT -m comment --comment "062 REJECT connmark" -m connmark --mark 0x1 -j REJECT --reject-with icmp-port-unreachable',
:table => 'filter',
:params => {
:proto => 'all',
:connmark => '0x1',
:action => 'reject',
},
},
}

# This hash is for testing converting a hash to an argument line.
Expand Down Expand Up @@ -868,4 +898,37 @@
},
:args => ['-t', :filter, '-p', :all, '-m', 'comment', '--comment', '050 testcomment-with-fdashf', '-j', 'ACCEPT'],
},
'connlimit_above' => {
:params => {
:name => '061 REJECT connlimit_above 10',
:table => 'filter',
:proto => 'tcp',
:dport => ["22"],
:connlimit_above => '10',
:action => 'reject',
},
:args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "22", "-m", "comment", "--comment", "061 REJECT connlimit_above 10", "-j", "REJECT", "-m", "connlimit", "--connlimit-above", "10"],
},
'connlimit_above_with_connlimit_mask' => {
:params => {
:name => '061 REJECT connlimit_above 10 with mask 24',
:table => 'filter',
:proto => 'tcp',
:dport => ["22"],
:connlimit_above => '10',
:connlimit_mask => '24',
:action => 'reject',
},
:args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "22", "-m", "comment", "--comment", "061 REJECT connlimit_above 10 with mask 24", "-j", "REJECT", "-m", "connlimit", "--connlimit-above", "10", "--connlimit-mask", "24"],
},
'connmark' => {
:params => {
:name => '062 REJECT connmark',
:table => 'filter',
:proto => 'all',
:connmark => '0x1',
:action => 'reject',
},
:args => ["-t", :filter, "-p", :all, "-m", "comment", "--comment", "062 REJECT connmark", "-j", "REJECT", "-m", "connmark", "--mark", "0x1"],
},
}
16 changes: 7 additions & 9 deletions spec/spec_helper_acceptance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@ def ip6tables_flush_all_tables
end
end

unless ENV['RS_PROVISION'] == 'no'
unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no'
if hosts.first.is_pe?
install_pe
else
install_puppet
end
hosts.each do |host|
# Install Puppet
if host.is_pe?
install_pe
else
install_package host, 'rubygems'
on host, 'gem install puppet --no-ri --no-rdoc'
on host, "mkdir -p #{host['distmoduledir']}"
end
on host, "mkdir -p #{host['distmoduledir']}"
end
end

Expand Down
10 changes: 8 additions & 2 deletions spec/unit/classes/firewall_linux_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@
context 'RedHat like' do
%w{RedHat CentOS Fedora}.each do |os|
context "operatingsystem => #{os}" do
let(:facts) { facts_default.merge({ :operatingsystem => os }) }
it { should contain_class('firewall::linux::redhat').with_require('Package[iptables]') }
releases = (os == 'Fedora' ? [14,15,'Rawhide'] : [6,7])
releases.each do |osrel|
context "operatingsystemrelease => #{osrel}" do
let(:facts) { facts_default.merge({ :operatingsystem => os,
:operatingsystemrelease => osrel}) }
it { should contain_class('firewall::linux::redhat').with_require('Package[iptables]') }
end
end
end
end
end
Expand Down
15 changes: 13 additions & 2 deletions spec/unit/puppet/util/firewall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,30 @@
describe 'when proto is IPv4' do
let(:proto) { 'IPv4' }

it 'should exec for RedHat identified from osfamily' do
it 'should exec /sbin/service if running RHEL 6 or earlier' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat')
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat')
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('6')

expect(subject).to receive(:execute).with(%w{/sbin/service iptables save})
subject.persist_iptables(proto)
end

it 'should exec for systemd if running RHEL 7 or greater' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat')
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat')
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('7')

expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables/iptables.init save})
subject.persist_iptables(proto)
end

it 'should exec for systemd if running Fedora 15 or greater' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat')
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Fedora')
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('15')

expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables.init save})
expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables/iptables.init save})
subject.persist_iptables(proto)
end

Expand All @@ -149,6 +159,7 @@
it 'should raise a warning when exec fails' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat')
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat')
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('6')

expect(subject).to receive(:execute).with(%w{/sbin/service iptables save}).
and_raise(Puppet::ExecutionFailure, 'some error')
Expand Down