Added puppetlabs-firewall (required by puppetlabs-postgresql), updated the other modules.

This commit is contained in:
Ciaby 2014-07-11 14:51:15 -05:00
parent 5f4b7a3b72
commit dee66abcdd
137 changed files with 11118 additions and 419 deletions

View file

@ -0,0 +1,32 @@
require 'spec_helper'
describe 'firewall::linux::archlinux', :type => :class do
it { should contain_service('iptables').with(
:ensure => 'running',
:enable => 'true'
)}
it { should contain_service('ip6tables').with(
:ensure => 'running',
:enable => 'true'
)}
context 'ensure => stopped' do
let(:params) {{ :ensure => 'stopped' }}
it { should contain_service('iptables').with(
:ensure => 'stopped'
)}
it { should contain_service('ip6tables').with(
:ensure => 'stopped'
)}
end
context 'enable => false' do
let(:params) {{ :enable => 'false' }}
it { should contain_service('iptables').with(
:enable => 'false'
)}
it { should contain_service('ip6tables').with(
:enable => 'false'
)}
end
end

View file

@ -0,0 +1,19 @@
require 'spec_helper'
describe 'firewall::linux::debian', :type => :class do
it { should contain_package('iptables-persistent').with(
:ensure => 'present'
)}
it { should contain_service('iptables-persistent').with(
:ensure => nil,
:enable => 'true',
:require => 'Package[iptables-persistent]'
)}
context 'enable => false' do
let(:params) {{ :enable => 'false' }}
it { should contain_service('iptables-persistent').with(
:enable => 'false'
)}
end
end

View file

@ -0,0 +1,22 @@
require 'spec_helper'
describe 'firewall::linux::redhat', :type => :class do
it { should contain_service('iptables').with(
:ensure => 'running',
:enable => 'true'
)}
context 'ensure => stopped' do
let(:params) {{ :ensure => 'stopped' }}
it { should contain_service('iptables').with(
:ensure => 'stopped'
)}
end
context 'enable => false' do
let(:params) {{ :enable => 'false' }}
it { should contain_service('iptables').with(
:enable => 'false'
)}
end
end

View file

@ -0,0 +1,30 @@
require 'spec_helper'
describe 'firewall::linux', :type => :class do
let(:facts_default) {{ :kernel => 'Linux' }}
it { should contain_package('iptables').with_ensure('present') }
context 'RedHat like' do
%w{RedHat CentOS Fedora}.each do |os|
context "operatingsystem => #{os}" do
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
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]') }
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'spec_helper'
describe 'firewall', :type => :class do
context 'kernel => Linux' do
let(:facts) {{ :kernel => 'Linux' }}
it { should contain_class('firewall::linux').with_ensure('running') }
end
context 'kernel => Windows' do
let(:facts) {{ :kernel => 'Windows' }}
it { expect { should contain_class('firewall::linux') }.to raise_error(Puppet::Error) }
end
context 'ensure => stopped' do
let(:facts) {{ :kernel => 'Linux' }}
let(:params) {{ :ensure => 'stopped' }}
it { should contain_class('firewall::linux').with_ensure('stopped') }
end
context 'ensure => test' do
let(:facts) {{ :kernel => 'Linux' }}
let(:params) {{ :ensure => 'test' }}
it { expect { should contain_class('firewall::linux') }.to raise_error(Puppet::Error) }
end
end

View file

@ -0,0 +1,35 @@
require 'spec_helper'
describe "Facter::Util::Fact iptables_persistent_version" do
before { Facter.clear }
let(:dpkg_cmd) { "dpkg-query -Wf '${Version}' iptables-persistent 2>/dev/null" }
{
"Debian" => "0.0.20090701",
"Ubuntu" => "0.5.3ubuntu2",
}.each do |os, ver|
describe "#{os} package installed" do
before {
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return(os)
allow(Facter::Util::Resolution).to receive(:exec).with(dpkg_cmd).
and_return(ver)
}
it { Facter.fact(:iptables_persistent_version).value.should == ver }
end
end
describe 'Ubuntu package not installed' do
before {
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu')
allow(Facter::Util::Resolution).to receive(:exec).with(dpkg_cmd).
and_return(nil)
}
it { Facter.fact(:iptables_persistent_version).value.should be_nil }
end
describe 'CentOS not supported' do
before { allow(Facter.fact(:operatingsystem)).to receive(:value).
and_return("CentOS") }
it { Facter.fact(:iptables_persistent_version).value.should be_nil }
end
end

View file

@ -0,0 +1,23 @@
require 'spec_helper'
describe "Facter::Util::Fact" do
before {
Facter.clear
allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux')
allow(Facter.fact(:kernelrelease)).to receive(:value).and_return('2.6')
}
describe 'iptables_version' do
it {
allow(Facter::Util::Resolution).to receive(:exec).with('iptables --version').
and_return('iptables v1.4.7')
Facter.fact(:iptables_version).value.should == '1.4.7'
}
end
describe 'ip6tables_version' do
before { allow(Facter::Util::Resolution).to receive(:exec).
with('ip6tables --version').and_return('ip6tables v1.4.7') }
it { Facter.fact(:ip6tables_version).value.should == '1.4.7' }
end
end

View file

