509 changes: 509 additions & 0 deletions README.md

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ end

def changelog_project
return unless Rake.application.top_level_tasks.include? "changelog"
returnVal = nil || JSON.load(File.read('metadata.json'))['source'].match(%r{.*/([^/]*)})[1]
raise "unable to find the changelog_project in .sync.yml or the name in metadata.json" if returnVal.nil?

returnVal = nil
returnVal ||= begin
metadata_source = JSON.load(File.read('metadata.json'))['source']
metadata_source_match = metadata_source && metadata_source.match(%r{.*\/([^\/]*?)(?:\.git)?\Z})

metadata_source_match && metadata_source_match[1]
end

raise "unable to find the changelog_project in .sync.yml or calculate it from the source in metadata.json" if returnVal.nil?

puts "GitHubChangelogGenerator project:#{returnVal}"
returnVal
end
Expand Down
25 changes: 25 additions & 0 deletions distelli-manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
team-modules/puppetlabs-firewall:
PreBuild:
- source /opt/rh/rh-ruby25/enable
- echo "--- LETS update BUNDLER ---"
- bundle install --path vendor/bundle --jobs 3
Build:
- echo "--- PROVISIONING ---"
- source /opt/rh/rh-ruby25/enable
- bundle exec rake litmus:provision_list[release_checks]
- cat inventory.yaml
- echo "--- AGENT INSTALLATION ---"
- bundle exec rake litmus:install_agent
- echo "--- MODULE INSTALLATION ---"
- bundle exec rake litmus:install_module
- echo "--- TESTS RUNNING ---"
- bundle exec rake litmus:acceptance:parallel
AfterBuildSuccess:
- source /opt/rh/rh-ruby25/enable
- bundle exec rake litmus:tear_down
AfterBuildFailure:
- source /opt/rh/rh-ruby25/enable
- bundle exec rake litmus:tear_down
CommitData:
- RepoType: Git
- RepoPath: .
3 changes: 2 additions & 1 deletion lib/facter/iptables_persistent_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
os = Facter.value(:operatingsystem)
os_release = Facter.value(:operatingsystemrelease)
cmd = if (os == 'Debian' && (Puppet::Util::Package.versioncmp(os_release, '8.0') >= 0)) ||
(os == 'Ubuntu' && (Puppet::Util::Package.versioncmp(os_release, '14.10') >= 0))
(os == 'Ubuntu' && (Puppet::Util::Package.versioncmp(os_release, '14.10') >= 0)) ||
(os == 'Debian' && (Puppet::Util::Package.versioncmp(os_release, 'unstable') >= 0))
"dpkg-query -Wf '${Version}' netfilter-persistent 2>/dev/null"
else
"dpkg-query -Wf '${Version}' iptables-persistent 2>/dev/null"
Expand Down
4 changes: 2 additions & 2 deletions lib/puppet/provider/firewall/iptables.rb
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ def self.rule_to_hash(line, table, counter)
if values =~ %r{-m comment --comment}
ind = values.index('-m comment --comment')
comments = values.scan(%r{-m comment --comment "((?:\\"|[^"])*)"})
comments += values.scan(%r{-m comment --comment ([^"]+?)\b})
comments += values.scan(%r{-m comment --comment ([^"\s]+)\b})
values = values.gsub(%r{-m comment --comment (".*?[^\\"]"|[^ ].*)( |$)}, '')
values = values.gsub(%r{-m comment --comment ([^"].*?)[ $]}, '')
values.insert(ind, "-m comment --comment \"#{comments.join(';')}\" ")
Expand Down Expand Up @@ -783,8 +783,8 @@ def general_args
end
if should_negate
resource_value, wrong_values = resource_value.map { |value|
# rubocop:disable Metrics/BlockNesting
if value.is_a?(String)
# rubocop:disable Metrics/BlockNesting
wrong = value unless value =~ %r{^!\s+}
[value.sub(%r{^!\s*}, ''), wrong]
else
Expand Down
5 changes: 3 additions & 2 deletions lib/puppet/provider/firewallchain/iptables_chain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ def destroy
if chain =~ INTERNAL_CHAINS
# can't delete internal chains
warning "Attempting to destroy internal chain #{@resource[:name]}"
else
debug "Deleting chain #{chain} on table #{table}"
t.call ['-t', table, '-X', chain]
end
debug "Deleting chain #{chain} on table #{table}"
t.call ['-t', table, '-X', chain]
end
end

Expand Down
2 changes: 1 addition & 1 deletion manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
}
contain "${title}::linux"
}
'FreeBSD': {
'FreeBSD', 'windows': {
}
default: {
fail("${title}: Kernel '${::kernel}' is not currently supported")
Expand Down
5 changes: 4 additions & 1 deletion manifests/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@
$service_name_v6 = undef
case $::operatingsystem {
'Debian': {
if versioncmp($::operatingsystemrelease, '8.0') >= 0 {
if versioncmp($::operatingsystemrelease, 'unstable') >= 0 {
$service_name = 'netfilter-persistent'
$package_name = 'netfilter-persistent'
} elsif versioncmp($::operatingsystemrelease, '8.0') >= 0 {
$service_name = 'netfilter-persistent'
$package_name = 'iptables-persistent'
} else {
Expand Down
12 changes: 7 additions & 5 deletions metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "puppetlabs-firewall",
"version": "2.1.0",
"version": "2.2.0",
"author": "puppetlabs",
"summary": "Manages Firewalls such as iptables",
"license": "Apache-2.0",
Expand Down Expand Up @@ -28,7 +28,8 @@
"operatingsystemrelease": [
"5",
"6",
"7"
"7",
"8"
]
},
{
Expand Down Expand Up @@ -57,7 +58,8 @@
"operatingsystem": "Debian",
"operatingsystemrelease": [
"8",
"9"
"9",
"10"
]
},
{
Expand All @@ -76,6 +78,6 @@
}
],
"template-url": "https://github.com/puppetlabs/pdk-templates#master",
"template-ref": "1.12.0-0-g55d9ae2",
"pdk-version": "1.12.0"
"template-ref": "heads/master-0-g643529a",
"pdk-version": "1.14.1"
}
16 changes: 16 additions & 0 deletions provision.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
default:
provisioner: docker
images: ['waffleimage/centos7']
waffle_deb:
provisioner: docker_exp
images: ['waffleimage/debian8', 'waffleimage/debian9', 'waffleimage/ubuntu14.04', 'waffleimage/ubuntu16.04', 'waffleimage/ubuntu18.04']
waffle_el:
provisioner: docker
images: ['waffleimage/centos7']
vagrant:
provisioner: vagrant
images: ['centos/7', 'generic/ubuntu1804']
release_checks:
provisioner: vmpooler
images: ['redhat-5-x86_64', 'redhat-6-x86_64', 'redhat-7-x86_64', 'redhat-8-x86_64', 'centos-5-x86_64', 'centos-6-x86_64', 'centos-7-x86_64', 'centos-8-x86_64', 'oracle-6-x86_64', 'scientific-6-x86_64', 'scientific-7-x86_64', 'debian-8-x86_64', 'debian-9-x86_64', 'debian-10-x86_64', 'sles-11-x86_64', 'sles-12-x86_64', 'sles-15-x86_64', 'ubuntu-1404-x86_64', 'ubuntu-1604-x86_64', 'ubuntu-1804-x86_64']
27 changes: 9 additions & 18 deletions spec/acceptance/class_spec.rb
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
require 'spec_helper_acceptance'

describe 'firewall class' do
before(:all) do
if os[:family] == 'ubuntu' || os[:family] == 'debian'
update_profile_file
end
end

it 'runs successfully' do
pp = "class { 'firewall': }"

# Run it twice and test for idempotency
apply_manifest(pp, catch_failures: true)
if do_catch_changes
expect(apply_manifest(pp, catch_failures: true).exit_code).to be_zero
end
idempotent_apply(pp)
end

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

# Run it twice and test for idempotency
apply_manifest(pp, catch_failures: true)
if do_catch_changes
expect(apply_manifest(pp, catch_failures: true).exit_code).to be_zero
end
idempotent_apply(pp)
end

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

# Run it twice and test for idempotency
apply_manifest(pp, catch_failures: true)
if do_catch_changes
expect(apply_manifest(pp, catch_failures: true).exit_code).to be_zero
end
idempotent_apply(pp)
end
end
285 changes: 147 additions & 138 deletions spec/acceptance/firewall_attributes_exceptions_spec.rb

Large diffs are not rendered by default.

141 changes: 47 additions & 94 deletions spec/acceptance/firewall_attributes_happy_path_spec.rb

Large diffs are not rendered by default.

109 changes: 55 additions & 54 deletions spec/acceptance/firewall_attributes_ipv6_exceptions_spec.rb

Large diffs are not rendered by default.

76 changes: 26 additions & 50 deletions spec/acceptance/firewall_attributes_ipv6_happy_path_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class { '::firewall': }
firewall { '571 - hop_limit':
ensure => present,
proto => tcp,
port => '571',
dport => '571',
action => accept,
hop_limit => '5',
provider => 'ip6tables',
Expand All @@ -31,47 +31,47 @@ class { '::firewall': }
firewall { '587 - ishasmorefrags true':
ensure => present,
proto => tcp,
port => '587',
dport => '587',
action => accept,
ishasmorefrags => true,
provider => 'ip6tables',
}
firewall { '588 - ishasmorefrags false':
ensure => present,
proto => tcp,
port => '588',
dport => '588',
action => accept,
ishasmorefrags => false,
provider => 'ip6tables',
}
firewall { '589 - islastfrag true':
ensure => present,
proto => tcp,
port => '589',
dport => '589',
action => accept,
islastfrag => true,
provider => 'ip6tables',
}
firewall { '590 - islastfrag false':
ensure => present,
proto => tcp,
port => '590',
dport => '590',
action => accept,
islastfrag => false,
provider => 'ip6tables',
}
firewall { '591 - isfirstfrag true':
ensure => present,
proto => tcp,
port => '591',
dport => '591',
action => accept,
isfirstfrag => true,
provider => 'ip6tables',
}
firewall { '592 - isfirstfrag false':
ensure => present,
proto => tcp,
port => '592',
dport => '592',
action => accept,
isfirstfrag => false,
provider => 'ip6tables',
Expand All @@ -84,14 +84,14 @@ class { '::firewall': }
}
firewall { '601 - src_range':
proto => tcp,
port => '601',
dport => '601',
action => accept,
src_range => '2001:db8::1-2001:db8::ff',
provider => 'ip6tables',
}
firewall { '602 - dst_range':
proto => tcp,
port => '602',
dport => '602',
action => accept,
dst_range => '2001:db8::1-2001:db8::ff',
provider => 'ip6tables',
Expand All @@ -106,7 +106,7 @@ class { '::firewall': }
firewall { '605 - socket true':
ensure => present,
proto => tcp,
port => '605',
dport => '605',
action => accept,
chain => 'INPUT',
socket => true,
Expand All @@ -115,7 +115,7 @@ class { '::firewall': }
firewall { '606 - socket false':
ensure => present,
proto => tcp,
port => '606',
dport => '606',
action => accept,
chain => 'INPUT',
socket => false,
Expand Down Expand Up @@ -173,7 +173,7 @@ class { '::firewall': }
ensure => present,
chain => 'OUTPUT',
proto => tcp,
port => '611',
dport => '611',
jump => 'MARK',
table => 'mangle',
set_mark => '0x3e8/0xffffffff',
Expand Down Expand Up @@ -262,52 +262,51 @@ class { '::firewall': }
provider => 'ip6tables',
}
PUPPETCODE
apply_manifest(pp, catch_failures: true)
apply_manifest(pp, catch_changes: do_catch_changes)
idempotent_apply(pp)
end
let(:result) { shell('ip6tables-save') }
let(:result) { run_shell('ip6tables-save') }

it 'hop_limit is set' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --ports 571 -m hl --hl-eq 5 -m comment --comment "571 - hop_limit" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --dports 571 -m hl --hl-eq 5 -m comment --comment "571 - hop_limit" -j ACCEPT})
end
it 'checksum_fill is set' do
expect(result.stdout).to match(%r{-A POSTROUTING -o virbr0 -p udp -m multiport --dports 68 -m comment --comment "576 - checksum_fill" -j CHECKSUM --checksum-fill})
end
it 'ishasmorefrags when true' do
expect(result.stdout).to match(%r{A INPUT -p tcp -m frag --fragid 0 --fragmore -m multiport --ports 587 -m comment --comment "587 - ishasmorefrags true" -j ACCEPT})
expect(result.stdout).to match(%r{A INPUT -p tcp -m frag --fragid 0 --fragmore -m multiport --dports 587 -m comment --comment "587 - ishasmorefrags true" -j ACCEPT})
end
it 'ishasmorefrags when false' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --ports 588 -m comment --comment "588 - ishasmorefrags false" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --dports 588 -m comment --comment "588 - ishasmorefrags false" -j ACCEPT})
end
it 'islastfrag when true' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m frag --fragid 0 --fraglast -m multiport --ports 589 -m comment --comment "589 - islastfrag true" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m frag --fragid 0 --fraglast -m multiport --dports 589 -m comment --comment "589 - islastfrag true" -j ACCEPT})
end
it 'islastfrag when false' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --ports 590 -m comment --comment "590 - islastfrag false" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --dports 590 -m comment --comment "590 - islastfrag false" -j ACCEPT})
end
it 'isfirstfrag when true' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m frag --fragid 0 --fragfirst -m multiport --ports 591 -m comment --comment "591 - isfirstfrag true" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m frag --fragid 0 --fragfirst -m multiport --dports 591 -m comment --comment "591 - isfirstfrag true" -j ACCEPT})
end
it 'isfirstfrag when false' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --ports 592 -m comment --comment "592 - isfirstfrag false" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --dports 592 -m comment --comment "592 - isfirstfrag false" -j ACCEPT})
end
it 'tcp_flags is set' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN ACK -m comment --comment "593 - tcpfrags" -j ACCEPT})
end
it 'src_range is set' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m iprange --src-range 2001:db8::1-2001:db8::ff -m multiport --ports 601 -m comment --comment "601 - src_range" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m iprange --src-range 2001:db8::1-2001:db8::ff -m multiport --dports 601 -m comment --comment "601 - src_range" -j ACCEPT})
end
it 'dst_range is set' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m iprange --dst-range 2001:db8::1-2001:db8::ff -m multiport --ports 602 -m comment --comment "602 - dst_range" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m iprange --dst-range 2001:db8::1-2001:db8::ff -m multiport --dports 602 -m comment --comment "602 - dst_range" -j ACCEPT})
end
it 'mac_source is set' do
expect(result.stdout).to match(%r{-A INPUT -s 2001:db8::1\/(128|ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff) -p tcp -m mac --mac-source 0A:1B:3C:4D:5E:6F -m comment --comment "604 - mac_source"})
end
it 'socket when true' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --ports 605 -m socket -m comment --comment "605 - socket true" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --dports 605 -m socket -m comment --comment "605 - socket true" -j ACCEPT})
end
it 'socket when false' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --ports 606 -m comment --comment "606 - socket false" -j ACCEPT})
expect(result.stdout).to match(%r{-A INPUT -p tcp -m multiport --dports 606 -m comment --comment "606 - socket false" -j ACCEPT})
end
it 'ipsec_policy when ipsec' do
expect(result.stdout).to match(
Expand All @@ -330,7 +329,7 @@ class { '::firewall': }
)
end
it 'set_mark is set' do
expect(result.stdout).to match(%r{-A OUTPUT -p tcp -m multiport --ports 611 -m comment --comment "611 - set_mark" -j MARK --set-xmark 0x3e8\/0xffffffff})
expect(result.stdout).to match(%r{-A OUTPUT -p tcp -m multiport --dports 611 -m comment --comment "611 - set_mark" -j MARK --set-xmark 0x3e8\/0xffffffff})
end
it 'dst_type when MULTICAST' do
expect(result.stdout).to match(%r{-A INPUT -p tcp -m addrtype\s--dst-type\sMULTICAST -m comment --comment "613 - dst_type MULTICAST" -j ACCEPT})
Expand Down Expand Up @@ -367,27 +366,4 @@ class { '::firewall': }
end
end
end

