[pve-devel] [PATCH pve-firewall 1/2] iptables : add raw table support

Alexandre Derumier aderumier at odiso.com
Tue Nov 12 13:59:03 CET 2019


---
 src/PVE/Firewall.pm             | 122 +++++++++++++++++++++++++-------
 src/PVE/Service/pve_firewall.pm |  27 ++++---
 test/fwtester.pl                |  10 +--
 3 files changed, 119 insertions(+), 40 deletions(-)

diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm
index 97e5384..8f4ff1a 100644
--- a/src/PVE/Firewall.pm
+++ b/src/PVE/Firewall.pm
@@ -1757,15 +1757,17 @@ sub enable_bridge_firewall {
 }
 
 sub iptables_restore_cmdlist {
-    my ($cmdlist) = @_;
+    my ($cmdlist, $table) = @_;
 
-    run_command(['iptables-restore', '-n'], input => $cmdlist, errmsg => "iptables_restore_cmdlist");
+    $table = 'filter' if !$table;
+    run_command(['iptables-restore', '-T', $table, '-n'], input => $cmdlist, errmsg => "iptables_restore_cmdlist");
 }
 
 sub ip6tables_restore_cmdlist {
-    my ($cmdlist) = @_;
+    my ($cmdlist, $table) = @_;
 
-    run_command(['ip6tables-restore', '-n'], input => $cmdlist, errmsg => "iptables_restore_cmdlist");
+    $table = 'filter' if !$table;
+    run_command(['ip6tables-restore', '-T', $table, '-n'], input => $cmdlist, errmsg => "iptables_restore_cmdlist");
 }
 
 sub ipset_restore_cmdlist {
@@ -1781,9 +1783,10 @@ sub ebtables_restore_cmdlist {
 }
 
 sub iptables_get_chains {
-    my ($iptablescmd) = @_;
+    my ($iptablescmd, $t) = @_;
 
     $iptablescmd = "iptables" if !$iptablescmd;
+    $t = 'filter' if !$t;
 
     my $res = {};
 
@@ -1818,7 +1821,7 @@ sub iptables_get_chains {
 	    return;
 	}
 
-	return if $table ne 'filter';
+	return if $table ne $t;
 
 	if ($line =~ m/^:(\S+)\s/) {
 	    my $chain = $1;
@@ -1828,7 +1831,7 @@ sub iptables_get_chains {
 	    my ($chain, $sig) = ($1, $2);
 	    return if !&$is_pvefw_chain($chain);
 	    $res->{$chain} = $sig;
-	} elsif ($line =~ m/^-A\s+(INPUT|OUTPUT|FORWARD)\s+-j\s+PVEFW-\1$/) {
+	} elsif ($line =~ m/^-A\s+(INPUT|OUTPUT|FORWARD|PREROUTING)\s+-j\s+PVEFW-\1$/) {
 	    $hooks->{$1} = 1;
 	} else {
 	    # simply ignore the rest
@@ -3552,14 +3555,26 @@ sub compile {
 
     push @{$cluster_conf->{ipset}->{management}}, { cidr => $localnet };
 
-    my $ruleset = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, 4);
-    my $rulesetv6 = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, 6);
+    my $ruleset = {};
+    my $rulesetv6 = {};
+    $ruleset->{filter} = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, 4);
+    $ruleset->{raw} = compile_iptables_raw($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, 4);
+    $rulesetv6->{filter} = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, 6);
+    $rulesetv6->{raw} = compile_iptables_raw($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, 6);
     my $ebtables_ruleset = compile_ebtables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata);
     my $ipset_ruleset = compile_ipsets($cluster_conf, $vmfw_configs, $vmdata);
 
     return ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset);
 }
 
