]> gitweb.fluxo.info Git - puppet-ferm.git/commitdiff
Allow adding custom ferm dsl for subchains. This is important for using complex iptab...
authorRehan Mahmood <rehanone@gmail.com>
Mon, 4 May 2020 18:11:05 +0000 (14:11 -0400)
committerRehan Mahmood <rehanone@gmail.com>
Thu, 7 May 2020 04:27:49 +0000 (00:27 -0400)
REFERENCE.md
manifests/chain.pp
spec/acceptance/ferm_spec.rb
spec/defines/chain_spec.rb
templates/ferm_chain_custom.conf.epp [new file with mode: 0644]

index 2d0a4e3f318a8adfdc6d9f1f082a520c35dc158a..5c85d3871a887ae503da35fc9930846f24f78be7 100644 (file)
@@ -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.
index ed581266089a5c524e76cc8697377a089e87e308..91cd930732870dbee5b27ae35006d5f46010017e 100644 (file)
@@ -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", {
index 0dd2399f409a48dc390bf903fcd8206a7baa1750..8c5c454760062eb273820fdd76763cd58e83dc9c 100644 (file)
@@ -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
index 1a6bb44d4e703b57420ec66e2394fe49c97ee467..52cc88c0335fc7e70a60e0585d15214d30c111b4 100644 (file)
@@ -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 (file)
index 0000000..356311c
--- /dev/null
@@ -0,0 +1,4 @@
+<%- | String[1] $content,
+| -%>
+# THIS FILE IS MANAGED BY PUPPET
+<%= $content %>