describe 'test CT target attributes which are not available on some OS', unless:
(os[:family] == 'redhat' && (os[:release].start_with?('5', '6') || host_inventory['facter']['os']['name'] == 'OracleLinux')) || (host_inventory['facter']['os']['family'] == 'Suse') do
before(:all) do
pp = <<-PUPPETCODE
firewall { '1100 - ct_target tests - zone':
proto => 'all',
zone => '4000',
jump => 'CT',
chain => 'PREROUTING',
table => 'raw',
}
PUPPETCODE
apply_manifest(pp, catch_failures: true)
apply_manifest(pp, catch_changes: do_catch_changes)
end

let(:result) { shell('iptables-save') }

it 'zone is set' do
expect(result.stdout).to match(%r{-A PREROUTING -m comment --comment "1100 - ct_target tests - zone" -j CT --zone 4000})
end
end
end
17 changes: 7 additions & 10 deletions spec/acceptance/firewallchain_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@
PUPPETCODE
it 'applies cleanly' do
# Run it twice and test for idempotency
apply_manifest(pp1, catch_failures: true)
apply_manifest(pp1, catch_changes: do_catch_changes)
idempotent_apply(pp1)
end

it 'finds the chain' do
shell('iptables-save') do |r|
run_shell('iptables-save') do |r|
expect(r.stdout).to match(%r{MY_CHAIN})
end
end
Expand All @@ -34,12 +33,11 @@
PUPPETCODE
it 'applies cleanly' do
# Run it twice and test for idempotency
apply_manifest(pp2, catch_failures: true)
apply_manifest(pp2, catch_changes: do_catch_changes)
idempotent_apply(pp2)
end

