commit b10c62165276b133767ca89024cbf866c89226a2
Author: Nacho Barrientos <nacho.barrientos@cern.ch>
Date:   Tue Nov 24 16:37:43 2020 +0100

    Set a customisable rate limit to the logging rules

diff --git a/manifests/inet_filter.pp b/manifests/inet_filter.pp
index c17d467..d8672f0 100644
--- a/manifests/inet_filter.pp
+++ b/manifests/inet_filter.pp
@@ -1,7 +1,12 @@
 # manage basic chains in table inet filter
 class nftables::inet_filter inherits nftables {
 
-  $_log_prefix_discard = sprintf($nftables::log_prefix, { 'chain' => '%<chain>s', 'comment' => 'Rejected: ' })
+  $_reject_rule = epp('nftables/reject_rule.epp',
+    {
+      'log_prefix' => sprintf($nftables::log_prefix, { 'chain' => '%<chain>s', 'comment' => 'Rejected: ' }),
+      'log_limit'  => $nftables::log_limit
+    }
+  )
 
   nftables::config{
     'inet-filter':
@@ -41,7 +46,7 @@ class nftables::inet_filter inherits nftables {
       content => 'jump global';
     'INPUT-log_discarded':
       order   => '97',
-      content => "log prefix \"${sprintf($_log_prefix_discard, { 'chain' => 'INPUT' })}\" flags all counter";
+      content => sprintf($_reject_rule, { 'chain' => 'INPUT' }),
   }
   if $nftables::reject_with {
     nftables::rule{
@@ -77,7 +82,7 @@ class nftables::inet_filter inherits nftables {
       content => 'jump global';
     'OUTPUT-log_discarded':
       order   => '97',
-      content => "log prefix \"${sprintf($_log_prefix_discard, { 'chain' => 'OUTPUT' })}\" flags all counter";
+      content => sprintf($_reject_rule, { 'chain' => 'OUTPUT' }),
   }
   if $nftables::reject_with {
     nftables::rule{
@@ -110,7 +115,7 @@ class nftables::inet_filter inherits nftables {
       content => 'jump global';
     'FORWARD-log_discarded':
       order   => '97',
-      content => "log prefix \"${sprintf($_log_prefix_discard, { 'chain' => 'FORWARD' })}\" flags all counter";
+      content => sprintf($_reject_rule, { 'chain' => 'FORWARD' }),
   }
   if $nftables::reject_with {
     nftables::rule{
diff --git a/manifests/init.pp b/manifests/init.pp
index 837db61..4ff0dd0 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -38,6 +38,11 @@
 #    * chain: Will be replaced by the name of the chain.
 #    * comment: Allows chains to add extra comments.
 #
+# @param log_limit
+#  String with the content of a limit statement to be applied
+#  to the rules that log discarded traffic. Set to false to
+#  disable rate limiting.
+#
 # @param reject_with
 #   How to discard packets not matching any rule. If `false`, the
 #   fate of the packet will be defined by the chain policy (normally
@@ -65,6 +70,8 @@ class nftables (
   Boolean $in_out_conntrack      = true,
   Hash $rules                    = {},
   String $log_prefix             = '[nftables] %<chain>s %<comment>s',
+  Variant[Boolean[false], String]
+    $log_limit                   = '3/minute burst 5 packets',
   Variant[Boolean[false], Pattern[
     /icmp(v6|x)? type .+|tcp reset/]]
     $reject_with                 = 'icmpx type port-unreachable',
diff --git a/spec/classes/inet_filter_spec.rb b/spec/classes/inet_filter_spec.rb
index 187da7d..774c3b9 100644
--- a/spec/classes/inet_filter_spec.rb
+++ b/spec/classes/inet_filter_spec.rb
@@ -111,7 +111,7 @@ describe 'nftables' do
         it {
           is_expected.to contain_concat__fragment('nftables-inet-filter-chain-INPUT-rule-log_discarded').with(
             target:  'nftables-inet-filter-chain-INPUT',
-            content: %r{^  log prefix \"\[nftables\] INPUT Rejected: \" flags all counter$},
+            content: %r{^  limit rate 3/minute burst 5 packets log prefix \"\[nftables\] INPUT Rejected: \" flags all counter$},
             order:   '97-nftables-inet-filter-chain-INPUT-rule-log_discarded-b',
           )
         }
@@ -234,7 +234,7 @@ describe 'nftables' do
         it {
           is_expected.to contain_concat__fragment('nftables-inet-filter-chain-OUTPUT-rule-log_discarded').with(
             target:  'nftables-inet-filter-chain-OUTPUT',
-            content: %r{^  log prefix \"\[nftables\] OUTPUT Rejected: \" flags all counter$},
+            content: %r{^  limit rate 3/minute burst 5 packets log prefix \"\[nftables\] OUTPUT Rejected: \" flags all counter$},
             order:   '97-nftables-inet-filter-chain-OUTPUT-rule-log_discarded-b',
           )
         }
@@ -370,7 +370,7 @@ describe 'nftables' do
         it {
           is_expected.to contain_concat__fragment('nftables-inet-filter-chain-FORWARD-rule-log_discarded').with(
             target:  'nftables-inet-filter-chain-FORWARD',
-            content: %r{^  log prefix \"\[nftables\] FORWARD Rejected: \" flags all counter$},
+            content: %r{^  limit rate 3/minute burst 5 packets log prefix \"\[nftables\] FORWARD Rejected: \" flags all counter$},
             order:   '97-nftables-inet-filter-chain-FORWARD-rule-log_discarded-b',
           )
         }
@@ -420,21 +420,21 @@ describe 'nftables' do
         it {
           is_expected.to contain_concat__fragment('nftables-inet-filter-chain-INPUT-rule-log_discarded').with(
             target:  'nftables-inet-filter-chain-INPUT',
-            content: %r{^  log prefix \"test " flags all counter$},
+            content: %r{^  limit rate 3/minute burst 5 packets log prefix \"test " flags all counter$},
             order:   '97-nftables-inet-filter-chain-INPUT-rule-log_discarded-b',
           )
         }
         it {
           is_expected.to contain_concat__fragment('nftables-inet-filter-chain-OUTPUT-rule-log_discarded').with(
             target:  'nftables-inet-filter-chain-OUTPUT',
-            content: %r{^  log prefix \"test " flags all counter$},
+            content: %r{^  limit rate 3/minute burst 5 packets log prefix \"test " flags all counter$},
             order:   '97-nftables-inet-filter-chain-OUTPUT-rule-log_discarded-b',
           )
         }
         it {
           is_expected.to contain_concat__fragment('nftables-inet-filter-chain-FORWARD-rule-log_discarded').with(
             target:  'nftables-inet-filter-chain-FORWARD',
-            content: %r{^  log prefix \"test " flags all counter$},
+            content: %r{^  limit rate 3/minute burst 5 packets log prefix \"test " flags all counter$},
             order:   '97-nftables-inet-filter-chain-FORWARD-rule-log_discarded-b',
           )
         }
@@ -446,21 +446,81 @@ describe 'nftables' do
         it {
           is_expected.to contain_concat__fragment('nftables-inet-filter-chain-INPUT-rule-log_discarded').with(
             target:  'nftables-inet-filter-chain-INPUT',
-            content: %r{^  log prefix \" bar \[INPUT\] " flags all counter$},
+            content: %r{^  limit rate 3/minute burst 5 packets log prefix \" bar \[INPUT\] " flags all counter$},
+            order:   '97-nftables-inet-filter-chain-INPUT-rule-log_discarded-b',
+          )
+        }
+        it {
+          is_expected.to contain_concat__fragment('nftables-inet-filter-chain-OUTPUT-rule-log_discarded').with(
+            target:  'nftables-inet-filter-chain-OUTPUT',
+            content: %r{^  limit rate 3/minute burst 5 packets log prefix \" bar \[OUTPUT\] " flags all counter$},
+            order:   '97-nftables-inet-filter-chain-OUTPUT-rule-log_discarded-b',
+          )
+        }
+        it {
+          is_expected.to contain_concat__fragment('nftables-inet-filter-chain-FORWARD-rule-log_discarded').with(
+            target:  'nftables-inet-filter-chain-FORWARD',
+            content: %r{^  limit rate 3/minute burst 5 packets log prefix \" bar \[FORWARD\] " flags all counter$},
+            order:   '97-nftables-inet-filter-chain-FORWARD-rule-log_discarded-b',
+          )
+        }
+      end
+
+      context 'no log limit' do
+        let(:params) do
+          {
+            'log_limit' => false,
+          }
+        end
+
+        it {
+          is_expected.to contain_concat__fragment('nftables-inet-filter-chain-INPUT-rule-log_discarded').with(
+            target:  'nftables-inet-filter-chain-INPUT',
+            content: %r{^  log prefix \"\[nftables\] INPUT Rejected: \" flags all counter$},
+            order:   '97-nftables-inet-filter-chain-INPUT-rule-log_discarded-b',
+          )
+        }
+        it {
+          is_expected.to contain_concat__fragment('nftables-inet-filter-chain-OUTPUT-rule-log_discarded').with(
+            target:  'nftables-inet-filter-chain-OUTPUT',
+            content: %r{^  log prefix \"\[nftables\] OUTPUT Rejected: \" flags all counter$},
+            order:   '97-nftables-inet-filter-chain-OUTPUT-rule-log_discarded-b',
+          )
+        }
+        it {
+          is_expected.to contain_concat__fragment('nftables-inet-filter-chain-FORWARD-rule-log_discarded').with(
+            target:  'nftables-inet-filter-chain-FORWARD',
+            content: %r{^  log prefix \"\[nftables\] FORWARD Rejected: \" flags all counter$},
+            order:   '97-nftables-inet-filter-chain-FORWARD-rule-log_discarded-b',
+          )
+        }
+      end
+
+      context 'custom log limit' do
+        let(:params) do
+          {
+            'log_limit' => '5/minute',
+          }
+        end
+
+        it {
+          is_expected.to contain_concat__fragment('nftables-inet-filter-chain-INPUT-rule-log_discarded').with(
+            target:  'nftables-inet-filter-chain-INPUT',
+            content: %r{^  limit rate 5/minute log prefix \"\[nftables\] INPUT Rejected: \" flags all counter$},
             order:   '97-nftables-inet-filter-chain-INPUT-rule-log_discarded-b',
           )
         }
         it {
           is_expected.to contain_concat__fragment('nftables-inet-filter-chain-OUTPUT-rule-log_discarded').with(
             target:  'nftables-inet-filter-chain-OUTPUT',
-            content: %r{^  log prefix \" bar \[OUTPUT\] " flags all counter$},
+            content: %r{^  limit rate 5/minute log prefix \"\[nftables\] OUTPUT Rejected: \" flags all counter$},
             order:   '97-nftables-inet-filter-chain-OUTPUT-rule-log_discarded-b',
           )
         }
         it {
           is_expected.to contain_concat__fragment('nftables-inet-filter-chain-FORWARD-rule-log_discarded').with(
             target:  'nftables-inet-filter-chain-FORWARD',
-            content: %r{^  log prefix \" bar \[FORWARD\] " flags all counter$},
+            content: %r{^  limit rate 5/minute log prefix \"\[nftables\] FORWARD Rejected: \" flags all counter$},
             order:   '97-nftables-inet-filter-chain-FORWARD-rule-log_discarded-b',
           )
         }
diff --git a/templates/reject_rule.epp b/templates/reject_rule.epp
new file mode 100644
index 0000000..4a1c9f9
--- /dev/null
+++ b/templates/reject_rule.epp
@@ -0,0 +1,5 @@
+<% if $log_limit { -%>
+limit rate <%= $log_limit %> log prefix "<%= $log_prefix %>" flags all counter
+<% } else { -%>
+log prefix "<%= $log_prefix %>" flags all counter
+<% } -%>
