216 changes: 134 additions & 82 deletions lib/puppet/provider/firewall/iptables.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
has_feature :ipsec_policy
has_feature :mask
has_feature :ipset
has_feature :clusterip

optional_commands({
:iptables => 'iptables',
Expand All @@ -41,7 +42,7 @@
defaultfor :kernel => :linux
confine :kernel => :linux

iptables_version = Facter.fact('iptables_version').value
iptables_version = Facter.value('iptables_version')
if (iptables_version and Puppet::Util::Package.versioncmp(iptables_version, '1.4.1') < 0)
mark_flag = '--set-mark'
else
Expand All @@ -51,78 +52,86 @@
@protocol = "IPv4"

@resource_map = {
:burst => "--limit-burst",
:checksum_fill => "--checksum-fill",
:clamp_mss_to_pmtu => "--clamp-mss-to-pmtu",
: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 => "--dst-range",
:dst_type => "--dst-type",
:gateway => "--gateway",
:gid => "--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',
:match_mark => "-m mark --mark",
:mss => '-m tcpmss --mss',
: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,
:set_mss => '--set-mss',
:socket => "-m socket",
:source => "-s",
:sport => ["-m multiport --sports", "--sport"],
:src_range => "--src-range",
:src_type => "--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",
:to => "--to",
:uid => "--uid-owner",
:physdev_in => "--physdev-in",
:physdev_out => "--physdev-out",
:physdev_is_bridged => "--physdev-is-bridged",
:date_start => "--datestart",
:date_stop => "--datestop",
:time_start => "--timestart",
:time_stop => "--timestop",
:month_days => "--monthdays",
:week_days => "--weekdays",
:time_contiguous => "--contiguous",
:kernel_timezone => "--kerneltz",
:burst => "--limit-burst",
:checksum_fill => "--checksum-fill",
:clamp_mss_to_pmtu => "--clamp-mss-to-pmtu",
: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 => "--dst-range",
:dst_type => "--dst-type",
:gateway => "--gateway",
:gid => "--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',
:match_mark => "-m mark --mark",
:mss => '-m tcpmss --mss',
: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_dscp => '--set-dscp',
:set_dscp_class => '--set-dscp-class',
:set_mark => mark_flag,
:set_mss => '--set-mss',
:socket => "-m socket",
:source => "-s",
:sport => ["-m multiport --sports", "--sport"],
:src_range => "--src-range",
:src_type => "--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",
:to => "--to",
:uid => "--uid-owner",
:physdev_in => "--physdev-in",
:physdev_out => "--physdev-out",
:physdev_is_bridged => "--physdev-is-bridged",
:date_start => "--datestart",
:date_stop => "--datestop",
:time_start => "--timestart",
:time_stop => "--timestop",
:month_days => "--monthdays",
:week_days => "--weekdays",
:time_contiguous => "--contiguous",
:kernel_timezone => "--kerneltz",
:clusterip_new => "--new",
:clusterip_hashmode => "--hashmode",
:clusterip_clustermac => "--clustermac",
:clusterip_total_nodes => "--total-nodes",
:clusterip_local_node => "--local-node",
:clusterip_hash_init => "--hash-init",
}

# These are known booleans that do not take a value, but we want to munge
Expand All @@ -140,6 +149,7 @@
:physdev_is_bridged,
:time_contiguous,
:kernel_timezone,
:clusterip_new,
]

# Properties that use "-m <ipt module name>" (with the potential to have multiple
Expand Down Expand Up @@ -242,9 +252,11 @@ def munge_resource_map_from_resource(resource_map_original, compare)
:src_range, :dst_range, :tcp_flags, :uid, :gid, :mac_source, :sport, :dport, :port,
:src_type, :dst_type, :socket, :pkttype, :name, :ipsec_dir, :ipsec_policy,
:state, :ctstate, :icmp, :limit, :burst, :recent, :rseconds, :reap,
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :jump, :clamp_mss_to_pmtu, :gateway, :set_mss, :todest,
:tosource, :toports, :to, :checksum_fill, :random, :log_prefix, :log_level, :reject, :set_mark, :match_mark, :mss,
:connlimit_above, :connlimit_mask, :connmark, :time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :jump, :clusterip_new, :clusterip_hashmode,
:clusterip_clustermac, :clusterip_total_nodes, :clusterip_local_node, :clusterip_hash_init,
:clamp_mss_to_pmtu, :gateway, :set_mss, :set_dscp, :set_dscp_class, :todest, :tosource, :toports, :to, :checksum_fill, :random, :log_prefix,
:log_level, :reject, :set_mark, :match_mark, :mss, :connlimit_above, :connlimit_mask, :connmark, :time_start, :time_stop,
:month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone
]