+sub compile_iptables_raw {
+    my ($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, $ipversion) = @_;
+
+    my $ruleset = {};
+
+    return $ruleset;
+}
+
 sub compile_iptables_filter {
     my ($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, $corosync_conf, $ipversion) = @_;
 
@@ -3958,11 +3973,13 @@ sub print_sig_rule {
 }
 
 sub get_ruleset_cmdlist {
-    my ($ruleset, $iptablescmd) = @_;
+    my ($ruleset, $iptablescmd, $table) = @_;
 
-    my $cmdlist = "*filter\n"; # we pass this to iptables-restore;
+    $table = 'filter' if !$table;
 
-    my ($active_chains, $hooks) = iptables_get_chains($iptablescmd);
+    my $cmdlist = "*$table\n"; # we pass this to iptables-restore;
+
+    my ($active_chains, $hooks) = iptables_get_chains($iptablescmd, $table);
     my $statushash = get_ruleset_status($ruleset, $active_chains, \&iptables_chain_digest);
 
     # create missing chains first
@@ -3974,7 +3991,7 @@ sub get_ruleset_cmdlist {
 	$cmdlist .= ":$chain - [0:0]\n";
     }
 
-    foreach my $h (qw(INPUT OUTPUT FORWARD)) {
+    foreach my $h (qw(INPUT OUTPUT FORWARD PREROUTING)) {
 	my $chain = "PVEFW-$h";
 	if ($ruleset->{$chain} && !$hooks->{$h}) {
 	    $cmdlist .= "-A $h -j $chain\n";
@@ -4009,10 +4026,11 @@ sub get_ruleset_cmdlist {
 	next if $chain eq 'PVEFW-INPUT';
 	next if $chain eq 'PVEFW-OUTPUT';
 	next if $chain eq 'PVEFW-FORWARD';
+	next if $chain eq 'PVEFW-PREROUTING';
 	$cmdlist .= "-X $chain\n";
     }
 
-    my $changes = $cmdlist ne "*filter\n" ? 1 : 0;
+    my $changes = $cmdlist ne "*$table\n" ? 1 : 0;
 
     $cmdlist .= "COMMIT\n";
 
@@ -4125,9 +4143,11 @@ sub apply_ruleset {
     my ($ipset_create_cmdlist, $ipset_delete_cmdlist, $ipset_changes) =
 	get_ipset_cmdlist($ipset_ruleset);
 
-    my ($cmdlist, $changes) = get_ruleset_cmdlist($ruleset);
-    my ($cmdlistv6, $changesv6) = get_ruleset_cmdlist($rulesetv6, "ip6tables");
+    my ($cmdlist, $changes) = get_ruleset_cmdlist($ruleset->{filter});
+    my ($cmdlistv6, $changesv6) = get_ruleset_cmdlist($rulesetv6->{filter}, "ip6tables");
     my ($ebtables_cmdlist, $ebtables_changes) = get_ebtables_cmdlist($ebtables_ruleset);
+    my ($cmdlist_raw, $changes_raw) = get_ruleset_cmdlist($ruleset->{raw}, undef, 'raw');
+    my ($cmdlistv6_raw, $changesv6_raw) = get_ruleset_cmdlist($rulesetv6->{raw}, "ip6tables", 'raw');
 
     if ($verbose) {
 	if ($ipset_changes) {
@@ -4146,6 +4166,16 @@ sub apply_ruleset {
 	    print $cmdlistv6;
 	}
 
+	if ($changes_raw) {
+	    print "iptables table raw changes:\n";
+	    print $cmdlist_raw;
+	}
+
+	if ($changesv6_raw) {
+	    print "ip6tables table raw changes:\n";
+	    print $cmdlistv6_raw;
+	}
+
 	if ($ebtables_changes) {
 	    print "ebtables changes:\n";
 	    print $ebtables_cmdlist;
@@ -4162,11 +4192,21 @@ sub apply_ruleset {
 
     iptables_restore_cmdlist($cmdlist);
 
+    $tmpfile = "$pve_fw_status_dir/ip4cmdlistraw";
+    PVE::Tools::file_set_contents($tmpfile, $cmdlist_raw || '');
+
+    iptables_restore_cmdlist($cmdlist_raw, 'raw');
+
     $tmpfile = "$pve_fw_status_dir/ip6cmdlist";
     PVE::Tools::file_set_contents($tmpfile, $cmdlistv6 || '');
 
     ip6tables_restore_cmdlist($cmdlistv6);
 
+    $tmpfile = "$pve_fw_status_dir/ip6cmdlistraw";
+    PVE::Tools::file_set_contents($tmpfile, $cmdlistv6_raw || '');
+
+    ip6tables_restore_cmdlist($cmdlistv6_raw, 'raw');
+
     $tmpfile = "$pve_fw_status_dir/ipsetcmdlist2";
     PVE::Tools::file_set_contents($tmpfile, $ipset_delete_cmdlist || '');
 
@@ -4178,11 +4218,12 @@ sub apply_ruleset {
     PVE::Tools::file_set_contents($tmpfile, $ebtables_cmdlist || '');
 
     # test: re-read status and check if everything is up to date
+    my $ruleset_filter = $ruleset->{filter};
     my $active_chains = iptables_get_chains();
-    my $statushash = get_ruleset_status($ruleset, $active_chains, \&iptables_chain_digest);
+    my $statushash = get_ruleset_status($ruleset_filter, $active_chains, \&iptables_chain_digest);
 
     my $errors;
-    foreach my $chain (sort keys %$ruleset) {
+    foreach my $chain (sort keys %$ruleset_filter) {
 	my $stat = $statushash->{$chain};
 	if ($stat->{action} ne 'exists') {
 	    warn "unable to update chain '$chain'\n";
@@ -4190,10 +4231,11 @@ sub apply_ruleset {
 	}
     }
 
+    my $rulesetv6_filter = $rulesetv6->{filter};
     my $active_chainsv6 = iptables_get_chains("ip6tables");
-    my $statushashv6 = get_ruleset_status($rulesetv6, $active_chainsv6, \&iptables_chain_digest);
+    my $statushashv6 = get_ruleset_status($rulesetv6_filter, $active_chainsv6, \&iptables_chain_digest);
 
-    foreach my $chain (sort keys %$rulesetv6) {
+    foreach my $chain (sort keys %$rulesetv6_filter) {
 	my $stat = $statushashv6->{$chain};
 	if ($stat->{action} ne 'exists') {
 	    warn "unable to update chain '$chain'\n";
@@ -4201,6 +4243,30 @@ sub apply_ruleset {
 	}
     }
 
+    my $ruleset_raw = $ruleset->{raw};
+    my $active_chains_raw = iptables_get_chains(undef, 'raw');
+    my $statushash_raw = get_ruleset_status($ruleset_raw, $active_chains_raw, \&iptables_chain_digest);
+
+    foreach my $chain (sort keys %$ruleset_raw) {
+	my $stat = $statushash_raw->{$chain};
+	if ($stat->{action} ne 'exists') {
+	    warn "unable to update chain '$chain'\n";
+	    $errors = 1;
+	}
+    }
+
+    my $rulesetv6_raw = $rulesetv6->{raw};
+    my $active_chainsv6_raw = iptables_get_chains("ip6tables", 'raw');
+    my $statushashv6_raw = get_ruleset_status($rulesetv6_raw, $active_chainsv6_raw, \&iptables_chain_digest);
+
+    foreach my $chain (sort keys %$rulesetv6_raw) {
+	my $stat = $statushashv6_raw->{$chain};
+	if ($stat->{action} ne 'exists') {
+	    warn "unable to update chain '$chain'\n";
+	    $errors = 1;
+	}
+    }
+
     my $active_ebtables_chains = ebtables_get_chains();
     my $ebtables_statushash = get_ruleset_status($ebtables_ruleset,
 				$active_ebtables_chains, \&iptables_chain_digest,
@@ -4278,18 +4344,22 @@ sub remove_pvefw_chains {
 
     PVE::Firewall::remove_pvefw_chains_iptables("iptables");
     PVE::Firewall::remove_pvefw_chains_iptables("ip6tables");
+    PVE::Firewall::remove_pvefw_chains_iptables("iptables", "raw");
+    PVE::Firewall::remove_pvefw_chains_iptables("ip6tables", "raw");
     PVE::Firewall::remove_pvefw_chains_ipset();
     PVE::Firewall::remove_pvefw_chains_ebtables();
 
 }
 
 sub remove_pvefw_chains_iptables {
-    my ($iptablescmd) = @_;
+    my ($iptablescmd, $table) = @_;
 
-    my ($chash, $hooks) = iptables_get_chains($iptablescmd);
-    my $cmdlist = "*filter\n";
+    $table = 'filter' if !$table;
+
+    my ($chash, $hooks) = iptables_get_chains($iptablescmd, $table);
+    my $cmdlist = "*$table\n";
 
-    foreach my $h (qw(INPUT OUTPUT FORWARD)) {
+    foreach my $h (qw(INPUT OUTPUT FORWARD PREROUTING)) {
 	if ($hooks->{$h}) {
 	    $cmdlist .= "-D $h -j PVEFW-$h\n";
 	}
@@ -4305,9 +4375,9 @@ sub remove_pvefw_chains_iptables {
     $cmdlist .= "COMMIT\n";
 
     if($iptablescmd eq "ip6tables") {
-	ip6tables_restore_cmdlist($cmdlist);
+	ip6tables_restore_cmdlist($cmdlist, $table);
     } else {
-	iptables_restore_cmdlist($cmdlist);
+	iptables_restore_cmdlist($cmdlist, $table);
     }
 }
 
diff --git a/src/PVE/Service/pve_firewall.pm b/src/PVE/Service/pve_firewall.pm
index d78bcb1..5a62f3d 100755
--- a/src/PVE/Service/pve_firewall.pm
+++ b/src/PVE/Service/pve_firewall.pm
@@ -170,11 +170,13 @@ __PACKAGE__->register_method ({
 
 		PVE::Firewall::set_verbose(0); # do not show iptables details
 		my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset);
-		my ($test, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset);
-		my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, "ip6tables");
+		my ($test, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset->{filter});
+		my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6->{filter}, "ip6tables");
+		my (undef, $ruleset_changes_raw) = PVE::Firewall::get_ruleset_cmdlist($ruleset->{raw}, undef, 'raw');
+		my (undef, $ruleset_changesv6_raw) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6->{raw}, "ip6tables", 'raw');
 		my (undef, $ebtables_changes) = PVE::Firewall::get_ebtables_cmdlist($ebtables_ruleset);
 
-		$res->{changes} = ($ipset_changes || $ruleset_changes || $ruleset_changesv6 || $ebtables_changes) ? 1 : 0;
+		$res->{changes} = ($ipset_changes || $ruleset_changes || $ruleset_changesv6 || $ebtables_changes || $ruleset_changes_raw || $ruleset_changesv6_raw) ? 1 : 0;
 	    }
 
 	    return $res;
@@ -210,15 +212,22 @@ __PACKAGE__->register_method ({
 	    my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset);
 
 	    print "\niptables cmdlist:\n";
-	    my (undef, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset);
+	    my (undef, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset->{filter});
 
 	    print "\nip6tables cmdlist:\n";
-	    my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, "ip6tables");
+	    my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6->{filter}, "ip6tables");
 
 	    print "\nebtables cmdlist:\n";
 	    my (undef, $ebtables_changes) = PVE::Firewall::get_ebtables_cmdlist($ebtables_ruleset);
 
-	    if ($ipset_changes || $ruleset_changes || $ruleset_changesv6 || $ebtables_changes) {
+	    print "\niptables table raw cmdlist:\n";
+	    my (undef, $ruleset_changes_raw) = PVE::Firewall::get_ruleset_cmdlist($ruleset->{raw}, undef, 'raw');
+
+	    print "\nip6tables table raw cmdlist:\n";
+	    my (undef, $ruleset_changesv6_raw) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6->{raw}, "ip6tables", 'raw');
+
+
+	    if ($ipset_changes || $ruleset_changes || $ruleset_changesv6 || $ebtables_changes || $ruleset_changes_raw || $ruleset_changesv6_raw) {
 		print "detected changes\n";
 	    } else {
 		print "no changes\n";
@@ -226,7 +235,6 @@ __PACKAGE__->register_method ({
 	    if (!$cluster_conf->{options}->{enable}) {
 		print "firewall disabled\n";
 	    }
-
 	};
 
 	PVE::Firewall::run_locked($code);
@@ -366,7 +374,8 @@ __PACKAGE__->register_method ({
 	my $host_ip = PVE::Cluster::remote_node_ip($nodename);
 
 	PVE::FirewallSimulator::reset_trace();
-	print Dumper($ruleset) if $param->{verbose};
+	print Dumper($ruleset->{filter}) if $param->{verbose};
+	print Dumper($ruleset->{raw}) if $param->{verbose};
 
 	my $test = {
 	    from => $param->{from},
@@ -397,7 +406,7 @@ __PACKAGE__->register_method ({
 
 	$test->{action} = 'QUERY';
 
-	my $res = PVE::FirewallSimulator::simulate_firewall($ruleset, $ipset_ruleset,
+	my $res = PVE::FirewallSimulator::simulate_firewall($ruleset->{filter}, $ipset_ruleset,
 							    $host_ip, $vmdata, $test);
 
 	print "ACTION: $res\n";
diff --git a/test/fwtester.pl b/test/fwtester.pl
index e9ed6d1..b969295 100755
--- a/test/fwtester.pl
+++ b/test/fwtester.pl
@@ -61,7 +61,7 @@ sub run_tests {
 	    die $@ if $@;
 	    next if defined($testid) && (!defined($test->{id}) || ($testid ne $test->{id}));
 	    PVE::FirewallSimulator::reset_trace();
-	    print Dumper($ruleset) if $debug;
+	    print Dumper($ruleset->{filter}) if $debug;
 	    $testcount++;
 	    eval {
 		my @test_zones = qw(host outside nfvm vm100 ct200);
@@ -72,7 +72,7 @@ sub run_tests {
 			next if $zone eq $test->{from};
 			$test->{to} = $zone;
 			PVE::FirewallSimulator::add_trace("Set Zone: to => '$zone'\n"); 
-			PVE::FirewallSimulator::simulate_firewall($ruleset, $ipset_ruleset, 
+			PVE::FirewallSimulator::simulate_firewall($ruleset->{filter}, $ipset_ruleset, 
 								  $host_ip, $vmdata, $test);
 		    }
 		} elsif (!defined($test->{from})) {
@@ -80,17 +80,17 @@ sub run_tests {
 			next if $zone eq $test->{to};
 			$test->{from} = $zone;
 			PVE::FirewallSimulator::add_trace("Set Zone: from => '$zone'\n"); 
-			PVE::FirewallSimulator::simulate_firewall($ruleset, $ipset_ruleset, 
+			PVE::FirewallSimulator::simulate_firewall($ruleset->{filter}, $ipset_ruleset, 
 								  $host_ip, $vmdata, $test);
 		    }
 		} else {
-		    PVE::FirewallSimulator::simulate_firewall($ruleset, $ipset_ruleset, 
+		    PVE::FirewallSimulator::simulate_firewall($ruleset->{filter}, $ipset_ruleset, 
 							      $host_ip, $vmdata, $test);
 		}
 	    };
 	    if (my $err = $@) {
 
-		print Dumper($ruleset) if !$debug;
+		print Dumper($ruleset->{filter}) if !$debug;
 
 		print PVE::FirewallSimulator::get_trace() . "\n" if !$debug;
 
-- 
2.20.1




More information about the pve-devel mailing list