it 'fails to find the chain' do
shell('iptables-save') do |r|
run_shell('iptables-save') do |r|
expect(r.stdout).not_to match(%r{MY_CHAIN})
end
end
Expand Down Expand Up @@ -102,7 +100,7 @@

describe 'policy' do
after :all do
shell('iptables -t filter -P FORWARD ACCEPT')
run_shell('iptables -t filter -P FORWARD ACCEPT')
end

context 'when DROP' do
Expand All @@ -113,12 +111,11 @@
PUPPETCODE
it 'applies cleanly' do
# Run it twice and test for idempotency
apply_manifest(pp6, catch_failures: true)
apply_manifest(pp6, catch_changes: do_catch_changes)
idempotent_apply(pp6)
end

it 'finds the chain' do
shell('iptables-save') do |r|
run_shell('iptables-save') do |r|
expect(r.stdout).to match(%r{FORWARD DROP})
end
end
Expand Down
77 changes: 42 additions & 35 deletions spec/acceptance/resource_cmd_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@
describe 'puppet resource firewall command' do
before(:all) do
# In order to properly check stderr for anomalies we need to fix the deprecation warnings from puppet.conf.
config = shell('puppet config print config').stdout
shell("sed -i -e \'s/^templatedir.*$//\' #{config}")
config = run_shell('puppet config print config').stdout
run_shell("sed -i -e \'s/^templatedir.*$//\' #{config}")
run_shell('echo export LC_ALL=C > ~/.bashrc')
run_shell('source ~/.bashrc')
end

