[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