From 64e0adf411ca322516fbf96a0ef2fbcb54881209 Mon Sep 17 00:00:00 2001 From: Alexandre Derumier Date: Tue, 12 Nov 2019 13:59:03 +0100 Subject: [PATCH] iptables : add raw table support Signed-off-by: Wolfgang Bumiller --- 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.39.2