context 'when make sure it returns no errors when executed on a clean machine' do
let(:result) { shell('puppet resource firewall') }
run_shell('locale')
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, some boxes come with rules, that is normal
run_shell('locale')
expect(result.exit_code).to be_zero
expect(result.stderr).to be_empty
end
Expand All @@ -26,7 +30,7 @@
ip6tables_flush_all_tables
end

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# No rules, means no output thanks. And no errors as well.
Expand All @@ -38,10 +42,10 @@
context 'when accepts rules without comments' do
before(:all) do
iptables_flush_all_tables
shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80')
run_shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80')
end

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, testing preexisting rules, output is normal
Expand All @@ -53,10 +57,10 @@
context 'when accepts rules with invalid comments' do
before(:all) do
iptables_flush_all_tables
shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80 -m comment --comment "http"')
run_shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80 -m comment --comment "http"')
end

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, testing preexisting rules, output is normal
Expand All @@ -68,10 +72,10 @@
context 'when accepts rules with multiple comments', unless: (os[:family] == 'redhat' && os[:release].start_with?('5')) do
before(:all) do
iptables_flush_all_tables
shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80 -m comment --comment "http" -m comment --comment "http"')
run_shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80 -m comment --comment "http" -m comment --comment "http"')
end

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, testing preexisting rules, output is normal
Expand All @@ -83,12 +87,12 @@
context 'when accepts rules with negation' do
before :all do
iptables_flush_all_tables
shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535')
shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p udp -j MASQUERADE --to-ports 1024-65535')
shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE')
run_shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535')
run_shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p udp -j MASQUERADE --to-ports 1024-65535')
run_shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE')
end

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, testing preexisting rules, output is normal
Expand All @@ -100,10 +104,10 @@
context 'when accepts rules with match extension tcp flag' do
before :all do
iptables_flush_all_tables
shell('iptables -t mangle -A PREROUTING -d 1.2.3.4 -p tcp -m tcp -m multiport --dports 80,443,8140 -j MARK --set-mark 42')
run_shell('iptables -t mangle -A PREROUTING -d 1.2.3.4 -p tcp -m tcp -m multiport --dports 80,443,8140 -j MARK --set-mark 42')
end

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, testing preexisting rules, output is normal
Expand All @@ -116,12 +120,12 @@
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')
run_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', expect_failures: true)
run_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')
run_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

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, testing preexisting rules, output is normal
Expand All @@ -133,14 +137,14 @@
context 'when accepts rules with negation' do
before :all do
iptables_flush_all_tables
shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 -m policy --dir out --pol ipsec -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.1.0/24 -d 192.168.122.0/24 -i eth0 -m policy --dir in --pol ipsec --reqid 108 --proto esp -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.122.0/24 -d 192.168.1.0/24 -o eth0 -m policy --dir out --pol ipsec --reqid 108 --proto esp -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.201.1/32 -d 192.168.122.0/24 -i eth0 -m policy --dir in --pol ipsec --reqid 107 --proto esp -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.122.0/24 -d 192.168.201.1/32 -o eth0 -m policy --dir out --pol ipsec --reqid 107 --proto esp -j ACCEPT')
run_shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 -m policy --dir out --pol ipsec -j ACCEPT')
run_shell('iptables -t filter -A FORWARD -s 192.168.1.0/24 -d 192.168.122.0/24 -i eth0 -m policy --dir in --pol ipsec --reqid 108 --proto esp -j ACCEPT')
run_shell('iptables -t filter -A FORWARD -s 192.168.122.0/24 -d 192.168.1.0/24 -o eth0 -m policy --dir out --pol ipsec --reqid 108 --proto esp -j ACCEPT')
run_shell('iptables -t filter -A FORWARD -s 192.168.201.1/32 -d 192.168.122.0/24 -i eth0 -m policy --dir in --pol ipsec --reqid 107 --proto esp -j ACCEPT')
run_shell('iptables -t filter -A FORWARD -s 192.168.122.0/24 -d 192.168.201.1/32 -o eth0 -m policy --dir out --pol ipsec --reqid 107 --proto esp -j ACCEPT')
end

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, testing preexisting rules, output is normal
Expand All @@ -152,10 +156,10 @@
context 'when accepts rules with -m (tcp|udp) without dport/sport' do
before :all do
iptables_flush_all_tables
shell('iptables -A INPUT -s 10.0.0.0/8 -p udp -m udp -j ACCEPT')
run_shell('iptables -A INPUT -s 10.0.0.0/8 -p udp -m udp -j ACCEPT')
end

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, testing preexisting rules, output is normal
Expand All @@ -167,13 +171,16 @@
context 'when accepts rules with -m ttl' do
before :all do
iptables_flush_all_tables
shell('iptables -t nat -A OUTPUT -s 10.0.0.0/8 -p tcp -m ttl ! --ttl-eq 42 -j REDIRECT --to-ports 12299')
run_shell('iptables -A FORWARD -m ttl --ttl-gt 100 -j LOG')
end

