diff options
author | Thore Bödecker <me@foxxx0.de> | 2019-09-03 11:56:58 +0200 |
---|---|---|
committer | Thore Bödecker <me@foxxx0.de> | 2019-09-11 13:20:35 +0200 |
commit | 882a45498ddefdfc83ff5b19da723fd0be3acdec (patch) | |
tree | 523f7a96c5b2640dbf2dd45cd89d931e12ceff9d /spec | |
parent | 81748ba786c6a55c4575a400c08de99716da8fbb (diff) | |
download | puppet-ferm-882a45498ddefdfc83ff5b19da723fd0be3acdec.tar.gz puppet-ferm-882a45498ddefdfc83ff5b19da723fd0be3acdec.tar.bz2 |
add ability to define rules in tables != filter
Previously it was neither possible to properly define custom chains nor
to define rules in tables other than the default filter table.
For various legitimate reasons it can be required to define rules in the
raw, nat or mangle tables, e.g. to use NOTRACK or to configure
DNAT/SNAT/MASQUERADE.
Additionally it might come in handy to define custom chains to group
certain rules and allow a more efficient evaluation for incoming packets
by not cramming all rules into the filter/INPUT chain so that
(worst-case) all packets need to traverse and evaluate all rules.
I have tried to maintain backwards compatibility and to not change
default filenames/paths so that it won't result in leftover obsolete unmaged
files from previous versions of this module.
In order to improve the naming schema the rule $policy has been renamed
to $action, however both parameters are available and optional now, with
some sanity checks that require at most one of them and issueing a
warning() for users of the now deprecated $policy parameter.
All previous tests have been adapted to the changes, a long with an
additional set of tests for the new feature.
Fixes #61
Diffstat (limited to 'spec')
-rw-r--r-- | spec/acceptance/ferm_spec.rb | 92 | ||||
-rw-r--r-- | spec/classes/ferm_spec.rb | 61 | ||||
-rw-r--r-- | spec/defines/chain_spec.rb | 30 | ||||
-rw-r--r-- | spec/defines/rule_spec.rb | 119 | ||||
-rw-r--r-- | spec/spec_helper.rb | 5 |
5 files changed, 272 insertions, 35 deletions
diff --git a/spec/acceptance/ferm_spec.rb b/spec/acceptance/ferm_spec.rb index 1b0f794..b0c41a5 100644 --- a/spec/acceptance/ferm_spec.rb +++ b/spec/acceptance/ferm_spec.rb @@ -12,27 +12,29 @@ manage_initfile = case sut_os false end +basic_manifest = %( + class { 'ferm': + manage_service => true, + manage_configfile => true, + manage_initfile => #{manage_initfile}, # CentOS-6 does not provide init script + forward_policy => 'DROP', + output_policy => 'DROP', + input_policy => 'DROP', + rules => { + 'allow_acceptance_tests' => { + chain => 'INPUT', + action => 'ACCEPT', + proto => tcp, + dport => 22, + }, + }, + ip_versions => ['ip'], #only ipv4 available with CI + } +) + describe 'ferm' do context 'with basics settings' do - pp = %( - class { 'ferm': - manage_service => true, - manage_configfile => true, - manage_initfile => #{manage_initfile}, # CentOS-6 does not provide init script - forward_policy => 'DROP', - output_policy => 'DROP', - input_policy => 'DROP', - rules => { - 'allow acceptance_tests' => { - chain => 'INPUT', - policy => 'ACCEPT', - proto => tcp, - dport => 22, - }, - }, - ip_versions => ['ip'], #only ipv4 available with CI - } - ) + pp = basic_manifest it 'works with no error' do apply_manifest(pp, catch_failures: true) @@ -54,7 +56,57 @@ describe 'ferm' do end describe iptables do - it { is_expected.to have_rule('-A INPUT -p tcp -m comment --comment "allow acceptance_tests" -m tcp --dport 22 -j ACCEPT').with_table('filter').with_chain('INPUT') } + it do + is_expected.to have_rule('-A INPUT -p tcp -m comment --comment ["]*allow_acceptance_tests["]* -m tcp --dport 22 -j ACCEPT'). \ + with_table('filter'). \ + with_chain('INPUT') + end + end + + context 'with custom chains' do + advanced_manifest = %( + ferm::chain { 'check-http': + chain => 'HTTP', + disable_conntrack => true, + log_dropped_packets => false, + } + ferm::rule { 'jump_http': + chain => 'INPUT', + action => 'HTTP', + proto => 'tcp', + dport => '80', + require => Ferm::Chain['check-http'], + } + ferm::rule { 'allow_http_localhost': + chain => 'HTTP', + action => 'ACCEPT', + proto => 'tcp', + dport => '80', + saddr => '127.0.0.1', + require => Ferm::Chain['check-http'], + } + ) + pp = [basic_manifest, advanced_manifest].join("\n") + + it 'works with no error' do + apply_manifest(pp, catch_failures: true) + end + it 'works idempotently' do + apply_manifest(pp, catch_changes: true) + end + + describe iptables do + it do + is_expected.to have_rule('-A INPUT -p tcp -m comment --comment ["]*jump_http["]* -m tcp --dport 80 -j HTTP'). \ + with_table('filter'). \ + with_chain('INPUT') + end + it do + is_expected.to have_rule('-A HTTP -s 127.0.0.1/32 -p tcp -m comment --comment ["]*allow_http_localhost["]* -m tcp --dport 80 -j ACCEPT'). \ + with_table('filter'). \ + with_chain('HTTP') + end + end end end end diff --git a/spec/classes/ferm_spec.rb b/spec/classes/ferm_spec.rb index e5669b8..225577b 100644 --- a/spec/classes/ferm_spec.rb +++ b/spec/classes/ferm_spec.rb @@ -64,6 +64,17 @@ describe 'ferm' do is_expected.to contain_concat__fragment('ferm.conf'). \ without_content(%r{@preserve;}) end + it { is_expected.to contain_concat__fragment('raw-PREROUTING-config-include') } + it { is_expected.to contain_concat__fragment('raw-OUTPUT-config-include') } + it { is_expected.to contain_concat__fragment('nat-PREROUTING-config-include') } + it { is_expected.to contain_concat__fragment('nat-INPUT-config-include') } + it { is_expected.to contain_concat__fragment('nat-OUTPUT-config-include') } + it { is_expected.to contain_concat__fragment('nat-POSTROUTING-config-include') } + it { is_expected.to contain_concat__fragment('mangle-PREROUTING-config-include') } + it { is_expected.to contain_concat__fragment('mangle-INPUT-config-include') } + it { is_expected.to contain_concat__fragment('mangle-FORWARD-config-include') } + it { is_expected.to contain_concat__fragment('mangle-OUTPUT-config-include') } + it { is_expected.to contain_concat__fragment('mangle-POSTROUTING-config-include') } end context 'with managed initfile' do let :params do @@ -77,18 +88,62 @@ describe 'ferm' do end end context 'it creates chains' do - it { is_expected.to contain_concat__fragment('FORWARD-policy') } - it { is_expected.to contain_concat__fragment('INPUT-policy') } - it { is_expected.to contain_concat__fragment('OUTPUT-policy') } + it { is_expected.to contain_concat__fragment('raw-PREROUTING-policy') } + it { is_expected.to contain_concat__fragment('raw-OUTPUT-policy') } + it { is_expected.to contain_concat__fragment('nat-PREROUTING-policy') } + it { is_expected.to contain_concat__fragment('nat-INPUT-policy') } + it { is_expected.to contain_concat__fragment('nat-OUTPUT-policy') } + it { is_expected.to contain_concat__fragment('nat-POSTROUTING-policy') } + it { is_expected.to contain_concat__fragment('mangle-PREROUTING-policy') } + it { is_expected.to contain_concat__fragment('mangle-INPUT-policy') } + it { is_expected.to contain_concat__fragment('mangle-FORWARD-policy') } + it { is_expected.to contain_concat__fragment('mangle-OUTPUT-policy') } + it { is_expected.to contain_concat__fragment('mangle-POSTROUTING-policy') } + it { is_expected.to contain_concat__fragment('filter-INPUT-policy') } + it { is_expected.to contain_concat__fragment('filter-FORWARD-policy') } + it { is_expected.to contain_concat__fragment('filter-OUTPUT-policy') } if facts[:os]['release']['major'].to_i == 10 + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/raw-PREROUTING.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/raw-OUTPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/nat-PREROUTING.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/nat-INPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/nat-OUTPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/nat-POSTROUTING.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/mangle-PREROUTING.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/mangle-INPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/mangle-FORWARD.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/mangle-OUTPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/mangle-POSTROUTING.conf') } it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/FORWARD.conf') } it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/INPUT.conf') } it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/OUTPUT.conf') } else + it { is_expected.to contain_concat('/etc/ferm.d/chains/raw-PREROUTING.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/raw-OUTPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/nat-PREROUTING.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/nat-INPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/nat-OUTPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/nat-POSTROUTING.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/mangle-PREROUTING.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/mangle-INPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/mangle-FORWARD.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/mangle-OUTPUT.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/mangle-POSTROUTING.conf') } it { is_expected.to contain_concat('/etc/ferm.d/chains/FORWARD.conf') } it { is_expected.to contain_concat('/etc/ferm.d/chains/INPUT.conf') } it { is_expected.to contain_concat('/etc/ferm.d/chains/OUTPUT.conf') } end + it { is_expected.to contain_ferm__chain('raw-PREROUTING') } + it { is_expected.to contain_ferm__chain('raw-OUTPUT') } + it { is_expected.to contain_ferm__chain('nat-PREROUTING') } + it { is_expected.to contain_ferm__chain('nat-INPUT') } + it { is_expected.to contain_ferm__chain('nat-OUTPUT') } + it { is_expected.to contain_ferm__chain('nat-POSTROUTING') } + it { is_expected.to contain_ferm__chain('mangle-PREROUTING') } + it { is_expected.to contain_ferm__chain('mangle-INPUT') } + it { is_expected.to contain_ferm__chain('mangle-FORWARD') } + it { is_expected.to contain_ferm__chain('mangle-OUTPUT') } + it { is_expected.to contain_ferm__chain('mangle-POSTROUTING') } it { is_expected.to contain_ferm__chain('FORWARD') } it { is_expected.to contain_ferm__chain('OUTPUT') } it { is_expected.to contain_ferm__chain('INPUT') } diff --git a/spec/defines/chain_spec.rb b/spec/defines/chain_spec.rb index 9425821..4a598b3 100644 --- a/spec/defines/chain_spec.rb +++ b/spec/defines/chain_spec.rb @@ -15,25 +15,25 @@ describe 'ferm::chain', type: :define do context 'default params creates INPUT2 chain' do let :params do { - policy: 'DROP', disable_conntrack: false, log_dropped_packets: true } end it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('filter-INPUT2-config-include') } it do - is_expected.to contain_concat__fragment('INPUT2-policy'). \ + is_expected.to contain_concat__fragment('filter-INPUT2-policy'). \ with_content(%r{ESTABLISHED RELATED}) end it do - is_expected.to contain_concat__fragment('INPUT2-footer'). \ + is_expected.to contain_concat__fragment('filter-INPUT2-footer'). \ with_content(%r{LOG log-prefix 'INPUT2: ';}) end if facts[:os]['release']['major'].to_i == 10 - it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/INPUT2.conf') } + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/filter-INPUT2.conf') } else - it { is_expected.to contain_concat('/etc/ferm.d/chains/INPUT2.conf') } + it { is_expected.to contain_concat('/etc/ferm.d/chains/filter-INPUT2.conf') } end it { is_expected.to contain_ferm__chain('INPUT2') } end @@ -41,7 +41,6 @@ describe 'ferm::chain', type: :define do context 'without conntrack' do let :params do { - policy: 'DROP', disable_conntrack: true, log_dropped_packets: false } @@ -49,15 +48,28 @@ describe 'ferm::chain', type: :define do it { is_expected.to compile.with_all_deps } it do - is_expected.to contain_concat__fragment('INPUT2-policy') - is_expected.not_to contain_concat__fragment('INPUT2-policy'). \ + is_expected.to contain_concat__fragment('filter-INPUT2-policy') + is_expected.not_to contain_concat__fragment('filter-INPUT2-policy'). \ with_content(%r{ESTABLISHED RELATED}) end it do - is_expected.not_to contain_concat__fragment('INPUT2-footer'). \ + is_expected.not_to contain_concat__fragment('filter-INPUT2-footer'). \ with_content(%r{LOG log-prefix 'INPUT2: ';}) end end + + context 'with policy setting for custom chain' do + let :params do + { + chain: 'INPUT2', + policy: 'DROP', + disable_conntrack: true, + log_dropped_packets: false + } + end + + it { is_expected.to compile.and_raise_error(%r{Can only set a default policy for builtin chains}) } + end end end end diff --git a/spec/defines/rule_spec.rb b/spec/defines/rule_spec.rb index 1bec758..ef20e17 100644 --- a/spec/defines/rule_spec.rb +++ b/spec/defines/rule_spec.rb @@ -11,7 +11,37 @@ describe 'ferm::rule', type: :define do 'include ferm' end - context 'without a specific interface' do + context 'without policy or action' do + let(:title) { 'filter-ssh' } + let :params do + { + chain: 'INPUT', + proto: 'tcp', + dport: '22', + saddr: '127.0.0.1' + } + end + + it { is_expected.to compile.and_raise_error(%r{Exactly one of "action" or the deprecated "policy" param is required}) } + end + + context 'with both policy and action' do + let(:title) { 'filter-ssh' } + let :params do + { + chain: 'INPUT', + policy: 'ACCEPT', + action: 'ACCEPT', + proto: 'tcp', + dport: '22', + saddr: '127.0.0.1' + } + end + + it { is_expected.to compile.and_raise_error(%r{Cannot specify both policy and action}) } + end + + context 'without a specific interface using legacy policy param' do let(:title) { 'filter-ssh' } let :params do { @@ -26,12 +56,32 @@ describe 'ferm::rule', type: :define do it { is_expected.to compile.with_all_deps } it { is_expected.to contain_concat__fragment('INPUT-filter-ssh').with_content("mod comment comment 'filter-ssh' proto tcp dport 22 saddr @ipfilter((127.0.0.1)) ACCEPT;\n") } end + + context 'without a specific interface' do + let(:title) { 'filter-ssh' } + let :params do + { + chain: 'INPUT', + action: 'ACCEPT', + proto: 'tcp', + dport: '22', + saddr: '127.0.0.1' + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('INPUT-filter-ssh').with_content("mod comment comment 'filter-ssh' proto tcp dport 22 saddr @ipfilter((127.0.0.1)) ACCEPT;\n") } + it { is_expected.to contain_concat__fragment('filter-INPUT-config-include') } + it { is_expected.to contain_concat__fragment('filter-FORWARD-config-include') } + it { is_expected.to contain_concat__fragment('filter-OUTPUT-config-include') } + end + context 'with a specific interface' do let(:title) { 'filter-ssh' } let :params do { chain: 'INPUT', - policy: 'ACCEPT', + action: 'ACCEPT', proto: 'tcp', dport: '22', saddr: '127.0.0.1', @@ -44,12 +94,13 @@ describe 'ferm::rule', type: :define do it { is_expected.to contain_concat__fragment('INPUT-eth0-aaa').with_content("interface eth0 {\n") } it { is_expected.to contain_concat__fragment('INPUT-eth0-zzz').with_content("}\n") } end + context 'with a specific interface using array for daddr' do let(:title) { 'filter-ssh' } let :params do { chain: 'INPUT', - policy: 'ACCEPT', + action: 'ACCEPT', proto: 'tcp', dport: '22', daddr: ['127.0.0.1', '123.123.123.123', ['10.0.0.1', '10.0.0.2']], @@ -62,6 +113,68 @@ describe 'ferm::rule', type: :define do it { is_expected.to contain_concat__fragment('INPUT-eth0-aaa').with_content("interface eth0 {\n") } it { is_expected.to contain_concat__fragment('INPUT-eth0-zzz').with_content("}\n") } end + + context 'with jumping to custom chains' do + # create custom chain + let(:pre_condition) do + 'include ferm ; + ferm::chain{"check-ssh": + chain => "SSH", + disable_conntrack => true, + log_dropped_packets => false, + }' + end + let(:title) { 'filter-ssh' } + let :params do + { + chain: 'INPUT', + action: 'SSH', + proto: 'tcp', + dport: '22' + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('filter-SSH-policy') } + it do + is_expected.to contain_concat__fragment('INPUT-filter-ssh').\ + with_content("mod comment comment 'filter-ssh' proto tcp dport 22 jump SSH;\n"). \ + that_requires('Ferm::Chain[check-ssh]') + end + it { is_expected.to contain_concat__fragment('filter-INPUT-config-include') } + if facts[:os]['release']['major'].to_i == 10 + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/filter-SSH.conf') } + else + it { is_expected.to contain_concat('/etc/ferm.d/chains/filter-SSH.conf') } + end + end + + context 'definining rules in custom chains' do + # create custom chain + let(:pre_condition) do + 'include ferm ; + ferm::chain{"check-ssh": + chain => "SSH", + disable_conntrack => true, + log_dropped_packets => false, + }' + end + let(:title) { 'allow-ssh-localhost' } + let :params do + { + chain: 'SSH', + action: 'ACCEPT', + proto: 'tcp', + dport: '22', + saddr: '127.0.0.1' + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('SSH-allow-ssh-localhost').with_content("mod comment comment 'allow-ssh-localhost' proto tcp dport 22 saddr @ipfilter((127.0.0.1)) ACCEPT;\n") } + it { is_expected.to contain_concat__fragment('filter-INPUT-config-include') } + it { is_expected.to contain_concat__fragment('filter-SSH-config-include') } + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f16fb15..96f14d5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -10,6 +10,11 @@ require 'rspec-puppet-facts' require 'bundler' include RspecPuppetFacts +if ENV['DEBUG'] + Puppet::Util::Log.level = :debug + Puppet::Util::Log.newdestination(:console) +end + if File.exist?(File.join(__dir__, 'default_module_facts.yml')) facts = YAML.load(File.read(File.join(__dir__, 'default_module_facts.yml'))) if facts |