use warnings;
use strict;
use Data::Dumper;
-use Digest::MD5;
+use Digest::SHA;
use PVE::Tools;
use PVE::QemuServer;
use File::Path;
return ($nbports);
}
+my $bridge_firewall_enabled = 0;
+
+sub enable_bridge_firewall {
+
+ return if $bridge_firewall_enabled; # only once
+
+ system("echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables");
+ system("echo 1 > /proc/sys/net/bridge/bridge-nf-call-ip6tables");
+
+ $bridge_firewall_enabled = 1;
+}
+
my $rule_format = "%-15s %-30s %-30s %-15s %-15s %-15s\n";
sub iptables {
my $chain = $1;
return if !&$is_pvefw_chain($chain);
$res->{$chain} = "unknown";
- } elsif ($line =~ m/^-A\s+(\S+)\s.*--log-prefix\s+\"PVESIG:(\S+)\"/) {
+ } elsif ($line =~ m/^-A\s+(\S+)\s.*--comment\s+\"PVESIG:(\S+)\"/) {
my ($chain, $sig) = ($1, $2);
return if !&$is_pvefw_chain($chain);
$res->{$chain} = $sig;
}
sub ruleset_generate_rule {
- my ($ruleset, $chain, $rule) = @_;
+ my ($ruleset, $chain, $rule, $goto) = @_;
my $cmd = '';
$cmd .= " --dport $rule->{dport}" if $rule->{dport};
$cmd .= " --match multiport" if $rule->{nbsport} && $rule->{nbsport} > 1;
$cmd .= " --sport $rule->{sport}" if $rule->{sport};
- $cmd .= " -j $rule->{action}" if $rule->{action};
+
+ if (my $action = $rule->{action}) {
+ $goto = 1 if !defined($goto) && $action eq 'PVEFW-SET-ACCEPT-MARK';
+ $cmd .= $goto ? " -g $action" : " -j $action";
+ };
ruleset_addrule($ruleset, $chain, $cmd) if $cmd;
}
sub ruleset_create_chain {
my ($ruleset, $chain) = @_;
+ die "Invalid chain name '$chain' (28 char max)\n" if length($chain) > 28;
+
die "chain '$chain' already exists\n" if $ruleset->{$chain};
$ruleset->{$chain} = [];
if(!ruleset_chain_exist($ruleset, $rule->{action})){
generate_group_rules($ruleset, $2);
}
+ ruleset_generate_rule($ruleset, $tapchain, $rule);
+ ruleset_addrule($ruleset, $tapchain, "-m mark --mark 1 -g $bridge-IN");
+ } else {
+ # we go to vmbr-IN if accept in out rules
+ $rule->{action} = "$bridge-IN" if $rule->{action} eq 'ACCEPT' && $direction eq 'OUT';
+ ruleset_generate_rule($ruleset, $tapchain, $rule);
}
- # we go to vmbr-IN if accept in out rules
- $rule->{action} = "$bridge-IN" if $rule->{action} eq 'ACCEPT' && $direction eq 'OUT';
- ruleset_generate_rule($ruleset, $tapchain, $rule);
- }
+ }
}
ruleset_addrule($ruleset, $tapchain, "-j LOG --log-prefix \"$tapchain-dropped: \" --log-level 4");
ruleset_addrule($ruleset, $chain, "-j DROP");
# host outbound firewall
- my $chain = "PVEFW-HOST-OUT";
+ $chain = "PVEFW-HOST-OUT";
ruleset_create_chain($ruleset, $chain);
ruleset_addrule($ruleset, $chain, "-m state --state INVALID -j DROP");
$chain = "GROUP-${group}-OUT";
ruleset_create_chain($ruleset, $chain);
+ ruleset_addrule($ruleset, $chain, "-j MARK --set-mark 0"); # clear mark
if ($rules->{out}) {
foreach my $rule (@{$rules->{out}}) {
- # we go the PVEFW-BRIDGE-IN because we need to check also other tap rules
- # (and group rules can be set on any bridge, so we can't go to VMBRXX-IN)
- $rule->{action} = 'PVEFW-BRIDGE-IN' if $rule->{action} eq 'ACCEPT';
- ruleset_generate_rule($rule, $chain, $rule);
+ # we go the PVEFW-SET-ACCEPT-MARK Instead of ACCEPT) because we need to
+ # check also other tap rules (and group rules can be set on any bridge,
+ # so we can't go to VMBRXX-IN)
+ $rule->{action} = 'PVEFW-SET-ACCEPT-MARK' if $rule->{action} eq 'ACCEPT';
+ ruleset_generate_rule($ruleset, $chain, $rule);
}
}
}
ruleset_create_chain($ruleset, "PVEFW-INPUT");
ruleset_create_chain($ruleset, "PVEFW-OUTPUT");
+ ruleset_create_chain($ruleset, "PVEFW-SET-ACCEPT-MARK");
+ ruleset_addrule($ruleset, "PVEFW-SET-ACCEPT-MARK", "-j MARK --set-mark 1");
+
enablehostfw($ruleset);
# generate firewall rules for QEMU VMs
my $statushash = {};
foreach my $chain (sort keys %$ruleset) {
- my $digest = Digest::MD5->new();
+ my $digest = Digest::SHA->new('sha1');
foreach my $cmd (@{$ruleset->{$chain}}) {
$digest->add("$cmd\n");
}
sub print_sig_rule {
my ($chain, $sig) = @_;
- # Note: This rule should never match! We just use this hack to store a SHA1 checksum
- # used to detect changes
- return "-A $chain -j LOG --log-prefix \"PVESIG:$sig\" -p tcp -s \"127.128.129.130\" --dport 1\n";
+ # We just use this to store a SHA1 checksum used to detect changes
+ return "-A $chain -m comment --comment \"PVESIG:$sig\"\n";
}
-sub compile_and_start {
- my ($verbose) = @_;
+sub apply_ruleset {
+ my ($ruleset, $verbose) = @_;
- my $ruleset = compile();
+ enable_bridge_firewall();
my $cmdlist = "*filter\n"; # we pass this to iptables-restore;
}
$cmdlist .= print_sig_rule($chain, $stat->{sig});
} elsif ($stat->{action} eq 'delete') {
- $cmdlist .= "-F $chain\n";
- $cmdlist .= "-X $chain\n";
+ die "internal error"; # this should not happen
} elsif ($stat->{action} eq 'exists') {
# do nothing
} else {
}
}
+ foreach my $chain (keys %$statushash) {
+ next if $statushash->{$chain}->{action} ne 'delete';
+ $cmdlist .= "-F $chain\n";
+ }
+ foreach my $chain (keys %$statushash) {
+ next if $statushash->{$chain}->{action} ne 'delete';
+ $cmdlist .= "-X $chain\n";
+ }
+
$cmdlist .= "COMMIT\n";
print $cmdlist if $verbose;