commit fcb79d73f30dc98983f392fdf7c774c04301ebce
Author: Ben Morrice <ben.morrice@cern.ch>
Date:   Thu Jul 15 17:30:07 2021 +0200

    support a different table name for 'nat'
    - Some applications (such as libvirt) still use iptables to inject firewall
      rules
    - iptables will refuse to update tables that were initially created with nft
    - This commit allows defining the name of the 'nat' table in order to avoid
      namespace conflicts

diff --git a/manifests/init.pp b/manifests/init.pp
index a1a1d99..19a2112 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -43,6 +43,9 @@
 # @param nat
 #   Add default tables and chains to process NAT traffic.
 #
+# @param nat_table_name
+#   The name of the 'nat' table.
+#
 # @param sets
 #   Allows sourcing set definitions directly from Hiera.
 #
@@ -99,6 +102,7 @@ class nftables (
   Hash $rules = {},
   Hash $sets = {},
   String $log_prefix = '[nftables] %<chain>s %<comment>s',
+  String[1] $nat_table_name = 'nat',
   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',
   Variant[Boolean[false], Enum['mask']] $firewalld_enable = 'mask',
diff --git a/manifests/ip_nat.pp b/manifests/ip_nat.pp
index 68d66c7..3cfca28 100644
--- a/manifests/ip_nat.pp
+++ b/manifests/ip_nat.pp
@@ -1,9 +1,9 @@
 # manage basic chains in table ip nat
 class nftables::ip_nat inherits nftables {
   nftables::config {
-    'ip-nat':
+    "ip-${nftables::nat_table_name}":
       prefix => '';
-    'ip6-nat':
+    "ip6-${nftables::nat_table_name}":
       prefix => '';
   }
 
@@ -12,7 +12,7 @@ class nftables::ip_nat inherits nftables {
       'PREROUTING',
       'POSTROUTING',
     ]:
-      table => 'ip-nat';
+      table => "ip-${nftables::nat_table_name}";
   }
 
   nftables::chain {
@@ -20,25 +20,25 @@ class nftables::ip_nat inherits nftables {
       'PREROUTING6',
       'POSTROUTING6',
     ]:
-      table => 'ip6-nat';
+      table => "ip6-${nftables::nat_table_name}";
   }
 
   # ip-nat-chain-PREROUTING
   nftables::rule {
     'PREROUTING-type':
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       order   => '01',
       content => 'type nat hook prerouting priority -100';
     'PREROUTING-policy':
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       order   => '02',
       content => 'policy accept';
     'PREROUTING6-type':
-      table   => 'ip6-nat',
+      table   => "ip6-${nftables::nat_table_name}",
       order   => '01',
       content => 'type nat hook prerouting priority -100';
     'PREROUTING6-policy':
-      table   => 'ip6-nat',
+      table   => "ip6-${nftables::nat_table_name}",
       order   => '02',
       content => 'policy accept';
   }
@@ -46,19 +46,19 @@ class nftables::ip_nat inherits nftables {
   # ip-nat-chain-POSTROUTING
   nftables::rule {
     'POSTROUTING-type':
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       order   => '01',
       content => 'type nat hook postrouting priority 100';
     'POSTROUTING-policy':
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       order   => '02',
       content => 'policy accept';
     'POSTROUTING6-type':
-      table   => 'ip6-nat',
+      table   => "ip6-${nftables::nat_table_name}",
       order   => '01',
       content => 'type nat hook postrouting priority 100';
     'POSTROUTING6-policy':
-      table   => 'ip6-nat',
+      table   => "ip6-${nftables::nat_table_name}",
       order   => '02',
       content => 'policy accept';
   }
diff --git a/manifests/rules/dnat4.pp b/manifests/rules/dnat4.pp
index 964da0a..d5acddc 100644
--- a/manifests/rules/dnat4.pp
+++ b/manifests/rules/dnat4.pp
@@ -32,7 +32,7 @@ define nftables::rules::dnat4 (
     "${chain}-${rulename}":
       content => "${iifname}ip daddr ${daddr} ${proto} dport ${filter_port} accept";
     "PREROUTING-${rulename}":
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       content => "${iifname}${proto} dport ${port} dnat to ${daddr}${nat_port}";
   }
 }
diff --git a/manifests/rules/docker_ce.pp b/manifests/rules/docker_ce.pp
index 18b0b0a..15cedd0 100644
--- a/manifests/rules/docker_ce.pp
+++ b/manifests/rules/docker_ce.pp
@@ -79,45 +79,45 @@ class nftables::rules::docker_ce (
 
   if $manage_docker_chains {
     nftables::chain {
-      'DOCKER-nat':
-        table => 'ip-nat',
+      "DOCKER-${nftables::nat_table_name}":
+        table => "ip-${nftables::nat_table_name}",
         chain => 'DOCKER';
     }
   }
 
   if $manage_base_chains {
     nftables::chain {
-      'OUTPUT-nat':
-        table => 'ip-nat',
+      "OUTPUT-${nftables::nat_table_name}":
+        table => "ip-${nftables::nat_table_name}",
         chain => 'OUTPUT';
-      'INPUT-nat':
-        table => 'ip-nat',
+      "INPUT-${nftables::nat_table_name}":
+        table => "ip-${nftables::nat_table_name}",
         chain => 'INPUT';
     }
   }
 
   nftables::rule {
     'POSTROUTING-docker':
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       content => "oifname != \"${docker_interface}\" ip saddr ${docker_prefix} counter masquerade";
     'PREROUTING-docker':
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       content => 'fib daddr type local counter jump DOCKER';
-    'OUTPUT-jump_docker@ip-nat':
+    "OUTPUT-jump_docker@ip-${nftables::nat_table_name}":
       rulename => 'OUTPUT-jump_docker',
-      table    => 'ip-nat',
+      table    => "ip-${nftables::nat_table_name}",
       content  => 'ip daddr != 127.0.0.0/8 fib daddr type local counter jump DOCKER';
     'DOCKER-counter':
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       content => "iifname \"${docker_interface}\" counter return";
-    'INPUT-type@ip-nat':
+    "INPUT-type@ip-${nftables::nat_table_name}":
       rulename => 'INPUT-type',
-      table    => 'ip-nat',
+      table    => "ip-${nftables::nat_table_name}",
       order    => '01',
       content  => 'type nat hook input priority 100';
-    'INPUT-policy@ip-nat':
+    "INPUT-policy@ip-${nftables::nat_table_name}":
       rulename => 'INPUT-policy',
-      table    => 'ip-nat',
+      table    => "ip-${nftables::nat_table_name}",
       order    => '02',
       content  => 'policy accept';
   }
diff --git a/manifests/rules/masquerade.pp b/manifests/rules/masquerade.pp
index 4d2c2c3..0960271 100644
--- a/manifests/rules/masquerade.pp
+++ b/manifests/rules/masquerade.pp
@@ -42,7 +42,7 @@ define nftables::rules::masquerade (
   nftables::rule {
     "${chain}-${rulename}":
       ensure  => $ensure,
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       order   => $order,
       content => "${oifname}${src}${dst}${protocol}${port}masquerade";
   }
diff --git a/manifests/rules/qemu.pp b/manifests/rules/qemu.pp
index 5686a35..05fb61a 100644
--- a/manifests/rules/qemu.pp
+++ b/manifests/rules/qemu.pp
@@ -93,19 +93,19 @@ class nftables::rules::qemu (
   if $masquerade {
     nftables::rule {
       'POSTROUTING-qemu_ignore_multicast':
-        table   => 'ip-nat',
+        table   => "ip-${nftables::nat_table_name}",
         content => "ip saddr ${network_v4} ip daddr 224.0.0.0/24 return";
       'POSTROUTING-qemu_ignore_broadcast':
-        table   => 'ip-nat',
+        table   => "ip-${nftables::nat_table_name}",
         content => "ip saddr ${network_v4} ip daddr 255.255.255.255 return";
       'POSTROUTING-qemu_masq_tcp':
-        table   => 'ip-nat',
+        table   => "ip-${nftables::nat_table_name}",
         content => "meta l4proto tcp ip saddr ${network_v4} ip daddr != ${network_v4} masquerade to :1024-65535";
       'POSTROUTING-qemu_masq_udp':
-        table   => 'ip-nat',
+        table   => "ip-${nftables::nat_table_name}",
         content => "meta l4proto udp ip saddr ${network_v4} ip daddr != ${network_v4} masquerade to :1024-65535";
       'POSTROUTING-qemu_masq_ip':
-        table   => 'ip-nat',
+        table   => "ip-${nftables::nat_table_name}",
         content => "ip saddr ${network_v4} ip daddr != ${network_v4} masquerade";
     }
   }
diff --git a/manifests/rules/snat4.pp b/manifests/rules/snat4.pp
index 1d10027..83acf45 100644
--- a/manifests/rules/snat4.pp
+++ b/manifests/rules/snat4.pp
@@ -38,7 +38,7 @@ define nftables::rules::snat4 (
   nftables::rule {
     "${chain}-${rulename}":
       ensure  => $ensure,
-      table   => 'ip-nat',
+      table   => "ip-${nftables::nat_table_name}",
       order   => $order,
       content => "${oifname}${src}${protocol}${port}snat ${snat}";
   }
diff --git a/spec/acceptance/default_spec.rb b/spec/acceptance/default_spec.rb
index b16eb4c..716e791 100644
--- a/spec/acceptance/default_spec.rb
+++ b/spec/acceptance/default_spec.rb
@@ -111,4 +111,34 @@ describe 'nftables class' do
       it { is_expected.to be_enabled }
     end
   end
+  context 'with custom nat_table_name' do
+    it 'no rules validate okay' do
+      pp = <<-EOS
+      class{'nftables':
+        firewalld_enable => false,
+        nat => true,
+        nat_table_name => 'mycustomtablename',
+      }
+      # nftables cannot be started in docker so replace service with a validation only.
+      systemd::dropin_file{"zzz_docker_nft.conf":
+        ensure  => present,
+        unit    => "nftables.service",
+        content => [
+          "[Service]",
+          "ExecStart=",
+          "ExecStart=/sbin/nft -c -I /etc/nftables/puppet -f /etc/sysconfig/nftables.conf",
+          "ExecReload=",
+          "ExecReload=/sbin/nft -c -I /etc/nftables/puppet -f /etc/sysconfig/nftables.conf",
+          "",
+          ].join("\n"),
+        notify  => Service["nftables"],
+      }
+      EOS
+      apply_manifest(pp, catch_failures: true)
+    end
+    describe service('nftables') do
+      it { is_expected.to be_running }
+      it { is_expected.to be_enabled }
+    end
+  end
 end
diff --git a/spec/classes/ip_nat_spec.rb b/spec/classes/ip_nat_spec.rb
index 3b95eb1..e8389ef 100644
--- a/spec/classes/ip_nat_spec.rb
+++ b/spec/classes/ip_nat_spec.rb
@@ -237,6 +237,25 @@ describe 'nftables' do
         }
       end
 
+      context 'custom ip nat table name' do
+        let(:params) do
+          {
+            'nat_table_name' => 'mycustomtablename',
+          }
+        end
+
+        it { is_expected.to compile }
+        it {
+          is_expected.to contain_concat('nftables-ip-mycustomtablename').with(
+            path:   '/etc/nftables/puppet-preflight/ip-mycustomtablename.nft',
+            ensure: 'present',
+            owner:  'root',
+            group:  'root',
+            mode:   '0640',
+          )
+        }
+      end
+
       context 'all nat tables disabled' do
         let(:params) do
           {
diff --git a/spec/classes/rules/docker_ce_spec.rb b/spec/classes/rules/docker_ce_spec.rb
index a6df0c6..34fd146 100644
--- a/spec/classes/rules/docker_ce_spec.rb
+++ b/spec/classes/rules/docker_ce_spec.rb
@@ -6,6 +6,7 @@ describe 'nftables::rules::docker_ce' do
   on_supported_os.each do |os, os_facts|
     context "on #{os}" do
       let(:facts) { os_facts }
+      let(:pre_condition) { 'include nftables' }
 
       context 'default options' do
         it { is_expected.to compile }
diff --git a/spec/classes/rules/qemu_spec.rb b/spec/classes/rules/qemu_spec.rb
index fc656cc..4bccd30 100644
--- a/spec/classes/rules/qemu_spec.rb
+++ b/spec/classes/rules/qemu_spec.rb
@@ -4,6 +4,7 @@ describe 'nftables::rules::qemu' do
   on_supported_os.each do |os, os_facts|
     context "on #{os}" do
       let(:facts) { os_facts }
+      let(:pre_condition) { 'include nftables' }
 
       context 'default options' do
         it { is_expected.to compile }
@@ -77,7 +78,17 @@ describe 'nftables::rules::qemu' do
         end
 
         it { is_expected.to compile }
-        it { is_expected.to have_resource_count(0) }
+        it { is_expected.not_to contain_nftables__rule('default_in-qemu_udp_dns') }
+        it { is_expected.not_to contain_nftables__rule('default_in-qemu_tcp_dns') }
+        it { is_expected.not_to contain_nftables__rule('default_in-qemu_dhcpv4') }
+        it { is_expected.not_to contain_nftables__rule('default_fwd-qemu_oip_v4') }
+        it { is_expected.not_to contain_nftables__rule('default_fwd-qemu_iip_v4') }
+        it { is_expected.not_to contain_nftables__rule('default_fwd-qemu_io_internal') }
+        it { is_expected.not_to contain_nftables__rule('POSTROUTING-qemu_ignore_multicast') }
+        it { is_expected.not_to contain_nftables__rule('POSTROUTING-qemu_ignore_broadcast') }
+        it { is_expected.not_to contain_nftables__rule('POSTROUTING-qemu_masq_tcp') }
+        it { is_expected.not_to contain_nftables__rule('POSTROUTING-qemu_masq_udp') }
+        it { is_expected.not_to contain_nftables__rule('POSTROUTING-qemu_masq_ip') }
       end
 
       context 'ipv6 prefix' do
diff --git a/spec/defines/rules/dnat4_spec.rb b/spec/defines/rules/dnat4_spec.rb
index af049ba..a514398 100644
--- a/spec/defines/rules/dnat4_spec.rb
+++ b/spec/defines/rules/dnat4_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
 
 describe 'nftables::rules::dnat4' do
   let(:title) { 'foobar' }
+  let(:pre_condition) { 'include nftables' }
 
   on_supported_os.each do |os, facts|
     context "on #{os}" do
diff --git a/spec/defines/rules/masquerade_spec.rb b/spec/defines/rules/masquerade_spec.rb
index dc082ba..c4106f1 100644
--- a/spec/defines/rules/masquerade_spec.rb
+++ b/spec/defines/rules/masquerade_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
 
 describe 'nftables::rules::masquerade' do
   let(:title) { 'foobar' }
+  let(:pre_condition) { 'include nftables' }
 
   on_supported_os.each do |os, facts|
     context "on #{os}" do
diff --git a/spec/defines/rules/snat4_spec.rb b/spec/defines/rules/snat4_spec.rb
index 0fa1e5d..1e0ebb4 100644
--- a/spec/defines/rules/snat4_spec.rb
+++ b/spec/defines/rules/snat4_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
 
 describe 'nftables::rules::snat4' do
   let(:title) { 'foobar' }
+  let(:pre_condition) { 'include nftables' }
 
   on_supported_os.each do |os, facts|
     context "on #{os}" do
diff --git a/templates/config/puppet.nft.epp b/templates/config/puppet.nft.epp
index 9fb9a9a..ff2297a 100644
--- a/templates/config/puppet.nft.epp
+++ b/templates/config/puppet.nft.epp
@@ -26,6 +26,6 @@ include "custom-*.nft"
 include "inet-filter.nft"
 <% } -%>
 <% if $nat { -%>
-include "ip-nat.nft"
-include "ip6-nat.nft"
+include "ip-<%= $nftables::nat_table_name %>.nft"
+include "ip6-<%= $nftables::nat_table_name %>.nft"
 <% } -%>
