diff options
author | Rehan Mahmood <rehanone@gmail.com> | 2020-05-04 14:11:05 -0400 |
---|---|---|
committer | Rehan Mahmood <rehanone@gmail.com> | 2020-05-07 00:27:49 -0400 |
commit | 6be13799d8a2ee49c3af88ffd7a474c39f1475e3 (patch) | |
tree | 77bc411088472f3e6db4e5095857d7cc550e0cb8 | |
parent | 6362585d53490ff9e837af1359d8e80f8053d0fc (diff) | |
download | puppet-ferm-6be13799d8a2ee49c3af88ffd7a474c39f1475e3.tar.gz puppet-ferm-6be13799d8a2ee49c3af88ffd7a474c39f1475e3.tar.bz2 |
Allow adding custom ferm dsl for subchains. This is important for using complex iptable rules that are currently not supported by this module or would be very hard to manage just using puppet.
-rw-r--r-- | REFERENCE.md | 36 | ||||
-rw-r--r-- | manifests/chain.pp | 54 | ||||
-rw-r--r-- | spec/acceptance/ferm_spec.rb | 63 | ||||
-rw-r--r-- | spec/defines/chain_spec.rb | 28 | ||||
-rw-r--r-- | templates/ferm_chain_custom.conf.epp | 4 |
5 files changed, 163 insertions, 22 deletions
diff --git a/REFERENCE.md b/REFERENCE.md index 2d0a4e3..5c85d38 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -243,6 +243,34 @@ ferm::chain{'check-ssh': } ``` +##### create a custom chain, e.g. for managing custom FORWARD chain rule for OpenVPN using custom ferm DSL. + +```puppet +$my_rules = @(EOT) +chain OPENVPN_FORWORD_RULES { + proto udp { + interface tun0 { + outerface enp4s0 { + mod conntrack ctstate (NEW) saddr @ipfilter((10.8.0.0/24)) ACCEPT; + } + } + } +} +| EOT + +ferm::chain{'OPENVPN_FORWORD_RULES': + chain => 'OPENVPN_FORWORD_RULES', + content => $my_rules, +} + +ferm::rule { "OpenVPN - FORWORD all udp traffic from network 10.8.0.0/24 to subchain OPENVPN_FORWORD_RULES": + chain => 'FORWARD', + action => 'OPENVPN_FORWORD_RULES', + saddr => '10.8.0.0/24', + proto => 'udp', +} +``` + #### Parameters The following parameters are available in the `ferm::chain` defined type. @@ -306,6 +334,14 @@ Set list of versions of ip we want ot use. Default value: $ferm::ip_versions +##### `content` + +Data type: `Optional[String]` + +Can only be used for custom chains. It allows you to provide your own ferm rules for this chain. Sets the contents of this custom chain to provided value. + +Default value: undef + ### ferm::ipset a defined resource that can match for ipsets at the top of a chain. This is a per-chain resource. You cannot mix IPv4 and IPv6 sets. diff --git a/manifests/chain.pp b/manifests/chain.pp index ed58126..91cd930 100644 --- a/manifests/chain.pp +++ b/manifests/chain.pp @@ -25,7 +25,8 @@ define ferm::chain ( String[1] $chain = $name, Optional[Ferm::Policies] $policy = undef, Ferm::Tables $table = 'filter', - Array[Enum['ip','ip6']] $ip_versions = $ferm::ip_versions, + Array[Enum['ip', 'ip6']] $ip_versions = $ferm::ip_versions, + Optional[String[1]] $content = undef, ) { # prevent unmanaged files due to new naming schema # keep the default "filter" chains in the original location @@ -43,32 +44,43 @@ define ferm::chain ( 'filter' => ['INPUT', 'FORWARD', 'OUTPUT'], } - if $policy and ! ($chain in $builtin_chains[$table]) { + if $policy and !($chain in $builtin_chains[$table]) { fail("Can only set a default policy for builtin chains. '${chain}' is not a builtin chain.") } # concat resource for the chain - concat{$filename: - ensure => 'present', + concat { $filename: + ensure => 'present', } - concat::fragment{"${table}-${chain}-policy": - target => $filename, - content => epp( - "${module_name}/ferm_chain_header.conf.epp", { - 'policy' => $policy, - 'disable_conntrack' => $disable_conntrack, - 'drop_invalid_packets_with_conntrack' => $drop_invalid_packets_with_conntrack, - } - ), - order => '01', - } - - if $log_dropped_packets { - concat::fragment{"${table}-${chain}-footer": + if $content { + concat::fragment { "${table}-${chain}-custom-content": target => $filename, - content => epp("${module_name}/ferm_chain_footer.conf.epp", { 'chain' => $chain }), - order => 'zzzzzzzzzzzzzzzzzzzzz', + content => epp( + "${module_name}/ferm_chain_custom.conf.epp", { + 'content' => $content, + }, + ), + } + } else { + concat::fragment { "${table}-${chain}-policy": + target => $filename, + content => epp( + "${module_name}/ferm_chain_header.conf.epp", { + 'policy' => $policy, + 'disable_conntrack' => $disable_conntrack, + 'drop_invalid_packets_with_conntrack' => $drop_invalid_packets_with_conntrack, + } + ), + order => '01', + } + + if $log_dropped_packets { + concat::fragment { "${table}-${chain}-footer": + target => $filename, + content => epp("${module_name}/ferm_chain_footer.conf.epp", { 'chain' => $chain }), + order => 'zzzzzzzzzzzzzzzzzzzzz', + } } } @@ -77,7 +89,7 @@ define ferm::chain ( # This happens if we add ipset matches. We suffix this ordering with `bbb`. This allows us to # insert ipset matches before other rules by adding `-aaa` or # insert them at the end by ordering them with `-ccc`. - concat::fragment{"${table}-${chain}-config-include": + concat::fragment { "${table}-${chain}-config-include": target => $ferm::configfile, content => epp( "${module_name}/ferm-table-chain-config-include.epp", { diff --git a/spec/acceptance/ferm_spec.rb b/spec/acceptance/ferm_spec.rb index 0dd2399..8c5c454 100644 --- a/spec/acceptance/ferm_spec.rb +++ b/spec/acceptance/ferm_spec.rb @@ -26,6 +26,10 @@ iptables_output = case sut_os '-A HTTP -s 127.0.0.1/32 -p tcp -m comment --comment ["]*allow_http_localhost["]* -m tcp --dport 80 -j ACCEPT' ] end + +iptables_output_custom = ['-A FORWARD -s 10.8.0.0/24 -p udp -m comment --comment "OpenVPN - FORWORD all udp traffic from network 10.8.0.0/24 to subchain OPENVPN_FORWORD_RULES" -j OPENVPN_FORWORD_RULES', + '-A OPENVPN_FORWORD_RULES -s 10.8.0.0/24 -i tun0 -o enp4s0 -p udp -m conntrack --ctstate NEW -j ACCEPT'] + basic_manifest = %( class { 'ferm': manage_service => true, @@ -124,7 +128,7 @@ describe 'ferm' do end end - context 'with dropping INVALID pakets' do + context 'with dropping INVALID packets' do pp2 = %( class { 'ferm': manage_service => true, @@ -162,4 +166,61 @@ describe 'ferm' do end end end + + context 'with custom chain using ferm DSL as content' do + advanced_manifest = %( + $my_rules = @(EOT) + chain OPENVPN_FORWORD_RULES { + proto udp { + interface tun0 { + outerface enp4s0 { + mod conntrack ctstate (NEW) saddr @ipfilter((10.8.0.0/24)) ACCEPT; + } + } + } + } + | EOT + + ferm::chain{'OPENVPN_FORWORD_RULES': + chain => 'OPENVPN_FORWORD_RULES', + content => $my_rules, + } + + ferm::rule { "OpenVPN - FORWORD all udp traffic from network 10.8.0.0/24 to subchain OPENVPN_FORWORD_RULES": + chain => 'FORWARD', + action => 'OPENVPN_FORWORD_RULES', + saddr => '10.8.0.0/24', + proto => 'udp', + } + ) + 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(iptables_output_custom[0]). \ + with_table('filter'). \ + with_chain('FORWARD') + end + it do + is_expected.to have_rule(iptables_output_custom[1]). \ + with_table('filter'). \ + with_chain('OPENVPN_FORWORD_RULES') + end + end + + describe service('ferm') do + it { is_expected.to be_running } + end + + describe command('iptables-save') do + its(:stdout) { is_expected.to match %r{FORWARD.*-j OPENVPN_FORWORD_RULES} } + end + end end diff --git a/spec/defines/chain_spec.rb b/spec/defines/chain_spec.rb index 1a6bb44..52cc88c 100644 --- a/spec/defines/chain_spec.rb +++ b/spec/defines/chain_spec.rb @@ -70,6 +70,34 @@ describe 'ferm::chain', type: :define do it { is_expected.to compile.and_raise_error(%r{Can only set a default policy for builtin chains}) } end + + context 'with custom chain FERM-DSL using content parameter' do + let(:title) { 'FERM-DSL' } + let :params do + { + content: 'mod rpfilter invert DROP;' + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_concat__fragment('filter-FERM-DSL-config-include') } + it do + is_expected.to contain_concat__fragment('filter-FERM-DSL-custom-content'). \ + with_content(%r{mod rpfilter invert DROP;}) + end + it do + is_expected.not_to contain_concat__fragment('filter-FERM-DSL-policy') + end + it do + is_expected.not_to contain_concat__fragment('filter-FERM-DSL-footer') + end + if facts[:os]['name'] == 'Debian' + it { is_expected.to contain_concat('/etc/ferm/ferm.d/chains/filter-FERM-DSL.conf') } + else + it { is_expected.to contain_concat('/etc/ferm.d/chains/filter-FERM-DSL.conf') } + end + it { is_expected.to contain_ferm__chain('FERM-DSL') } + end end end end diff --git a/templates/ferm_chain_custom.conf.epp b/templates/ferm_chain_custom.conf.epp new file mode 100644 index 0000000..356311c --- /dev/null +++ b/templates/ferm_chain_custom.conf.epp @@ -0,0 +1,4 @@ +<%- | String[1] $content, +| -%> +# THIS FILE IS MANAGED BY PUPPET +<%= $content %> |