def insert
Expand Down Expand Up @@ -319,6 +331,8 @@ def self.rule_to_hash(line, table, counter)
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.gsub(/(?!-m tcp --tcp-flags)-m (tcp|udp) /, '')
# There is a bug in EL5 which puts 2 spaces before physdev, so we fix it
values = values.gsub(/\s{2}--physdev/, ' --physdev')
# '--pol ipsec' takes many optional arguments; we cheat again by adding " around them
values = values.sub(/
--pol\sipsec
Expand Down Expand Up @@ -382,8 +396,16 @@ def self.rule_to_hash(line, table, counter)
values.slice!('-A')
keys << :chain

# Here we generate the main hash
keys.zip(values.scan(/"[^"]*"|\S+/).reverse) { |f, v| hash[f] = v.gsub(/"/, '') }
# Here we generate the main hash by scanning arguments off the values
# string, handling any quoted characters present in the value, and then
# zipping the values with the array of keys.
keys.zip(values.scan(/("([^"\\]|\\.)*"|\S+)/).transpose[0].reverse) do |f, v|
if v =~ /^".*"$/ then
hash[f] = v.sub(/^"(.*)"$/, '\1').gsub(/\\(\\|'|")/, '\1')
else
hash[f] = v.dup
end
end

#####################
# POST PARSE CLUDGING
Expand All @@ -392,6 +414,37 @@ def self.rule_to_hash(line, table, counter)
[:dport, :sport, :port, :state, :ctstate].each do |prop|
hash[prop] = hash[prop].split(',') if ! hash[prop].nil?
end

## clean up DSCP class to HEX mappings
valid_dscp_classes = {
'0x0a' => 'af11',
'0x0c' => 'af12',
'0x0e' => 'af13',
'0x12' => 'af21',
'0x14' => 'af22',
'0x16' => 'af23',
'0x1a' => 'af31',
'0x1c' => 'af32',
'0x1e' => 'af33',
'0x22' => 'af41',
'0x24' => 'af42',
'0x26' => 'af43',
'0x08' => 'cs1',
'0x10' => 'cs2',
'0x18' => 'cs3',
'0x20' => 'cs4',
'0x28' => 'cs5',
'0x30' => 'cs6',
'0x38' => 'cs7',
'0x2e' => 'ef'
}
[:set_dscp_class].each do |prop|
[:set_dscp].each do |dmark|
next unless hash[dmark]
hash[prop] = valid_dscp_classes[hash[dmark]]
end
end


# Convert booleans removing the previous cludge we did
@known_booleans.each do |bool|
Expand Down Expand Up @@ -462,9 +515,9 @@ def self.rule_to_hash(line, table, counter)
if ! hash[:name]
num = 9000 + counter
hash[:name] = "#{num} #{Digest::MD5.hexdigest(line)}"
elsif not /^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/ =~ hash[:name]
elsif not /^\d+[[:graph:][:space:]]+$/ =~ hash[:name]
num = 9000 + counter
hash[:name] = "#{num} #{/([[:alpha:][:digit:][:punct:][:space:]]+)/.match(hash[:name])[1]}"
hash[:name] = "#{num} #{/([[:graph:][:space:]]+)/.match(hash[:name])[1]}"
end

# Iptables defaults to log_level '4', so it is omitted from the output of iptables-save.
Expand Down Expand Up @@ -495,7 +548,6 @@ def self.rule_to_hash(line, table, counter)
hash[:action] = hash[:jump].downcase
hash.delete(:jump)
end

hash
end

Expand All @@ -515,7 +567,7 @@ def update_args

def delete_args
# Split into arguments
line = properties[:line].gsub(/\-A /, '-D ').split(/\s(?=(?:[^"]|"[^"]*")*$)/).map{|v| v.gsub(/"/, '')}
line = properties[:line].gsub(/^\-A /, '-D ').split(/\s(?=(?:[^"]|"[^"]*")*$)/).map{|v| v.gsub(/"/, '')}
line.unshift("-t", properties[:table])
end

Expand Down
110 changes: 103 additions & 7 deletions lib/puppet/type/firewall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
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"
feature :clusterip, "Configure a simple cluster of nodes that share a certain IP and MAC address without an explicit load balancer in front of them."

# provider specific features
feature :iptables, "The provider provides iptables features."
Expand Down Expand Up @@ -91,7 +92,7 @@
isnamevar

# Keep rule names simple - they must start with a number
newvalues(/^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/)
newvalues(/^\d+[[:graph:][:space:]]+$/)
end

newproperty(:action) do
Expand Down Expand Up @@ -321,8 +322,10 @@ def should_to_s(value)
* XRESOLVE - undocumented
EOS

newvalues(:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST,
:BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE)
newvalues(*[:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST,
:BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE].collect do |address_type|
[address_type, "! #{address_type}".to_sym]
end.flatten)
end

newproperty(:src_type, :required_features => :address_type) do
Expand All @@ -347,8 +350,10 @@ def should_to_s(value)
* XRESOLVE - undocumented
EOS

newvalues(:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST,
:BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE)
newvalues(*[:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST,
:BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE].collect do |address_type|
[address_type, "! #{address_type}".to_sym]
end.flatten)
end

newproperty(:proto) do
Expand Down Expand Up @@ -836,7 +841,7 @@ def insync?(is)
end

# Old iptables does not support a mask. New iptables will expect one.
iptables_version = Facter.fact('iptables_version').value
iptables_version = Facter.value('iptables_version')
mask_required = (iptables_version and Puppet::Util::Package.versioncmp(iptables_version, '1.4.1') >= 0)

if mask_required
Expand Down Expand Up @@ -868,6 +873,30 @@ def insync?(is)
newvalues(:true, :false)
end

newproperty(:set_dscp, :required_features => :iptables) do
desc <<-EOS
Set DSCP Markings.
EOS
end

newproperty(:set_dscp_class, :required_features => :iptables) do
desc <<-EOS
This sets the DSCP field according to a predefined DiffServ class.
EOS
# iptables uses the cisco DSCP classes as the basis for this flag. Values may be found here:
# 'http://www.cisco.com/c/en/us/support/docs/quality-of-service-qos/qos-packet-marking/10103-dscpvalues.html'
valid_codes = [
'af11','af12','af13','af21','af22','af23','af31','af32','af33','af41',
'af42','af43','cs1','cs2','cs3','cs4','cs5','cs6','cs7','ef'
]
munge do |value|
unless valid_codes.include? value.downcase
raise ArgumentError, "#{value} is not a valid DSCP Class"
end
value.downcase
end
end

newproperty(:set_mss, :required_features => :iptables) do
desc <<-EOS
Sets the TCP MSS value for packets.
Expand Down Expand Up @@ -1255,6 +1284,58 @@ def insync?(is)
newvalues(:true, :false)
end

newproperty(:clusterip_new, :required_features => :clusterip) do
desc <<-EOS
Used with the CLUSTERIP jump target.
Create a new ClusterIP. You always have to set this on the first rule for a given ClusterIP.
EOS

newvalues(:true, :false)
end

newproperty(:clusterip_hashmode, :required_features => :clusterip) do
desc <<-EOS
Used with the CLUSTERIP jump target.
Specify the hashing mode. Valid values: sourceip, sourceip-sourceport, sourceip-sourceport-destport.
EOS

newvalues(:sourceip, :'sourceip-sourceport', :'sourceip-sourceport-destport')
end

newproperty(:clusterip_clustermac, :required_features => :clusterip) do
desc <<-EOS
Used with the CLUSTERIP jump target.
Specify the ClusterIP MAC address. Has to be a link-layer multicast address.
EOS

newvalues(/^([0-9a-f]{2}[:]){5}([0-9a-f]{2})$/i)
end

newproperty(:clusterip_total_nodes, :required_features => :clusterip) do
desc <<-EOS
Used with the CLUSTERIP jump target.
Number of total nodes within this cluster.
EOS

newvalues(/\d+/)
end

newproperty(:clusterip_local_node, :required_features => :clusterip) do
desc <<-EOS
Used with the CLUSTERIP jump target.
Specify the random seed used for hash initialization.
EOS

newvalues(/\d+/)
end

newproperty(:clusterip_hash_init, :required_features => :clusterip) do
desc <<-EOS
Used with the CLUSTERIP jump target.
Specify the random seed used for hash initialization.
EOS
end


autorequire(:firewallchain) do
reqs = []
Expand Down Expand Up @@ -1282,7 +1363,16 @@ def insync?(is)
autorequire(:package) do
case value(:provider)
when :iptables, :ip6tables
%w{iptables iptables-persistent netfilter-persistent iptables-services}
%w{iptables iptables-persistent iptables-services}
else
[]
end
end

autorequire(:service) do
case value(:provider)
when :iptables, :ip6tables
%w{firewalld iptables ip6tables iptables-persistent netfilter-persistent}
else
[]
end
Expand Down Expand Up @@ -1348,6 +1438,12 @@ def insync?(is)
end
end

if value(:jump).to_s == "DSCP"
unless value(:set_dscp) || value(:set_dscp_class)
self.fail "When using jump => DSCP, the set_dscp or set_dscp_class property is required"
end
end

if value(:jump).to_s == "TCPMSS"
unless value(:set_mss) || value(:clamp_mss_to_pmtu)
self.fail "When using jump => TCPMSS, the set_mss or clamp_mss_to_pmtu property is required"
Expand Down
9 changes: 9 additions & 0 deletions lib/puppet/type/firewallchain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,15 @@
end
end

autorequire(:service) do
case value(:provider)
when :iptables, :ip6tables
%w{firewalld iptables ip6tables iptables-persistent netfilter-persistent}
else
[]
end
end

validate do
debug("[validate]")

Expand Down
8 changes: 7 additions & 1 deletion manifests/linux/debian.pp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@
) inherits ::firewall::params {

if $package_name {
#Fixes hang while installing iptables-persistent on debian 8
exec {'iptables-persistent-debconf':
command => "/bin/echo \"${package_name} ${package_name}/autosave_v4 boolean false\" | /usr/bin/debconf-set-selections && /bin/echo \"${package_name} ${package_name}/autosave_v6 boolean false\" | /usr/bin/debconf-set-selections",
refreshonly => true
}
package { $package_name:
ensure => present,
ensure => present,
require => Exec['iptables-persistent-debconf']
}
}

Expand Down
17 changes: 13 additions & 4 deletions manifests/linux/redhat.pp
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,19 @@
require => File['/etc/sysconfig/iptables'],
}

# Redhat 7 selinux user context for /etc/sysconfig/iptables is set to unconfined_u
case $::selinux {
#lint:ignore:quoted_booleans
'true',true: { $seluser = 'unconfined_u' }
#lint:endignore
default: { $seluser = undef }
}

file { '/etc/sysconfig/iptables':
ensure => present,
owner => 'root',
group => 'root',
mode => '0600',
ensure => present,
owner => 'root',
group => 'root',
mode => '0600',
seluser => $seluser,
}
}
6 changes: 3 additions & 3 deletions metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "puppetlabs-firewall",
"version": "1.6.0",
"version": "1.7.0",
"author": "Puppet Labs",
"summary": "Manages Firewalls such as iptables",
"license": "Apache-2.0",
Expand Down Expand Up @@ -72,11 +72,11 @@
"requirements": [
{
"name": "pe",
"version_requirement": "3.x"
"version_requirement": ">= 3.0.0 < 2015.3.0"
},
{
"name": "puppet",
"version_requirement": "3.x"
"version_requirement": ">= 3.0.0 < 5.0.0"
}
],
"dependencies": [
Expand Down
12 changes: 9 additions & 3 deletions spec/acceptance/class_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@

# 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
if do_catch_changes
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
end
end

it 'ensure => stopped:' do
pp = "class { 'firewall': ensure => stopped }"

# 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
if do_catch_changes
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
end
end

it 'ensure => running:' do
pp = "class { 'firewall': ensure => running }"

# 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
if do_catch_changes
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
end
end
end
91 changes: 51 additions & 40 deletions spec/acceptance/connlimit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,63 @@

describe 'firewall type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) 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
describe 'reset' do
it 'deletes all iptables rules' do
shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush')
end
it 'deletes all ip6tables rules' do
shell('ip6tables --flush; ip6tables -t nat --flush; ip6tables -t mangle --flush')
end
end

if default['platform'] !~ /sles-10/
describe 'connlimit_above' do
context '10' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '500 - test':
proto => tcp,
dport => '2222',
connlimit_above => '10',
action => reject,
}
EOS

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/)
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 2222 -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
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
describe 'connlimit_mask' do
context '24' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '501 - test':
proto => tcp,
dport => '2222',
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/)
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 2222 -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
Expand Down
60 changes: 16 additions & 44 deletions spec/acceptance/firewall_bridging_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ class { '::firewall': }
EOS

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

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

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

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

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

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

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

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

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

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

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

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

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

it 'should contain the rule' do
Expand All @@ -202,7 +188,7 @@ class { '::firewall': }
end

#iptables version 1.3.5 is not suppored by the ip6tables provider
if default['platform'] !~ /el-5/
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'ip6tables physdev tests' do
context 'physdev_in eth0' do
it 'applies' do
Expand All @@ -219,9 +205,7 @@ class { '::firewall': }
EOS

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

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

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

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

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

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

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

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

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

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

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

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

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

it 'should contain the rule' do
Expand All @@ -400,4 +372,4 @@ class { '::firewall': }
end
end

end
end
45 changes: 45 additions & 0 deletions spec/acceptance/firewall_clusterip_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'spec_helper_acceptance'

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

before(:all) do
shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush')
shell('ip6tables --flush; ip6tables -t nat --flush; ip6tables -t mangle --flush')
end

# SLES doesn't have the CLUSTERIP module
if default['platform'] !~ /sles/
describe 'clusterip' do
context 'cluster ipv4 test' do
it 'applies' do
pending("MODULES-2124 should be resolved for clusterip RHEL7 support") if default['platform'] =~ /el-7/
pp = <<-EOS
class { '::firewall': }
firewall {
'830 - clusterip test':
chain => 'FORWARD',
jump => 'CLUSTERIP',
destination => '1.1.1.1',
iniface => 'eth0',
clusterip_new => true,
clusterip_hashmode => "sourceip",
clusterip_clustermac => "01:00:5E:00:00:00",
clusterip_total_nodes => "2",
clusterip_local_node => "1",
clusterip_hash_init => "1337",
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should contain the rule' do
pending("MODULES-2124 should be resolved for clusterip RHEL7 support") if default['platform'] =~ /el-7/
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -d (1.1.1.1\/32|1.1.1.1) -i eth0 -p tcp -m comment --comment "830 - clusterip test" -j CLUSTERIP --new --hashmode sourceip --clustermac 01:00:5E:00:00:00 --total-nodes 2 --local-node 1 --hash-init 1337/)
end
end
end
end
end
end
118 changes: 118 additions & 0 deletions spec/acceptance/firewall_dscp_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
require 'spec_helper_acceptance'

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

before(:all) do
shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush')
shell('ip6tables --flush; ip6tables -t nat --flush; ip6tables -t mangle --flush')
end

describe 'dscp ipv4 tests' do
context 'set_dscp 0x01' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'1000 - set_dscp':
proto => 'tcp',
jump => 'DSCP',
set_dscp => '0x01',
port => '997',
chain => 'OUTPUT',
table => 'mangle',
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should contain the rule' do
shell('iptables-save -t mangle') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1000 - set_dscp" -j DSCP --set-dscp 0x01/)
end
end
end

context 'set_dscp_class EF' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'1001 EF - set_dscp_class':
proto => 'tcp',
jump => 'DSCP',
port => '997',
set_dscp_class => 'EF',
chain => 'OUTPUT',
table => 'mangle',
}
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 OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1001 EF - set_dscp_class" -j DSCP --set-dscp 0x2e/)
end
end
end
end

if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'dscp ipv6 tests' do
context 'set_dscp 0x01' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'1002 - set_dscp':
proto => 'tcp',
jump => 'DSCP',
set_dscp => '0x01',
port => '997',
chain => 'OUTPUT',
table => 'mangle',
provider => 'ip6tables',
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should contain the rule' do
shell('ip6tables-save -t mangle') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1002 - set_dscp" -j DSCP --set-dscp 0x01/)
end
end
end

context 'set_dscp_class EF' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'1003 EF - set_dscp_class':
proto => 'tcp',
jump => 'DSCP',
port => '997',
set_dscp_class => 'EF',
chain => 'OUTPUT',
table => 'mangle',
provider => 'ip6tables',
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1003 EF - set_dscp_class" -j DSCP --set-dscp 0x2e/)
end
end
end
end
end

end
28 changes: 8 additions & 20 deletions spec/acceptance/firewall_iptmodules_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ class { '::firewall': }
EOS

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

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

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

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

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

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

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

it 'should contain the rule' do
Expand All @@ -148,7 +140,7 @@ class { '::firewall': }
end
# Older OSes don't have addrtype so we leave those properties out.
# el-5 doesn't support ipv6 by default
elsif default['platform'] !~ /el-5/
elsif default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'ip6tables ipt_modules tests' do
context 'all the modules with multiple args' do
it 'applies' do
Expand All @@ -171,9 +163,7 @@ class { '::firewall': }
EOS

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

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

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

it 'should contain the rule' do
Expand All @@ -215,4 +203,4 @@ class { '::firewall': }
end
end

end
end
2 changes: 1 addition & 1 deletion spec/acceptance/firewall_mss_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class { '::firewall': }
end
end

if default['platform'] !~ /el-5/
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'mss ipv6 tests' do
context '1360' do
it 'applies' do
Expand Down
133 changes: 99 additions & 34 deletions spec/acceptance/firewall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,7 @@ class { '::firewall': }
EOS

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

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

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

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

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

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

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

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

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

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

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

it 'should contain the rule' do
Expand Down Expand Up @@ -584,6 +572,28 @@ class { '::firewall': }
end
end

context '! MULTICAST' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '563 - test inversion':
proto => tcp,
action => accept,
#{type} => '! MULTICAST',
}
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end

it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A INPUT -p tcp -m addrtype( !\s.*\sMULTICAST|\s.*\s! MULTICAST) -m comment --comment "563 - test inversion" -j ACCEPT/)
end
end
end

context 'BROKEN' do
it 'fails' do
pp = <<-EOS
Expand Down Expand Up @@ -893,7 +903,7 @@ class { '::firewall': }
end

# RHEL5 does not support --random
if default['platform'] !~ /el-5/
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'random' do
context '192.168.1.1' do
it 'applies' do
Expand All @@ -910,9 +920,7 @@ class { '::firewall': }
EOS

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

it 'should contain the rule' do
Expand Down Expand Up @@ -949,7 +957,7 @@ class { '::firewall': }
end

#iptables version 1.3.5 is not suppored by the ip6tables provider
if default['platform'] !~ /el-5/
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'hop_limit' do
context '5' do
it 'applies' do
Expand Down Expand Up @@ -1191,9 +1199,7 @@ class { '::firewall': }
EOS

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

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

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

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

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

it 'should contain the rule' do
Expand All @@ -1617,6 +1619,29 @@ class { '::firewall': }
end
end

context '! MULTICAST' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '603 - test inversion':
proto => tcp,
action => accept,
#{type} => '! MULTICAST',
provider => 'ip6tables',
}
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end

it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A INPUT -p tcp -m addrtype( !\s.*\sMULTICAST|\s.*\s! MULTICAST) -m comment --comment "603 - test inversion" -j ACCEPT/)
end
end
end

context 'BROKEN' do
it 'fails' do
pp = <<-EOS
Expand Down Expand Up @@ -1776,7 +1801,7 @@ class { '::firewall': }
end

#iptables version 1.3.5 does not support masks on MARK rules
if default['platform'] !~ /el-5/
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'set_mark' do
context '0x3e8/0xffffffff' do
it 'applies' do
Expand Down Expand Up @@ -2192,7 +2217,7 @@ class { '::firewall': }

it 'should contain the rule' do
shell('iptables-save') do |r|
if (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5')
if (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5') or (default['platform'] =~ /sles-10/)
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"/)
Expand Down Expand Up @@ -2305,4 +2330,44 @@ class { '::firewall': }
end
end

context 'comment containing "-A "' do
it 'adds the rule' do
pp = <<-EOS
class { '::firewall': }
firewall { '700 - blah-A Test Rule':
jump => 'LOG',
log_prefix => 'FW-A-INPUT: ',
}
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 -p tcp -m comment --comment "700 - blah-A Test Rule" -j LOG --log-prefix "FW-A-INPUT: "/)
end
end

it 'removes the rule' do
pp = <<-EOS
class { '::firewall': }
firewall { '700 - blah-A Test Rule':
ensure => absent,
jump => 'LOG',
log_prefix => 'FW-A-INPUT: ',
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should not contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to_not match(/-A INPUT -p tcp -m comment --comment "700 - blah-A Test Rule" -j LOG --log-prefix "FW-A-INPUT: "/)
end
end
end


end
8 changes: 2 additions & 6 deletions spec/acceptance/firewall_time_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ class { '::firewall': }
EOS

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

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

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

it 'should contain the rule' do
Expand Down
16 changes: 4 additions & 12 deletions spec/acceptance/firewall_uid_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ class { '::firewall': }
EOS

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

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

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

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

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

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

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

it 'should contain the rule' do
Expand Down
18 changes: 6 additions & 12 deletions spec/acceptance/firewallchain_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
apply_manifest(pp, :catch_changes => do_catch_changes)
end

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

it 'fails to find the chain' do
Expand All @@ -64,7 +60,7 @@
# EOS
# # Run it twice and test for idempotency
# apply_manifest(pp, :catch_failures => true)
# apply_manifest(pp, :catch_changes => true)
# apply_manifest(pp, :catch_changes => do_catch_changes)
# end
#end

Expand All @@ -85,7 +81,7 @@
# expect(r.stdout).to_not match(/removed/)
# expect(r.stderr).to eq('')
# end
# apply_manifest(pp, :catch_changes => true)
# apply_manifest(pp, :catch_changes => do_catch_changes)
# end

# it 'still has the rule' do
Expand All @@ -98,7 +94,7 @@
# }
# EOS
# # Run it twice and test for idempotency
# apply_manifest(pp, :catch_changes => true)
# apply_manifest(pp, :catch_changes => do_catch_changes)
# end
#end

Expand All @@ -116,9 +112,7 @@
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
apply_manifest(pp, :catch_changes => do_catch_changes)
end

it 'finds the chain' do
Expand Down
6 changes: 2 additions & 4 deletions spec/acceptance/invert_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,12 @@ class { '::firewall': }
EOS

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

it 'should contain the rules' do
shell('iptables-save') do |r|
if (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5')
if (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5') or (default['platform'] =~ /sles-10/)
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
Expand Down
12 changes: 3 additions & 9 deletions spec/acceptance/ip6_fragment_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'spec_helper_acceptance'

if default['platform'] =~ /el-5/
if default['platform'] =~ /el-5/ or default['platform'] =~ /sles-10/
describe "firewall ip6tables doesn't work on 1.3.5 because --comment is missing", :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
before :all do
ip6tables_flush_all_tables
Expand Down Expand Up @@ -37,9 +37,7 @@ class { '::firewall': }
EOS

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

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

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

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

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

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

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

shell('iptables-save') do |r|
expect(r.stdout).to match(/#{line_match}/)
Expand Down
2 changes: 1 addition & 1 deletion spec/acceptance/match_mark_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
shell('ip6tables --flush; ip6tables -t nat --flush; ip6tables -t mangle --flush')
end

if default['platform'] !~ /el-5/
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'match_mark' do
context '0x1' do
it 'applies' do
Expand Down
193 changes: 92 additions & 101 deletions spec/acceptance/purge_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ class { 'firewall': }
}
EOS

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

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

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

it 'adds managed rules with ignored rules' do
Expand Down Expand Up @@ -130,110 +124,107 @@ 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("")
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
context 'ipv6 chain purge' do
after(:all) do
ip6tables_flush_all_tables
end
end
before(:each) do
ip6tables_flush_all_tables

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
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

unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
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
end

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

apply_manifest(pp, :catch_changes => do_catch_changes)
end

if fact('selinux') == 'true'
apply_manifest(pp, :catch_failures => true)
else
apply_manifest(pp, :catch_changes => true)
it 'ignores specified rules' do
pp = <<-EOS
class { 'firewall': }
firewallchain { 'INPUT:filter:IPv6':
purge => true,
ignore => [
'-s 1::42',
],
}
EOS

apply_manifest(pp, :catch_changes => do_catch_changes)
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
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)
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/)
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
end
30 changes: 16 additions & 14 deletions spec/acceptance/resource_cmd_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,22 @@
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
if default['platform'] !~ /sles-10/
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
r.stderr.should be_empty
it do
shell('puppet resource firewall') do |r|
r.exit_code.should be_zero
# don't check stdout, testing preexisting rules, output is normal
r.stderr.should be_empty
end
end
end
end
Expand Down Expand Up @@ -150,7 +152,7 @@

# version of iptables that ships with el5 doesn't work with the
# ip6tables provider
if default['platform'] !~ /el-5/
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
context 'dport/sport with ip6tables' do
before :all do
if fact('osfamily') == 'Debian'
Expand Down
19 changes: 11 additions & 8 deletions spec/acceptance/rules_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,20 @@ class { '::firewall': }
],
}
firewall { '001 ssh needed for beaker testing':
proto => 'tcp',
dport => '22',
action => 'accept',
before => Firewallchain['INPUT:filter:IPv4'],
}
firewall { '010 INPUT allow established and related':
proto => 'all',
state => ['ESTABLISHED', 'RELATED'],
action => 'accept',
before => Firewallchain['INPUT:filter:IPv4'],
}
firewall { "011 reject local traffic not on loopback interface":
iniface => '! lo',
proto => 'all',
Expand All @@ -164,6 +172,7 @@ class { '::firewall': }
action => 'accept',
before => Firewallchain['INPUT:filter:IPv4'],
}
firewall { '025 smtp':
outiface => '! eth0:2',
chain => 'OUTPUT',
Expand Down Expand Up @@ -195,10 +204,6 @@ class { '::firewall': }
action => 'accept',
iniface => 'eth0:3',
}
firewall { '999 reject':
action => 'reject',
reject => 'icmp-host-prohibited',
}
firewallchain { 'LOCAL_INPUT_PRE:filter:IPv4': }
firewall { '001 LOCAL_INPUT_PRE':
Expand Down Expand Up @@ -242,9 +247,7 @@ class { '::firewall': }

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

it 'contains appropriate rules' do
Expand All @@ -263,10 +266,10 @@ class { '::firewall': }
/-A INPUT -s 10.0.0.0\/(8|255\.0\.0\.0) -p icmp -m comment --comment \"013 icmp echo-request\" -m icmp --icmp-type 8 -j ACCEPT/,
/-A INPUT -p icmp -m comment --comment \"013 icmp time-exceeded\" -m icmp --icmp-type 11 -j ACCEPT/,
/-A INPUT -p tcp -m multiport --dports 22 -m comment --comment \"020 ssh\" -m state --state NEW -j ACCEPT/,
/-A INPUT -p tcp -m multiport --dports 22 -m comment --comment \"001 ssh needed for beaker testing\" -j ACCEPT/,
/-A OUTPUT (! -o|-o !) eth0:2 -p tcp -m multiport --dports 25 -m comment --comment \"025 smtp\" -m state --state NEW -j ACCEPT/,
/-A INPUT -i eth0:3 -p tcp -m multiport --dports 443 -m comment --comment \"443 ssl on aliased interface\" -m state --state NEW -j ACCEPT/,
/-A INPUT -m comment --comment \"900 LOCAL_INPUT\" -j LOCAL_INPUT/,
/-A INPUT -m comment --comment \"999 reject\" -j REJECT --reject-with icmp-host-prohibited/,
/-A FORWARD -m comment --comment \"010 allow established and related\" -m state --state RELATED,ESTABLISHED -j ACCEPT/
].each do |line|
expect(r.stdout).to match(line)
Expand Down
10 changes: 2 additions & 8 deletions spec/acceptance/socket_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ class { '::firewall': }
EOS

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

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

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

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

# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
unless fact('selinux') == 'true'
apply_manifest(pp, :catch_changes => true)
end
apply_manifest(pp, :catch_changes => do_catch_changes)
end
end
25 changes: 25 additions & 0 deletions spec/fixtures/iptables/conversion_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,13 @@
:source => '192.168.0.1/32',
},
},
'string_escape_sequences' => {
:line => '-A INPUT -m comment --comment "000 parse escaped \\"s, \\\'s, and \\\\s"',
:table => 'filter',
:params => {
:name => '000 parse escaped "s, \'s, and \\s',
},
},
'log_level_debug' => {
:line => '-A INPUT -m comment --comment "956 INPUT log-level" -m state --state NEW -j LOG --log-level 7',
:table => 'filter',
Expand Down Expand Up @@ -761,6 +768,13 @@
},
:args => ['-t', :filter, '-s', '192.168.0.1/32', '-p', :tcp, '-m', 'comment', '--comment', '000 allow from 192.168.0.1, please'],
},
'comment_string_character_validation_2' => {
:params => {
:name => "000 allow symbols ( $+<=>^`|~ ) in ruby >= 1.9",
:table => 'filter',
},
:args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '000 allow symbols ( $+<=>^`|~ ) in ruby >= 1.9'],
},
'port_property' => {
:params => {
:name => '001 port property',
Expand Down Expand Up @@ -1092,4 +1106,15 @@
},
:args => ["-t", :filter, "-p", :tcp, "-m", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-m", "comment", "--comment", "067 change max segment size", "-j", "TCPMSS", "--clamp-mss-to-pmtu"],
},
'set_dscp_class' => {
:params => {
:name => '068 set dscp class to EF',
:table => 'mangle',
:proto => 'tcp',
:port => '997',
:jump => 'DSCP',
:set_dscp_class => 'ef',
},
:args => ["-t", :mangle, "-p", :tcp, "-m", "multiport", '--ports', '997', "-m", "comment", "--comment", "068 set dscp class to EF", "-j", "DSCP", "--set-dscp-class", "ef"],
},
}
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@
end
config.module_path = File.join(fixture_path, 'modules')
config.manifest_dir = File.join(fixture_path, 'manifests')
config.environmentpath = File.expand_path(File.join(Dir.pwd, 'spec'))
end
29 changes: 6 additions & 23 deletions spec/spec_helper_acceptance.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'beaker-rspec'
require 'beaker/puppet_install_helper'