@ -0,0 +1,227 @@
#!/usr/bin/env rspec
require 'spec_helper'
if Puppet.version < '3.4.0'
require 'puppet/provider/confine/exists'
else
require 'puppet/confine/exists'
end
describe 'iptables chain provider detection' do
if Puppet.version < '3.4.0'
let(:exists) {
Puppet::Provider::Confine::Exists
}
else
let(:exists) {
Puppet::Confine::Exists
}
end
before :each do
# Reset the default provider
Puppet::Type.type(:firewallchain).defaultprovider = nil
end
it "should default to iptables provider if /sbin/(eb|ip|ip6)tables[-save] exists" do
# Stub lookup for /sbin/iptables & /sbin/iptables-save
allow(exists).to receive(:which).with("ebtables").
and_return "/sbin/ebtables"
allow(exists).to receive(:which).with("ebtables-save").
and_return "/sbin/ebtables-save"
allow(exists).to receive(:which).with("iptables").
and_return "/sbin/iptables"
allow(exists).to receive(:which).with("iptables-save").
and_return "/sbin/iptables-save"
allow(exists).to receive(:which).with("ip6tables").
and_return "/sbin/ip6tables"
allow(exists).to receive(:which).with("ip6tables-save").
and_return "/sbin/ip6tables-save"
# Every other command should return false so we don't pick up any
# other providers
allow(exists).to receive(:which).with() { |value|
value !~ /(eb|ip|ip6)tables(-save)?$/
}.and_return false
# Create a resource instance and make sure the provider is iptables
resource = Puppet::Type.type(:firewallchain).new({
:name => 'test:filter:IPv4',
})
expect(resource.provider.class.to_s).to eq("Puppet::Type::Firewallchain::ProviderIptables_chain")
end
end
describe 'iptables chain provider' do
let(:provider) { Puppet::Type.type(:firewallchain).provider(:iptables_chain) }
let(:resource) {
Puppet::Type.type(:firewallchain).new({
:name => ':test:',
})
}
before :each do
allow(Puppet::Type::Firewallchain).to receive(:defaultprovider).and_return provider
allow(provider).to receive(:command).with(:ebtables_save).and_return "/sbin/ebtables-save"
allow(provider).to receive(:command).with(:iptables_save).and_return "/sbin/iptables-save"
allow(provider).to receive(:command).with(:ip6tables_save).and_return "/sbin/ip6tables-save"
end
it 'should be able to get a list of existing rules' do
# Pretend to return nil from iptables
allow(provider).to receive(:execute).with(['/sbin/ip6tables-save']).and_return("")
allow(provider).to receive(:execute).with(['/sbin/ebtables-save']).and_return("")
allow(provider).to receive(:execute).with(['/sbin/iptables-save']).and_return("")
provider.instances.each do |chain|
expect(chain).to be_instance_of(provider)
expect(chain.properties[:provider].to_s).to eq(provider.name.to_s)
end
end
end
describe 'iptables chain resource parsing' do
let(:provider) { Puppet::Type.type(:firewallchain).provider(:iptables_chain) }
before :each do
ebtables = ['BROUTE:BROUTING:ethernet',
'BROUTE:broute:ethernet',
':INPUT:ethernet',
':FORWARD:ethernet',
':OUTPUT:ethernet',
':filter:ethernet',
':filterdrop:ethernet',
':filterreturn:ethernet',
'NAT:PREROUTING:ethernet',
'NAT:OUTPUT:ethernet',
'NAT:POSTROUTING:ethernet',
]
allow(provider).to receive(:execute).with(['/sbin/ebtables-save']).and_return('
*broute
:BROUTING ACCEPT
:broute ACCEPT
*filter
:INPUT ACCEPT
:FORWARD ACCEPT
:OUTPUT ACCEPT
:filter ACCEPT
:filterdrop DROP
:filterreturn RETURN
*nat
:PREROUTING ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
')
iptables = [
'raw:PREROUTING:IPv4',
'raw:OUTPUT:IPv4',
'raw:raw:IPv4',
'mangle:PREROUTING:IPv4',
'mangle:INPUT:IPv4',
'mangle:FORWARD:IPv4',
'mangle:OUTPUT:IPv4',
'mangle:POSTROUTING:IPv4',
'mangle:mangle:IPv4',
'NAT:PREROUTING:IPv4',
'NAT:OUTPUT:IPv4',
'NAT:POSTROUTING:IPv4',
'NAT:mangle:IPv4',
'NAT:mangle:IPv4',
'NAT:mangle:IPv4',
':$5()*&%\'"^$): :IPv4',
]
allow(provider).to receive(:execute).with(['/sbin/iptables-save']).and_return('
# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012
*raw
:PREROUTING ACCEPT [12:1780]
:OUTPUT ACCEPT [19:1159]
:raw - [0:0]
COMMIT
# Completed on Mon Jan 2 01:20:06 2012
# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012
*mangle
:PREROUTING ACCEPT [12:1780]
:INPUT ACCEPT [12:1780]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [19:1159]
:POSTROUTING ACCEPT [19:1159]
:mangle - [0:0]
COMMIT
# Completed on Mon Jan 2 01:20:06 2012
# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012
*nat
:PREROUTING ACCEPT [2242:639750]
:OUTPUT ACCEPT [5176:326206]
:POSTROUTING ACCEPT [5162:325382]
COMMIT
# Completed on Mon Jan 2 01:20:06 2012
# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [5673:420879]
:$5()*&%\'"^$): - [0:0]
COMMIT
# Completed on Mon Jan 2 01:20:06 2012
')
ip6tables = [
'raw:PREROUTING:IPv6',
'raw:OUTPUT:IPv6',
'raw:ff:IPv6',
'mangle:PREROUTING:IPv6',
'mangle:INPUT:IPv6',
'mangle:FORWARD:IPv6',
'mangle:OUTPUT:IPv6',
'mangle:POSTROUTING:IPv6',
'mangle:ff:IPv6',
':INPUT:IPv6',
':FORWARD:IPv6',
':OUTPUT:IPv6',
':test:IPv6',
]
allow(provider).to receive(:execute).with(['/sbin/ip6tables-save']).and_return('
# Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012
*raw
:PREROUTING ACCEPT [2173:489241]
:OUTPUT ACCEPT [0:0]
:ff - [0:0]
COMMIT
# Completed on Mon Jan 2 01:31:39 2012
# Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012
*mangle
:PREROUTING ACCEPT [2301:518373]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:ff - [0:0]
COMMIT
# Completed on Mon Jan 2 01:31:39 2012
# Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [20:1292]
:test - [0:0]
COMMIT
# Completed on Mon Jan 2 01:31:39 2012
')
@all = ebtables + iptables + ip6tables
# IPv4 and IPv6 names also exist as resources {table}:{chain}:IP and {table}:{chain}:
iptables.each { |name| @all += [ name[0..-3], name[0..-5] ] }
ip6tables.each { |name| @all += [ name[0..-3], name[0..-5] ] }
end
it 'should have all in parsed resources' do
provider.instances.each do |resource|
@all.include?(resource.name)
end
end
end

View file

@ -0,0 +1,410 @@
#!/usr/bin/env rspec
require 'spec_helper'
if Puppet.version < '3.4.0'
require 'puppet/provider/confine/exists'
else
require 'puppet/confine/exists'
end
describe 'iptables provider detection' do
if Puppet.version < '3.4.0'
let(:exists) {
Puppet::Provider::Confine::Exists
}
else
let(:exists) {
Puppet::Confine::Exists
}
end
before :each do
# Reset the default provider
Puppet::Type.type(:firewall).defaultprovider = nil
end
it "should default to iptables provider if /sbin/iptables[-save] exists" do
# Stub lookup for /sbin/iptables & /sbin/iptables-save
allow(exists).to receive(:which).with("iptables").
and_return "/sbin/iptables"
allow(exists).to receive(:which).with("iptables-save").
and_return "/sbin/iptables-save"
# Every other command should return false so we don't pick up any
# other providers
allow(exists).to receive(:which).with() { |value|
! ["iptables","iptables-save"].include?(value)
}.and_return false
# Create a resource instance and make sure the provider is iptables
resource = Puppet::Type.type(:firewall).new({
:name => '000 test foo',
})
expect(resource.provider.class.to_s).to eq("Puppet::Type::Firewall::ProviderIptables")
end
end
describe 'iptables provider' do
let(:provider) { Puppet::Type.type(:firewall).provider(:iptables) }
let(:resource) {
Puppet::Type.type(:firewall).new({
:name => '000 test foo',
:action => 'accept',
})
}
before :each do
allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return provider
allow(provider).to receive(:command).with(:iptables_save).and_return "/sbin/iptables-save"
# Stub iptables version
allow(Facter.fact(:iptables_version)).to receive(:value).and_return("1.4.2")
allow(Puppet::Util::Execution).to receive(:execute).and_return ""
allow(Puppet::Util).to receive(:which).with("iptables-save").
and_return "/sbin/iptables-save"
end
it 'should be able to get a list of existing rules' do
provider.instances.each do |rule|
expect(rule).to be_instance_of(provider)
expect(rule.properties[:provider].to_s).to eq(provider.name.to_s)
end
end
it 'should ignore lines with fatal errors' do
allow(Puppet::Util::Execution).to receive(:execute).with(['/sbin/iptables-save']).
and_return("FATAL: Could not load /lib/modules/2.6.18-028stab095.1/modules.dep: No such file or directory")
expect(provider.instances.length).to be_zero
end
describe '#insert_order' do
let(:iptables_save_output) { [
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT',
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -m comment --comment "200 test" -j ACCEPT',
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT'
] }
let(:resources) do
iptables_save_output.each_with_index.collect { |l,index| provider.rule_to_hash(l, 'filter', index) }
end
let(:providers) do
resources.collect { |r| provider.new(r) }
end
it 'understands offsets for adding rules to the beginning' do
resource = Puppet::Type.type(:firewall).new({ :name => '001 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(1) # 1-indexed
end
it 'understands offsets for editing rules at the beginning' do
resource = Puppet::Type.type(:firewall).new({ :name => '100 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(1)
end
it 'understands offsets for adding rules to the middle' do
resource = Puppet::Type.type(:firewall).new({ :name => '101 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(2)
end
it 'understands offsets for editing rules at the middle' do
resource = Puppet::Type.type(:firewall).new({ :name => '200 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(2)
end
it 'understands offsets for adding rules to the end' do
resource = Puppet::Type.type(:firewall).new({ :name => '301 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(4)
end
it 'understands offsets for editing rules at the end' do
resource = Puppet::Type.type(:firewall).new({ :name => '300 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(3)
end
context 'with unname rules between' do
let(:iptables_save_output) { [
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT',
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 150 -m comment --comment "150 test" -j ACCEPT',
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -j ACCEPT',
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 250 -j ACCEPT',
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT',
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 350 -m comment --comment "350 test" -j ACCEPT',
] }
it 'understands offsets for adding rules before unnamed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '001 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(1)
end
it 'understands offsets for editing rules before unnamed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '100 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(1)
end
it 'understands offsets for adding rules between managed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '120 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(2)
end
it 'understands offsets for adding rules between unnamed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '151 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(3)
end
it 'understands offsets for adding rules after unnamed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '351 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(7)
end
end
context 'with unname rules before and after' do
let(:iptables_save_output) { [
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 050 -j ACCEPT',
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 090 -j ACCEPT',
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT',
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 150 -m comment --comment "150 test" -j ACCEPT',
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -j ACCEPT',
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 250 -j ACCEPT',
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT',
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 350 -m comment --comment "350 test" -j ACCEPT',
'-A INPUT -s 8.0.0.5/32 -p tcp -m multiport --ports 400 -j ACCEPT',
'-A INPUT -s 8.0.0.5/32 -p tcp -m multiport --ports 450 -j ACCEPT',
] }
it 'understands offsets for adding rules before unnamed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '001 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(1)
end
it 'understands offsets for editing rules before unnamed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '100 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(3)
end
it 'understands offsets for adding rules between managed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '120 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(4)
end
it 'understands offsets for adding rules between unnamed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '151 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(5)
end
it 'understands offsets for adding rules after unnamed rules' do
resource = Puppet::Type.type(:firewall).new({ :name => '351 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(9)
end
it 'understands offsets for adding rules at the end' do
resource = Puppet::Type.type(:firewall).new({ :name => '950 test', })
allow(resource.provider.class).to receive(:instances).and_return(providers)
expect(resource.provider.insert_order).to eq(11)
end
end
end
# Load in ruby hash for test fixtures.
load 'spec/fixtures/iptables/conversion_hash.rb'
describe 'when converting rules to resources' do
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
expect(resource.keys).to match_array(data[:params].keys)
end
end
# Iterate across each parameter, creating an example for comparison
data[:params].each do |param_name, param_value|
it "the parameter '#{param_name.to_s}' should match #{param_value.inspect}" do
# booleans get cludged to string "true"
if param_value == true then
expect(resource[param_name]).to be_true
else
expect(resource[param_name]).to eq(data[:params][param_name])
end
end
end
end
end
end
describe 'when working out general_args' do
HASH_TO_ARGS.each do |test_name,data|
describe "for test data '#{test_name}'" do
let(:resource) { Puppet::Type.type(:firewall).new(data[:params]) }
let(:provider) { Puppet::Type.type(:firewall).provider(:iptables) }
let(:instance) { provider.new(resource) }
it 'general_args should be valid' do
expect(instance.general_args.flatten).to eq(data[:args])
end
end
end
end
describe 'when converting rules without comments to resources' do
let(:sample_rule) {
'-A INPUT -s 1.1.1.1 -d 1.1.1.1 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -j ACCEPT'
}
let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) }
let(:instance) { provider.new(resource) }
it 'rule name contains a MD5 sum of the line' do
expect(resource[:name]).to eq("9000 #{Digest::MD5.hexdigest(resource[:line])}")
end
it 'parsed the rule arguments correctly' do
expect(resource[:chain]).to eq('INPUT')
expect(resource[:source]).to eq('1.1.1.1/32')
expect(resource[:destination]).to eq('1.1.1.1/32')
expect(resource[:proto]).to eq('tcp')
expect(resource[:dport]).to eq(['7061', '7062'])
expect(resource[:sport]).to eq(['7061', '7062'])
expect(resource[:action]).to eq('accept')
end
end
describe 'when converting existing rules generates by system-config-firewall-tui to resources' do
let(:sample_rule) {
# as generated by iptables-save from rules created with system-config-firewall-tui
'-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT'
}
let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) }
let(:instance) { provider.new(resource) }
it 'rule name contains a MD5 sum of the line' do
expect(resource[:name]).to eq("9000 #{Digest::MD5.hexdigest(resource[:line])}")
end
it 'parse arguments' do
expect(resource[:chain]).to eq('INPUT')
expect(resource[:proto]).to eq('tcp')
expect(resource[:dport]).to eq(['22'])
expect(resource[:state]).to eq(['NEW'])
expect(resource[:action]).to eq('accept')
end
end
describe 'when creating resources' do
let(:instance) { provider.new(resource) }
it 'insert_args should be an array' do
expect(instance.insert_args.class).to eq(Array)
end
end
describe 'when modifying resources' do
let(:instance) { provider.new(resource) }
it 'update_args should be an array' do
expect(instance.update_args.class).to eq(Array)
end
it 'fails when modifying the chain' do
expect { instance.chain = "OUTPUT" }.to raise_error(/is not supported/)
end
end
describe 'when deleting resources' do
let(:sample_rule) {
'-A INPUT -s 1.1.1.1 -d 1.1.1.1 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -j ACCEPT'
}
let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) }
let(:instance) { provider.new(resource) }
it 'resource[:line] looks like the original rule' do
resource[:line] == sample_rule
end
it 'delete_args is an array' do
expect(instance.delete_args.class).to eq(Array)
end
it 'delete_args is the same as the rule string when joined' do
expect(instance.delete_args.join(' ')).to eq(sample_rule.gsub(/\-A/,
'-t filter -D'))
end
end
end
describe 'ip6tables provider' do
let(:provider6) { Puppet::Type.type(:firewall).provider(:ip6tables) }
let(:resource) {
Puppet::Type.type(:firewall).new({
:name => '000 test foo',
:action => 'accept',
:provider => "ip6tables",
})
}
before :each do
allow(Puppet::Type::Firewall).to receive(:ip6tables).and_return provider6
allow(provider6).to receive(:command).with(:ip6tables_save).and_return "/sbin/ip6tables-save"
# Stub iptables version
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return '1.4.7'
allow(Puppet::Util::Execution).to receive(:execute).and_return ''
allow(Puppet::Util).to receive(:which).with("ip6tables-save").
and_return "/sbin/ip6tables-save"
end
it 'should be able to get a list of existing rules' do
provider6.instances.each do |rule|
rule.should be_instance_of(provider6)
rule.properties[:provider6].to_s.should == provider6.name.to_s
end
end
it 'should ignore lines with fatal errors' do
allow(Puppet::Util::Execution).to receive(:execute).with(['/sbin/ip6tables-save']).
and_return("FATAL: Could not load /lib/modules/2.6.18-028stab095.1/modules.dep: No such file or directory")
provider6.instances.length.should == 0
end
# Load in ruby hash for test fixtures.
load 'spec/fixtures/ip6tables/conversion_hash.rb'
describe 'when converting rules to resources' do
ARGS_TO_HASH6.each do |test_name,data|
describe "for test data '#{test_name}'" do
let(:resource) { provider6.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
resource.keys.should =~ data[:params].keys
end
end
# Iterate across each parameter, creating an example for comparison
data[:params].each do |param_name, param_value|
it "the parameter '#{param_name.to_s}' should match #{param_value.inspect}" do
resource[param_name].should == data[:params][param_name]
end
end
end
end
end
describe 'when working out general_args' do
HASH_TO_ARGS6.each do |test_name,data|
describe "for test data '#{test_name}'" do
let(:resource) { Puppet::Type.type(:firewall).new(data[:params]) }
let(:provider6) { Puppet::Type.type(:firewall).provider(:ip6tables) }
let(:instance) { provider6.new(resource) }
it 'general_args should be valid' do
instance.general_args.flatten.should == data[:args]
end
end
end
end
end

View file

@ -0,0 +1,650 @@
#!/usr/bin/env rspec
require 'spec_helper'
firewall = Puppet::Type.type(:firewall)
describe firewall do
before :each do
@class = firewall
@provider = double 'provider'
allow(@provider).to receive(:name).and_return(:iptables)
allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return @provider
@resource = @class.new({:name => '000 test foo'})
# Stub iptables version
allow(Facter.fact(:iptables_version)).to receive(:value).and_return('1.4.2')
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return('1.4.2')
# Stub confine facts
allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux')
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Debian')
end
it 'should have :name be its namevar' do
@class.key_attributes.should == [:name]
end
describe ':name' do
it 'should accept a name' do
@resource[:name] = '000-test-foo'
@resource[:name].should == '000-test-foo'
end
it 'should not accept a name with non-ASCII chars' do
lambda { @resource[:name] = '%*#^(#$' }.should raise_error(Puppet::Error)
end
end
describe ':action' do
it "should have no default" do
res = @class.new(:name => "000 test")
res.parameters[:action].should == nil
end
[:accept, :drop, :reject].each do |action|
it "should accept value #{action}" do
@resource[:action] = action
@resource[:action].should == action
end
end
it 'should fail when value is not recognized' do
lambda { @resource[:action] = 'not valid' }.should raise_error(Puppet::Error)
end
end
describe ':chain' do
[:INPUT, :FORWARD, :OUTPUT, :PREROUTING, :POSTROUTING].each do |chain|
it "should accept chain value #{chain}" do
@resource[:chain] = chain
@resource[:chain].should == chain
end
end
it 'should fail when the chain value is not recognized' do
lambda { @resource[:chain] = 'not valid' }.should raise_error(Puppet::Error)
end
end
describe ':table' do
[:nat, :mangle, :filter, :raw].each do |table|
it "should accept table value #{table}" do
@resource[:table] = table
@resource[:table].should == table
end
end
it "should fail when table value is not recognized" do
lambda { @resource[:table] = 'not valid' }.should raise_error(Puppet::Error)
end
end
describe ':proto' do
[:tcp, :udp, :icmp, :esp, :ah, :vrrp, :igmp, :ipencap, :ospf, :gre, :all].each do |proto|
it "should accept proto value #{proto}" do
@resource[:proto] = proto
@resource[:proto].should == proto
end
end
it "should fail when proto value is not recognized" do
lambda { @resource[:proto] = 'foo' }.should raise_error(Puppet::Error)
end
end
describe ':jump' do
it "should have no default" do
res = @class.new(:name => "000 test")
res.parameters[:jump].should == nil
end
['QUEUE', 'RETURN', 'DNAT', 'SNAT', 'LOG', 'MASQUERADE', 'REDIRECT', 'MARK'].each do |jump|
it "should accept jump value #{jump}" do
@resource[:jump] = jump
@resource[:jump].should == jump
end
end
['ACCEPT', 'DROP', 'REJECT'].each do |jump|
it "should now fail when value #{jump}" do
lambda { @resource[:jump] = jump }.should raise_error(Puppet::Error)
end
end
it "should fail when jump value is not recognized" do
lambda { @resource[:jump] = '%^&*' }.should raise_error(Puppet::Error)
end
end
[:source, :destination].each do |addr|
describe addr do
it "should accept a #{addr} as a string" do
@resource[addr] = '127.0.0.1'
@resource[addr].should == '127.0.0.1/32'
end
['0.0.0.0/0', '::/0'].each do |prefix|
it "should be nil for zero prefix length address #{prefix}" do
@resource[addr] = prefix
@resource[addr].should == nil
end
end
it "should accept a negated #{addr} as a string" do
@resource[addr] = '! 127.0.0.1'
@resource[addr].should == '! 127.0.0.1/32'
end
end
end
[:dport, :sport].each do |port|
describe port do
it "should accept a #{port} as string" do
@resource[port] = '22'
@resource[port].should == ['22']
end
it "should accept a #{port} as an array" do
@resource[port] = ['22','23']
@resource[port].should == ['22','23']
end
it "should accept a #{port} as a number" do
@resource[port] = 22
@resource[port].should == ['22']
end
it "should accept a #{port} as a hyphen separated range" do
@resource[port] = ['22-1000']
@resource[port].should == ['22-1000']
end
it "should accept a #{port} as a combination of arrays of single and " \
"hyphen separated ranges" do
@resource[port] = ['22-1000','33','3000-4000']
@resource[port].should == ['22-1000','33','3000-4000']
end
it "should convert a port name for #{port} to its number" do
@resource[port] = 'ssh'
@resource[port].should == ['22']
end
it "should not accept something invalid for #{port}" do
expect { @resource[port] = 'something odd' }.to raise_error(Puppet::Error, /^Parameter .+ failed.+Munging failed for value ".+" in class .+: no such service/)
end
it "should not accept something invalid in an array for #{port}" do
expect { @resource[port] = ['something odd','something even odder'] }.to raise_error(Puppet::Error, /^Parameter .+ failed.+Munging failed for value ".+" in class .+: no such service/)
end
end
end
[:dst_type, :src_type].each do |addrtype|
describe addrtype do
it "should have no default" do
res = @class.new(:name => "000 test")
res.parameters[addrtype].should == nil
end
end
[:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, :BLACKHOLE,
:UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE].each do |type|
it "should accept #{addrtype} value #{type}" do
@resource[addrtype] = type
@resource[addrtype].should == type
end
end
it "should fail when #{addrtype} value is not recognized" do
lambda { @resource[addrtype] = 'foo' }.should raise_error(Puppet::Error)
end
end
[:iniface, :outiface].each do |iface|
describe iface do
it "should accept #{iface} value as a string" do
@resource[iface] = 'eth1'
@resource[iface].should == 'eth1'
end
end
end
[:tosource, :todest].each do |addr|
describe addr do
it "should accept #{addr} value as a string" do
@resource[addr] = '127.0.0.1'
end
end
end
describe ':log_level' do
values = {
'panic' => '0',
'alert' => '1',
'crit' => '2',
'err' => '3',
'warn' => '4',
'warning' => '4',
'not' => '5',
'notice' => '5',
'info' => '6',
'debug' => '7'
}
values.each do |k,v|
it {
@resource[:log_level] = k
@resource[:log_level].should == v
}
it {
@resource[:log_level] = 3
@resource[:log_level].should == 3
}
it { lambda { @resource[:log_level] = 'foo' }.should raise_error(Puppet::Error) }
end
end
describe ':icmp' do
icmp_codes = {
:iptables => {
'0' => 'echo-reply',
'3' => 'destination-unreachable',
'4' => 'source-quench',
'6' => 'redirect',
'8' => 'echo-request',
'9' => 'router-advertisement',
'10' => 'router-solicitation',
'11' => 'time-exceeded',
'12' => 'parameter-problem',
'13' => 'timestamp-request',
'14' => 'timestamp-reply',
'17' => 'address-mask-request',
'18' => 'address-mask-reply'
},
:ip6tables => {
'1' => 'destination-unreachable',
'3' => 'time-exceeded',
'4' => 'parameter-problem',
'128' => 'echo-request',
'129' => 'echo-reply',
'133' => 'router-solicitation',
'134' => 'router-advertisement',
'137' => 'redirect'
}
}
icmp_codes.each do |provider, values|
describe provider do
values.each do |k,v|
it 'should convert icmp string to number' do
@resource[:provider] = provider
@resource[:provider].should == provider
@resource[:icmp] = v
@resource[:icmp].should == k
end
end
end
end
it 'should accept values as integers' do
@resource[:icmp] = 9
@resource[:icmp].should == 9
end
it 'should fail if icmp type is "any"' do
lambda { @resource[:icmp] = 'any' }.should raise_error(Puppet::Error)
end
it 'should fail if icmp type cannot be mapped to a numeric' do
lambda { @resource[:icmp] = 'foo' }.should raise_error(Puppet::Error)
end
end
describe ':state' do
it 'should accept value as a string' do
@resource[:state] = :INVALID
@resource[:state].should == [:INVALID]
end
it 'should accept value as an array' do
@resource[:state] = [:INVALID, :NEW]
@resource[:state].should == [:INVALID, :NEW]
end
it 'should sort values alphabetically' do
@resource[:state] = [:NEW, :ESTABLISHED]
@resource[:state].should == [:ESTABLISHED, :NEW]
end
end
describe ':ctstate' do
it 'should accept value as a string' do
@resource[:ctstate] = :INVALID
@resource[:ctstate].should == [:INVALID]
end
it 'should accept value as an array' do
@resource[:ctstate] = [:INVALID, :NEW]
@resource[:ctstate].should == [:INVALID, :NEW]
end
it 'should sort values alphabetically' do
@resource[:ctstate] = [:NEW, :ESTABLISHED]
@resource[:ctstate].should == [:ESTABLISHED, :NEW]
end
end
describe ':burst' do
it 'should accept numeric values' do
@resource[:burst] = 12
@resource[:burst].should == 12
end
it 'should fail if value is not numeric' do
lambda { @resource[:burst] = 'foo' }.should raise_error(Puppet::Error)
end
end
describe ':recent' do
['set', 'update', 'rcheck', 'remove'].each do |recent|
it "should accept recent value #{recent}" do
@resource[:recent] = recent
@resource[:recent].should == "--#{recent}"
end
end
end
describe ':action and :jump' do
it 'should allow only 1 to be set at a time' do
expect {
@class.new(
:name => "001-test",
:action => "accept",
:jump => "custom_chain"
)
}.to raise_error(Puppet::Error, /Only one of the parameters 'action' and 'jump' can be set$/)
end
end
describe ':gid and :uid' do
it 'should allow me to set uid' do
@resource[:uid] = 'root'
@resource[:uid].should == 'root'
end
it 'should allow me to set uid as an array, and silently hide my error' do
@resource[:uid] = ['root', 'bobby']
@resource[:uid].should == 'root'
end
it 'should allow me to set gid' do
@resource[:gid] = 'root'
@resource[:gid].should == 'root'
end
it 'should allow me to set gid as an array, and silently hide my error' do
@resource[:gid] = ['root', 'bobby']
@resource[:gid].should == 'root'
end
end
describe ':set_mark' do
['1.3.2', '1.4.2'].each do |iptables_version|
describe "with iptables #{iptables_version}" do
before {
Facter.clear
allow(Facter.fact(:iptables_version)).to receive(:value).and_return iptables_version
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return iptables_version
}
if iptables_version == '1.3.2'
it 'should allow me to set set-mark without mask' do
@resource[:set_mark] = '0x3e8'
@resource[:set_mark].should == '0x3e8'
end
it 'should convert int to hex without mask' do
@resource[:set_mark] = '1000'
@resource[:set_mark].should == '0x3e8'
end
it 'should fail if mask is present' do
lambda { @resource[:set_mark] = '0x3e8/0xffffffff'}.should raise_error(
Puppet::Error, /iptables version #{iptables_version} does not support masks on MARK rules$/
)
end
end
if iptables_version == '1.4.2'
it 'should allow me to set set-mark with mask' do
@resource[:set_mark] = '0x3e8/0xffffffff'
@resource[:set_mark].should == '0x3e8/0xffffffff'
end
it 'should convert int to hex and add a 32 bit mask' do
@resource[:set_mark] = '1000'
@resource[:set_mark].should == '0x3e8/0xffffffff'
end
it 'should add a 32 bit mask' do
@resource[:set_mark] = '0x32'
@resource[:set_mark].should == '0x32/0xffffffff'
end
it 'should use the mask provided' do
@resource[:set_mark] = '0x32/0x4'
@resource[:set_mark].should == '0x32/0x4'
end
it 'should use the mask provided and convert int to hex' do
@resource[:set_mark] = '1000/0x4'
@resource[:set_mark].should == '0x3e8/0x4'
end
it 'should fail if mask value is more than 32 bits' do
lambda { @resource[:set_mark] = '1/4294967296'}.should raise_error(
Puppet::Error, /MARK mask must be integer or hex between 0 and 0xffffffff$/
)
end
it 'should fail if mask is malformed' do
lambda { @resource[:set_mark] = '1000/0xq4'}.should raise_error(
Puppet::Error, /MARK mask must be integer or hex between 0 and 0xffffffff$/
)
end
end
['/', '1000/', 'pwnie'].each do |bad_mark|
it "should fail with malformed mark '#{bad_mark}'" do
lambda { @resource[:set_mark] = bad_mark}.should raise_error(Puppet::Error)
end
end
it 'should fail if mark value is more than 32 bits' do
lambda { @resource[:set_mark] = '4294967296'}.should raise_error(
Puppet::Error, /MARK value must be integer or hex between 0 and 0xffffffff$/
)
end
end
end
end
[:chain, :jump].each do |param|
describe param do
it 'should autorequire fwchain when table and provider are undefined' do
@resource[param] = 'FOO'
@resource[:table].should == :filter
@resource[:provider].should == :iptables
chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv4')
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource chain
rel = @resource.autorequire[0]
rel.source.ref.should == chain.ref
rel.target.ref.should == @resource.ref
end
it 'should autorequire fwchain when table is undefined and provider is ip6tables' do
@resource[param] = 'FOO'
@resource[:table].should == :filter
@resource[:provider] = :ip6tables
chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv6')
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource chain
rel = @resource.autorequire[0]
rel.source.ref.should == chain.ref
rel.target.ref.should == @resource.ref
end
it 'should autorequire fwchain when table is raw and provider is undefined' do
@resource[param] = 'FOO'
@resource[:table] = :raw
@resource[:provider].should == :iptables
chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:raw:IPv4')
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource chain
rel = @resource.autorequire[0]
rel.source.ref.should == chain.ref
rel.target.ref.should == @resource.ref
end
it 'should autorequire fwchain when table is raw and provider is ip6tables' do
@resource[param] = 'FOO'
@resource[:table] = :raw
@resource[:provider] = :ip6tables
chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:raw:IPv6')
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource chain
rel = @resource.autorequire[0]
rel.source.ref.should == chain.ref
rel.target.ref.should == @resource.ref
end
# test where autorequire is still needed (table != filter)
['INPUT', 'OUTPUT', 'FORWARD'].each do |test_chain|
it "should autorequire fwchain #{test_chain} when table is mangle and provider is undefined" do
@resource[param] = test_chain
@resource[:table] = :mangle
@resource[:provider].should == :iptables
chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:mangle:IPv4")
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource chain
rel = @resource.autorequire[0]
rel.source.ref.should == chain.ref
rel.target.ref.should == @resource.ref
end
it "should autorequire fwchain #{test_chain} when table is mangle and provider is ip6tables" do
@resource[param] = test_chain
@resource[:table] = :mangle
@resource[:provider] = :ip6tables
chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:mangle:IPv6")
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource chain
rel = @resource.autorequire[0]
rel.source.ref.should == chain.ref
rel.target.ref.should == @resource.ref
end
end
# test of case where autorequire should not happen
['INPUT', 'OUTPUT', 'FORWARD'].each do |test_chain|
it "should not autorequire fwchain #{test_chain} when table and provider are undefined" do
@resource[param] = test_chain
@resource[:table].should == :filter
@resource[:provider].should == :iptables
chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:filter:IPv4")
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource chain
rel = @resource.autorequire[0]
rel.should == nil
end
it "should not autorequire fwchain #{test_chain} when table is undefined and provider is ip6tables" do
@resource[param] = test_chain
@resource[:table].should == :filter
@resource[:provider] = :ip6tables
chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:filter:IPv6")
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource chain
rel = @resource.autorequire[0]
rel.should == nil
end
end
end
end
describe ":chain and :jump" do
it 'should autorequire independent fwchains' do
@resource[:chain] = 'FOO'
@resource[:jump] = 'BAR'
@resource[:table].should == :filter
@resource[:provider].should == :iptables
chain_foo = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv4')
chain_bar = Puppet::Type.type(:firewallchain).new(:name => 'BAR:filter:IPv4')
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource chain_foo
catalog.add_resource chain_bar
rel = @resource.autorequire
rel[0].source.ref.should == chain_foo.ref
rel[0].target.ref.should == @resource.ref
rel[1].source.ref.should == chain_bar.ref
rel[1].target.ref.should == @resource.ref
end
end
describe ':pkttype' do
[:multicast, :broadcast, :unicast].each do |pkttype|
it "should accept pkttype value #{pkttype}" do
@resource[:pkttype] = pkttype
@resource[:pkttype].should == pkttype
end
end
it 'should fail when the pkttype value is not recognized' do
lambda { @resource[:pkttype] = 'not valid' }.should raise_error(Puppet::Error)
end
end
describe 'autorequire packages' do
[:iptables, :ip6tables].each do |provider|
it "provider #{provider} should autorequire package iptables" do
@resource[:provider] = provider
@resource[:provider].should == provider
package = Puppet::Type.type(:package).new(:name => 'iptables')
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
catalog.add_resource package
rel = @resource.autorequire[0]
rel.source.ref.should == package.ref
rel.target.ref.should == @resource.ref
end
it "provider #{provider} should autorequire packages iptables and iptables-persistent" do
@resource[:provider] = provider
@resource[:provider].should == provider
packages = [
Puppet::Type.type(:package).new(:name => 'iptables'),
Puppet::Type.type(:package).new(:name => 'iptables-persistent')
]
catalog = Puppet::Resource::Catalog.new
catalog.add_resource @resource
packages.each do |package|
catalog.add_resource package
end
packages.zip(@resource.autorequire) do |package, rel|
rel.source.ref.should == package.ref
rel.target.ref.should == @resource.ref
end
end
end
end
end

View file

@ -0,0 +1,185 @@
#!/usr/bin/env rspec
require 'spec_helper'
firewallchain = Puppet::Type.type(:firewallchain)
describe firewallchain do
before(:each) do
# Stub confine facts
allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux')
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Debian')
end
let(:klass) { firewallchain }
let(:provider) {
prov = double 'provider'
allow(prov).to receive(:name).and_return(:iptables_chain)
prov
}
let(:resource) {
allow(Puppet::Type::Firewallchain).to receive(:defaultprovider).and_return provider
klass.new({:name => 'INPUT:filter:IPv4', :policy => :accept })
}
it 'should have :name be its namevar' do
klass.key_attributes.should == [:name]
end
describe ':name' do
{'nat' => ['PREROUTING', 'POSTROUTING', 'INPUT', 'OUTPUT'],
'mangle' => [ 'PREROUTING', 'POSTROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ],
'filter' => ['INPUT','OUTPUT','FORWARD'],
'raw' => [ 'PREROUTING', 'OUTPUT'],
'broute' => ['BROUTING']
}.each_pair do |table, allowedinternalchains|
['IPv4', 'IPv6', 'ethernet'].each do |protocol|
[ 'test', '$5()*&%\'"^$09):' ].each do |chainname|
name = "#{chainname}:#{table}:#{protocol}"
if table == 'nat' && protocol == 'IPv6'
it "should fail #{name}" do
expect { resource[:name] = name }.to raise_error(Puppet::Error)
end
elsif protocol != 'ethernet' && table == 'broute'
it "should fail #{name}" do
expect { resource[:name] = name }.to raise_error(Puppet::Error)
end
else
it "should accept name #{name}" do
resource[:name] = name
resource[:name].should == name
end
end
end # chainname
end # protocol
[ 'PREROUTING', 'POSTROUTING', 'BROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ].each do |internalchain|
name = internalchain + ':' + table + ':'
if internalchain == 'BROUTING'
name += 'ethernet'
elsif table == 'nat'
name += 'IPv4'
else
name += 'IPv4'
end
if allowedinternalchains.include? internalchain
it "should allow #{name}" do
resource[:name] = name
resource[:name].should == name
end
else
it "should fail #{name}" do
expect { resource[:name] = name }.to raise_error(Puppet::Error)
end
end
end # internalchain
end # table, allowedinternalchainnames
it 'should fail with invalid table names' do
expect { resource[:name] = 'wrongtablename:test:IPv4' }.to raise_error(Puppet::Error)
end
it 'should fail with invalid protocols names' do
expect { resource[:name] = 'test:filter:IPv5' }.to raise_error(Puppet::Error)
end
end
describe ':policy' do
[:accept, :drop, :queue, :return].each do |policy|
it "should accept policy #{policy}" do
resource[:policy] = policy
resource[:policy].should == policy
end
end
it 'should fail when value is not recognized' do
expect { resource[:policy] = 'not valid' }.to raise_error(Puppet::Error)
end
[:accept, :drop, :queue, :return].each do |policy|
it "non-inbuilt chains should not accept policy #{policy}" do
expect { klass.new({:name => 'testchain:filter:IPv4', :policy => policy }) }.to raise_error(Puppet::Error)
end
it "non-inbuilt chains can accept policies on protocol = ethernet (policy #{policy})" do
klass.new({:name => 'testchain:filter:ethernet', :policy => policy })
end
end
end
describe 'autorequire packages' do
it "provider iptables_chain should autorequire package iptables" do
resource[:provider].should == :iptables_chain
package = Puppet::Type.type(:package).new(:name => 'iptables')
catalog = Puppet::Resource::Catalog.new
catalog.add_resource resource
catalog.add_resource package
rel = resource.autorequire[0]
rel.source.ref.should == package.ref
rel.target.ref.should == resource.ref
end
it "provider iptables_chain should autorequire packages iptables and iptables-persistent" do
resource[:provider].should == :iptables_chain
packages = [
Puppet::Type.type(:package).new(:name => 'iptables'),
Puppet::Type.type(:package).new(:name => 'iptables-persistent')
]
catalog = Puppet::Resource::Catalog.new
catalog.add_resource resource
packages.each do |package|
catalog.add_resource package
end
packages.zip(resource.autorequire) do |package, rel|
rel.source.ref.should == package.ref
rel.target.ref.should == resource.ref
end
end
end
describe 'purge iptables rules' do
before(:each) do
allow(Puppet::Type.type(:firewall).provider(:iptables)).to receive(:iptables_save).and_return(<<EOS
# Completed on Sun Jan 5 19:30:21 2014
# Generated by iptables-save v1.4.12 on Sun Jan 5 19:30:21 2014
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:LOCAL_FORWARD - [0:0]
:LOCAL_FORWARD_PRE - [0:0]
:LOCAL_INPUT - [0:0]
:LOCAL_INPUT_PRE - [0:0]
:fail2ban-ssh - [0:0]
-A INPUT -p tcp -m multiport --dports 22 -j fail2ban-ssh
-A INPUT -i lo -m comment --comment "012 accept loopback" -j ACCEPT
-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "020 ssh" -j ACCEPT
-A OUTPUT -d 1.2.1.2 -j DROP
-A fail2ban-ssh -j RETURN
COMMIT
# Completed on Sun Jan 5 19:30:21 2014
EOS
)
end
it 'should generate iptables resources' do
resource = Puppet::Type::Firewallchain.new(:name => 'INPUT:filter:IPv4', :purge => true)
expect(resource.generate.size).to eq(3)
end
it 'should not generate ignored iptables rules' do
resource = Puppet::Type::Firewallchain.new(:name => 'INPUT:filter:IPv4', :purge => true, :ignore => ['-j fail2ban-ssh'])
expect(resource.generate.size).to eq(2)
end
it 'should not generate iptables resources when not enabled' do
resource = Puppet::Type::Firewallchain.new(:name => 'INPUT:filter:IPv4')
expect(resource.generate.size).to eq(0)
end
end
end

View file

@ -0,0 +1,197 @@
require 'spec_helper'
describe 'Puppet::Util::Firewall' do
let(:resource) {
type = Puppet::Type.type(:firewall)
provider = double 'provider'
allow(provider).to receive(:name).and_return(:iptables)
allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return(provider)
type.new({:name => '000 test foo'})
}
before(:each) { resource }
describe '#host_to_ip' do
subject { resource }
specify {
expect(Resolv).to receive(:getaddress).with('puppetlabs.com').and_return('96.126.112.51')
subject.host_to_ip('puppetlabs.com').should == '96.126.112.51/32'
}
specify { subject.host_to_ip('96.126.112.51').should == '96.126.112.51/32' }
specify { subject.host_to_ip('96.126.112.51/32').should == '96.126.112.51/32' }
specify { subject.host_to_ip('2001:db8:85a3:0:0:8a2e:370:7334').should == '2001:db8:85a3::8a2e:370:7334/128' }
specify { subject.host_to_ip('2001:db8:1234::/48').should == '2001:db8:1234::/48' }
specify { subject.host_to_ip('0.0.0.0/0').should == nil }
specify { subject.host_to_ip('::/0').should == nil }
end
describe '#host_to_mask' do
subject { resource }
specify {
expect(Resolv).to receive(:getaddress).at_least(:once).with('puppetlabs.com').and_return('96.126.112.51')
subject.host_to_mask('puppetlabs.com').should == '96.126.112.51/32'
subject.host_to_mask('!puppetlabs.com').should == '! 96.126.112.51/32'
}
specify { subject.host_to_mask('96.126.112.51').should == '96.126.112.51/32' }
specify { subject.host_to_mask('!96.126.112.51').should == '! 96.126.112.51/32' }
specify { subject.host_to_mask('96.126.112.51/32').should == '96.126.112.51/32' }
specify { subject.host_to_mask('! 96.126.112.51/32').should == '! 96.126.112.51/32' }
specify { subject.host_to_mask('2001:db8:85a3:0:0:8a2e:370:7334').should == '2001:db8:85a3::8a2e:370:7334/128' }
specify { subject.host_to_mask('!2001:db8:85a3:0:0:8a2e:370:7334').should == '! 2001:db8:85a3::8a2e:370:7334/128' }
specify { subject.host_to_mask('2001:db8:1234::/48').should == '2001:db8:1234::/48' }
specify { subject.host_to_mask('! 2001:db8:1234::/48').should == '! 2001:db8:1234::/48' }
specify { subject.host_to_mask('0.0.0.0/0').should == nil }
specify { subject.host_to_mask('!0.0.0.0/0').should == nil }
specify { subject.host_to_mask('::/0').should == nil }
specify { subject.host_to_mask('! ::/0').should == nil }
end
describe '#icmp_name_to_number' do
describe 'proto unsupported' do
subject { resource }
%w{inet5 inet8 foo}.each do |proto|
it "should reject invalid proto #{proto}" do
expect { subject.icmp_name_to_number('echo-reply', proto) }.
to raise_error(ArgumentError, "unsupported protocol family '#{proto}'")
end
end
end
describe 'proto IPv4' do
proto = 'inet'
subject { resource }
specify { subject.icmp_name_to_number('echo-reply', proto).should == '0' }
specify { subject.icmp_name_to_number('destination-unreachable', proto).should == '3' }
specify { subject.icmp_name_to_number('source-quench', proto).should == '4' }
specify { subject.icmp_name_to_number('redirect', proto).should == '6' }
specify { subject.icmp_name_to_number('echo-request', proto).should == '8' }
specify { subject.icmp_name_to_number('router-advertisement', proto).should == '9' }
specify { subject.icmp_name_to_number('router-solicitation', proto).should == '10' }
specify { subject.icmp_name_to_number('time-exceeded', proto).should == '11' }
specify { subject.icmp_name_to_number('parameter-problem', proto).should == '12' }
specify { subject.icmp_name_to_number('timestamp-request', proto).should == '13' }
specify { subject.icmp_name_to_number('timestamp-reply', proto).should == '14' }
specify { subject.icmp_name_to_number('address-mask-request', proto).should == '17' }
specify { subject.icmp_name_to_number('address-mask-reply', proto).should == '18' }
end
describe 'proto IPv6' do
proto = 'inet6'
subject { resource }
specify { subject.icmp_name_to_number('destination-unreachable', proto).should == '1' }
specify { subject.icmp_name_to_number('time-exceeded', proto).should == '3' }
specify { subject.icmp_name_to_number('parameter-problem', proto).should == '4' }
specify { subject.icmp_name_to_number('echo-request', proto).should == '128' }
specify { subject.icmp_name_to_number('echo-reply', proto).should == '129' }
specify { subject.icmp_name_to_number('router-solicitation', proto).should == '133' }
specify { subject.icmp_name_to_number('router-advertisement', proto).should == '134' }
specify { subject.icmp_name_to_number('redirect', proto).should == '137' }
end
end
describe '#string_to_port' do
subject { resource }
specify { subject.string_to_port('80','tcp').should == '80' }
specify { subject.string_to_port(80,'tcp').should == '80' }
specify { subject.string_to_port('http','tcp').should == '80' }
specify { subject.string_to_port('domain','udp').should == '53' }
end
describe '#to_hex32' do
subject { resource }
specify { subject.to_hex32('0').should == '0x0' }
specify { subject.to_hex32('0x32').should == '0x32' }
specify { subject.to_hex32('42').should == '0x2a' }
specify { subject.to_hex32('4294967295').should == '0xffffffff' }
specify { subject.to_hex32('4294967296').should == nil }
specify { subject.to_hex32('-1').should == nil }
specify { subject.to_hex32('bananas').should == nil }
end
describe '#persist_iptables' do
before { Facter.clear }
subject { resource }
describe 'when proto is IPv4' do
let(:proto) { 'IPv4' }
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/iptables.init save})
subject.persist_iptables(proto)
end
it 'should exec for CentOS identified from operatingsystem' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS')
expect(subject).to receive(:execute).with(%w{/sbin/service iptables save})
subject.persist_iptables(proto)
end
it 'should exec for Archlinux identified from osfamily' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return('Archlinux')
expect(subject).to receive(:execute).with(['/bin/sh', '-c', '/usr/sbin/iptables-save > /etc/iptables/iptables.rules'])
subject.persist_iptables(proto)
end
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')
expect(subject).to receive(:warning).with('Unable to persist firewall rules: some error')
subject.persist_iptables(proto)
end
end
describe 'when proto is IPv6' do
let(:proto) { 'IPv6' }
it 'should exec for newer Ubuntu' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu')
allow(Facter.fact(:iptables_persistent_version)).to receive(:value).and_return('0.5.3ubuntu2')
expect(subject).to receive(:execute).with(%w{/usr/sbin/service iptables-persistent save})
subject.persist_iptables(proto)
end
it 'should not exec for older Ubuntu which does not support IPv6' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu')
allow(Facter.fact(:iptables_persistent_version)).to receive(:value).and_return('0.0.20090701')
expect(subject).to receive(:execute).never
subject.persist_iptables(proto)
end
it 'should not exec for Suse which is not supported' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return('Suse')
expect(subject).to receive(:execute).never
subject.persist_iptables(proto)
end
end
end
end

View file

@ -0,0 +1,67 @@
require 'spec_helper'
describe 'Puppet::Util::IPCidr' do
describe 'ipv4 address' do
before { @ipaddr = Puppet::Util::IPCidr.new('96.126.112.51') }
subject { @ipaddr }
specify { subject.cidr.should == '96.126.112.51/32' }
specify { subject.prefixlen.should == 32 }
specify { subject.netmask.should == '255.255.255.255' }
end
describe 'single ipv4 address with cidr' do
before { @ipcidr = Puppet::Util::IPCidr.new('96.126.112.51/32') }
subject { @ipcidr }
specify { subject.cidr.should == '96.126.112.51/32' }
specify { subject.prefixlen.should == 32 }
specify { subject.netmask.should == '255.255.255.255' }
end
describe 'ipv4 address range with cidr' do
before { @ipcidr = Puppet::Util::IPCidr.new('96.126.112.0/24') }
subject { @ipcidr }
specify { subject.cidr.should == '96.126.112.0/24' }
specify { subject.prefixlen.should == 24 }
specify { subject.netmask.should == '255.255.255.0' }
end
describe 'ipv4 open range with cidr' do
before { @ipcidr = Puppet::Util::IPCidr.new('0.0.0.0/0') }
subject { @ipcidr }
specify { subject.cidr.should == '0.0.0.0/0' }
specify { subject.prefixlen.should == 0 }
specify { subject.netmask.should == '0.0.0.0' }
end
describe 'ipv6 address' do
before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:85a3:0:0:8a2e:370:7334') }
subject { @ipaddr }
specify { subject.cidr.should == '2001:db8:85a3::8a2e:370:7334/128' }
specify { subject.prefixlen.should == 128 }
specify { subject.netmask.should == 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' }
end
describe 'single ipv6 addr with cidr' do
before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:85a3:0:0:8a2e:370:7334/128') }
subject { @ipaddr }
specify { subject.cidr.should == '2001:db8:85a3::8a2e:370:7334/128' }
specify { subject.prefixlen.should == 128 }
specify { subject.netmask.should == 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' }
end
describe 'ipv6 addr range with cidr' do
before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:1234::/48') }
subject { @ipaddr }
specify { subject.cidr.should == '2001:db8:1234::/48' }
specify { subject.prefixlen.should == 48 }
specify { subject.netmask.should == 'ffff:ffff:ffff:0000:0000:0000:0000:0000' }
end
describe 'ipv6 open range with cidr' do
before { @ipaddr = Puppet::Util::IPCidr.new('::/0') }
subject { @ipaddr }
specify { subject.cidr.should == '::/0' }
specify { subject.prefixlen.should == 0 }
specify { subject.netmask.should == '0000:0000:0000:0000:0000:0000:0000:0000' }
end
end