let(:result) { shell('puppet resource firewall') }
let(:result) { run_shell('puppet resource firewall') }

it do
# Don't check stdout, testing preexisting rules, output is normal
puts "reslt = #{result}"
puts "resltexit_code = #{result.exit_code}"
puts "resltstderr = #{result.stderr}"
expect(result.exit_code).to be_zero
expect(result.stderr).to be_empty
end
Expand All @@ -185,15 +192,15 @@
context 'when dport/sport with ip6tables', unless: os[:family] == 'redhat' && os[:release].start_with?('5') do
before :all do
if os['family'] == 'debian'
shell('echo "iptables-persistent iptables-persistent/autosave_v4 boolean false" | debconf-set-selections')
shell('echo "iptables-persistent iptables-persistent/autosave_v6 boolean false" | debconf-set-selections')
shell('apt-get install iptables-persistent -y')
run_shell('echo "iptables-persistent iptables-persistent/autosave_v4 boolean false" | debconf-set-selections')
run_shell('echo "iptables-persistent iptables-persistent/autosave_v6 boolean false" | debconf-set-selections')
run_shell('apt-get install iptables-persistent -y')
end
ip6tables_flush_all_tables
shell('ip6tables -A INPUT -d fe80::/64 -p tcp -m tcp --dport 546 --sport 547 -j ACCEPT -m comment --comment 000-foobar')
run_shell('ip6tables -A INPUT -d fe80::/64 -p tcp -m tcp --dport 546 --sport 547 -j ACCEPT -m comment --comment 000-foobar')
end