def iptables_flush_all_tables
['filter', 'nat', 'mangle', 'raw'].each do |t|
Expand All @@ -12,32 +13,15 @@ def ip6tables_flush_all_tables
end
end

def is_rhel7(osfamily, operatingsystem, operatingsystemrelease)
if osfamily == 'RedHat'
case operatingsystem
when 'Amazon'
false
when 'Fedora'
operatingsystemrelease >= '7.0'
else
operatingsystemrelease >= '15'
end
def do_catch_changes
if default['platform'] =~ /el-5/
return false
else
false
return true
end
end

unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no'
# 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
end
run_puppet_install_helper

UNSUPPORTED_PLATFORMS = ['windows','Solaris','Darwin']

Expand All @@ -53,7 +37,6 @@ def is_rhel7(osfamily, operatingsystem, operatingsystemrelease)
# Install module and dependencies
hosts.each do |host|
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
Expand Down
7 changes: 5 additions & 2 deletions spec/unit/classes/firewall_linux_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
context 'Debian like' do
%w{Debian Ubuntu}.each do |os|
context "operatingsystem => #{os}" do
let(:facts) { facts_default.merge({ :operatingsystem => os }) }
it { should contain_class('firewall::linux::debian').with_require('Package[iptables]') }
releases = (os == 'Debian' ? ['6','7','8'] : ['10.04','12.04','14.04'])
releases.each do |osrel|
let(:facts) { facts_default.merge({ :operatingsystem => os, :operatingsystemrelease => osrel}) }
it { should contain_class('firewall::linux::debian').with_require('Package[iptables]') }
end
end
end
end
Expand Down
1 change: 0 additions & 1 deletion spec/unit/puppet/provider/iptables_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@
ARGS_TO_HASH.each do |test_name,data|
describe "for test data '#{test_name}'" do
let(:resource) { provider.rule_to_hash(data[:line], data[:table], 0) }

# If this option is enabled, make sure the parameters exactly match
if data[:compare_all] then
it "the parameter hash keys should be the same as returned by rules_to_hash" do
Expand Down