let(:result) { shell('puppet resource firewall \'000-foobar\' provider=ip6tables') }
let(:result) { run_shell('puppet resource firewall \000-foobar\ provider=ip6tables') }

it do
# Don't check stdout, testing preexisting rules, output is normal
Expand Down
40 changes: 21 additions & 19 deletions spec/acceptance/rules_spec.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
require 'spec_helper_acceptance'
require 'spec_helper_acceptance_local'

describe 'rules spec' do
describe 'complex ruleset 1' do
before :all do
if os[:family] == 'redhat'
pre_setup
end
iptables_flush_all_tables
ip6tables_flush_all_tables
end

after :all do
shell('iptables -t filter -P INPUT ACCEPT')
shell('iptables -t filter -P FORWARD ACCEPT')
shell('iptables -t filter -P OUTPUT ACCEPT')
shell('iptables -t filter --flush')
run_shell('iptables -t filter -P INPUT ACCEPT')
run_shell('iptables -t filter -P FORWARD ACCEPT')
run_shell('iptables -t filter -P OUTPUT ACCEPT')
run_shell('iptables -t filter --flush')
end

pp1 = <<-PUPPETCODE
Expand All @@ -28,15 +32,15 @@
destination => '!10.0.0.0/8',
proto => 'tcp',
ctstate => 'NEW',
port => [80,443,21,20,22,53,123,43,873,25,465],
sport => [80,443,21,20,22,53,123,43,873,25,465],
action => 'accept',
}
firewall { '100 forward standard allow udp':
chain => 'FORWARD',
source => '10.0.0.0/8',
destination => '!10.0.0.0/8',
proto => 'udp',
port => [53,123],
sport => [53,123],
action => 'accept',
}
firewall { '100 forward standard allow icmp':
Expand Down Expand Up @@ -87,24 +91,23 @@
chain => 'PREROUTING',
iniface => 'eth0',
proto => 'tcp',
dport => '1',
sport => '1',
toports => '22',
jump => 'REDIRECT',
}
PUPPETCODE
it 'applies cleanly' do
apply_manifest(pp1, catch_failures: true)
apply_manifest(pp1, catch_changes: true)
idempotent_apply(pp1)
end
regex_values = [
%r{INPUT ACCEPT}, %r{FORWARD ACCEPT}, %r{OUTPUT ACCEPT},
%r{-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) -d 10.0.0.0\/(8|255\.0\.0\.0) -m comment --comment \"090 forward allow local\" -j ACCEPT},
%r{-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p icmp -m comment --comment \"100 forward standard allow icmp\" -j ACCEPT},
%r{-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p tcp -m multiport --ports 80,443,21,20,22,53,123,43,873,25,465 -m conntrack --ctstate NEW -m comment --comment \"100 forward standard allow tcp\" -j ACCEPT}, # rubocop:disable Metrics/LineLength
%r{-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p udp -m multiport --ports 53,123 -m comment --comment \"100 forward standard allow udp\" -j ACCEPT}
%r{-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p tcp -m multiport --sports 80,443,21,20,22,53,123,43,873,25,465 -m conntrack --ctstate NEW -m comment --comment \"100 forward standard allow tcp\" -j ACCEPT}, # rubocop:disable Metrics/LineLength
%r{-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p udp -m multiport --sports 53,123 -m comment --comment \"100 forward standard allow udp\" -j ACCEPT}
]
it 'contains appropriate rules' do
shell('iptables-save') do |r|
run_shell('iptables-save') do |r|
regex_values.each do |line|
expect(r.stdout).to match(line)
end
Expand All @@ -114,10 +117,10 @@

describe 'complex ruleset 2' do
after :all do
shell('iptables -t filter -P INPUT ACCEPT')
shell('iptables -t filter -P FORWARD ACCEPT')
shell('iptables -t filter -P OUTPUT ACCEPT')
shell('iptables -t filter --flush')
run_shell('iptables -t filter -P INPUT ACCEPT')
run_shell('iptables -t filter -P FORWARD ACCEPT')
run_shell('iptables -t filter -P OUTPUT ACCEPT')
run_shell('iptables -t filter --flush')
end

pp2 = <<-PUPPETCODE
Expand Down Expand Up @@ -239,8 +242,7 @@ class { '::firewall': }
PUPPETCODE
it 'applies cleanly' do
# Run it twice and test for idempotency
apply_manifest(pp2, catch_failures: true)
apply_manifest(pp2, catch_changes: true)
idempotent_apply(pp2)
end

regex_values = [
Expand All @@ -264,7 +266,7 @@ class { '::firewall': }
%r{-A OUTPUT (! -o|-o !) eth0:2 -p tcp -m multiport --dports 25 -m conntrack --ctstate NEW -m comment --comment \"025 smtp\" -j ACCEPT},
]
it 'contains appropriate rules' do
shell('iptables-save') do |r|
run_shell('iptables-save') do |r|
regex_values.each do |line|
expect(r.stdout).to match(line)
end
Expand Down
3 changes: 1 addition & 2 deletions spec/acceptance/standard_usage_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class { 'firewall': }
PUPPETCODE
it 'applies twice' do
# Run it twice and test for idempotency
apply_manifest(pp, catch_failures: true)
apply_manifest(pp, catch_changes: do_catch_changes)
idempotent_apply(pp)
end
end
1 change: 1 addition & 0 deletions spec/default_facts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
# Facts specified here will override the values provided by rspec-puppet-facts.
---
ipaddress: "172.16.254.254"
ipaddress6: "FE80:0000:0000:0000:AAAA:AAAA:AAAA"
is_pe: false
macaddress: "AA:AA:AA:AA:AA:AA"
7 changes: 7 additions & 0 deletions spec/fixtures/iptables/conversion_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,13 @@
name: '9000 comment_without_quotes',
},
},
'comments_without_quotes' => {
line: '-A INPUT -s 192.168.0.1/32 -m comment --comment 100-comment_without-quotes',
table: 'filter',
params: {
name: '100-comment_without-quotes',
},
},
'string_escape_sequences' => {
line: '-A INPUT -m comment --comment "000 parse escaped \\"s, \\"s, \\\'s, \\\'s, \\\\s and \\\\s"',
table: 'filter',
Expand Down
106 changes: 72 additions & 34 deletions spec/spec_helper_acceptance.rb
Original file line number Diff line number Diff line change
@@ -1,42 +1,80 @@
require 'beaker-pe'
require 'beaker-puppet'
require 'beaker-rspec'
require 'beaker/puppet_install_helper'
require 'beaker/module_install_helper'
require 'serverspec'
require 'puppet_litmus'
require 'spec_helper_acceptance_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_acceptance_local.rb'))
include PuppetLitmus

def iptables_flush_all_tables
['filter', 'nat', 'mangle', 'raw'].each do |t|
expect(shell("iptables -t #{t} -F").stderr).to eq('')
if ENV['TARGET_HOST'].nil? || ENV['TARGET_HOST'] == 'localhost'
puts 'Running tests against this machine !'
if Gem.win_platform?
set :backend, :cmd
else
set :backend, :exec
end
end
else
# load inventory
inventory_hash = inventory_hash_from_inventory_file
node_config = config_from_node(inventory_hash, ENV['TARGET_HOST'])

def ip6tables_flush_all_tables
['filter', 'mangle', 'raw'].each do |t|
expect(shell("ip6tables -t #{t} -F").stderr).to eq('')
end
end
if target_in_group(inventory_hash, ENV['TARGET_HOST'], 'docker_nodes')
host = ENV['TARGET_HOST']
set :backend, :docker
set :docker_container, host
elsif target_in_group(inventory_hash, ENV['TARGET_HOST'], 'ssh_nodes')
set :backend, :ssh
options = Net::SSH::Config.for(host)
options[:user] = node_config.dig('ssh', 'user') unless node_config.dig('ssh', 'user').nil?
options[:port] = node_config.dig('ssh', 'port') unless node_config.dig('ssh', 'port').nil?
options[:keys] = node_config.dig('ssh', 'private-key') unless node_config.dig('ssh', 'private-key').nil?
options[:password] = node_config.dig('ssh', 'password') unless node_config.dig('ssh', 'password').nil?
# Support both net-ssh 4 and 5.
# rubocop:disable Metrics/BlockNesting
options[:verify_host_key] = if node_config.dig('ssh', 'host-key-check').nil?
# Fall back to SSH behavior. This variable will only be set in net-ssh 5.3+.
if @strict_host_key_checking.nil? || @strict_host_key_checking
Net::SSH::Verifiers::Always.new
else
# SSH's behavior with StrictHostKeyChecking=no: adds new keys to known_hosts.
# If known_hosts points to /dev/null, then equivalent to :never where it
# accepts any key beacuse they're all new.
Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
end
elsif node_config.dig('ssh', 'host-key-check')
if defined?(Net::SSH::Verifiers::Always)
Net::SSH::Verifiers::Always.new
else
Net::SSH::Verifiers::Secure.new
end
elsif defined?(Net::SSH::Verifiers::Never)
Net::SSH::Verifiers::Never.new
else
Net::SSH::Verifiers::Null.new
end

def do_catch_changes
if default['platform'] =~ %r{el-5}
false
else
true
end
end
host = if ENV['TARGET_HOST'].include?(':')
ENV['TARGET_HOST'].split(':').first
else
ENV['TARGET_HOST']
end
set :host, options[:host_name] || host
set :ssh_options, options
set :request_pty, true
elsif target_in_group(inventory_hash, ENV['TARGET_HOST'], 'winrm_nodes')
require 'winrm'

set :backend, :winrm
set :os, family: 'windows'
user = node_config.dig('winrm', 'user') unless node_config.dig('winrm', 'user').nil?
pass = node_config.dig('winrm', 'password') unless node_config.dig('winrm', 'password').nil?
endpoint = "http://#{ENV['TARGET_HOST']}:5985/wsman"

run_puppet_install_helper
configure_type_defaults_on(hosts)
install_module_on(hosts)
install_module_dependencies_on(hosts)
opts = {
user: user,
password: pass,
endpoint: endpoint,
operation_timeout: 300,
}

RSpec.configure do |c|
# Configure all nodes in nodeset
c.before :suite do
# Install module and dependencies
hosts.each do |host|
on host, puppet('module', 'install', 'puppetlabs-stdlib'), acceptable_exit_codes: [0]
# the ubuntu-14.04 docker image doesn't carry the iptables command
apply_manifest_on host, 'package { "iptables": ensure => installed }' if fact('osfamily') == 'Debian'
end
winrm = WinRM::Connection.new opts
Specinfra.configuration.winrm = winrm
end
end
33 changes: 33 additions & 0 deletions spec/spec_helper_acceptance_local.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
def iptables_flush_all_tables
['filter', 'nat', 'mangle', 'raw'].each do |t|
expect(run_shell("iptables -t #{t} -F").stderr).to eq('')
end
end

def ip6tables_flush_all_tables
['filter', 'mangle'].each do |t|
expect(run_shell("ip6tables -t #{t} -F").stderr).to eq('')
end
end

def install_iptables
run_shell('iptables -V')
rescue
run_shell('apt-get install iptables -y')
end

def iptables_version
install_iptables
x = run_shell('iptables -V')
x.stdout.split(' ')[1][1..-1]
end

def pre_setup
run_shell('mkdir -p /lib/modules/`uname -r`')
run_shell('depmod -a')
end

def update_profile_file
run_shell("sed -i '/mesg n/c\\test -t 0 && mesg n || true' ~/.profile")
run_shell("sed -i '/mesg n || true/c\\test -t 0 && mesg n || true' ~/.profile")
end
63 changes: 63 additions & 0 deletions spec/unit/classes/firewall_linux_debian_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,67 @@
)
}
end

context 'with Debian 10' do
let(:facts) do
{
osfamily: 'Debian',
operatingsystem: 'Debian',
operatingsystemrelease: '10.0',
}
end

it {
is_expected.to contain_package('iptables-persistent').with(
ensure: 'present',
)
}
it {
is_expected.to contain_service('netfilter-persistent').with(
ensure: nil,
enable: 'true',
require: 'Package[iptables-persistent]',
)
}
end

context 'with Debian 10, enable => false' do
let(:facts) do
{
osfamily: 'Debian',
operatingsystem: 'Debian',
operatingsystemrelease: '10',
}
end
let(:params) { { enable: 'false' } }

it {
is_expected.to contain_service('netfilter-persistent').with(
enable: 'false',
)
}
end

context 'with Debian unstable' do
let(:facts) do
{
osfamily: 'Debian',
operatingsystem: 'Debian',
operatingsystemrelease: 'unstable',
}
end

it {
is_expected.to contain_package('netfilter-persistent').with(
ensure: 'present',
)
}
it {
is_expected.to contain_service('netfilter-persistent').with(
ensure: nil,
enable: 'true',
require: 'Package[netfilter-persistent]',
)
}
end
end