],
};
+$pve_std_chains->{6} = {
+ 'PVEFW-SET-ACCEPT-MARK' => [
+ "-j MARK --set-mark 1",
+ ],
+ 'PVEFW-DropBroadcast' => [
+ # same as shorewall 'Broadcast'
+ # simply DROP BROADCAST/MULTICAST/ANYCAST
+ # we can use this to reduce logging
+ #{ action => 'DROP', dsttype => 'BROADCAST' }, #no broadcast in ipv6
+ { action => 'DROP', dsttype => 'MULTICAST' },
+ { action => 'DROP', dsttype => 'ANYCAST' },
+ #{ action => 'DROP', dest => '224.0.0.0/4' },
+ ],
+ 'PVEFW-reject' => [
+ # same as shorewall 'reject'
+ #{ action => 'DROP', dsttype => 'BROADCAST' },
+ #{ action => 'DROP', source => '224.0.0.0/4' },
+ { action => 'DROP', proto => 'icmpv6' },
+ "-p tcp -j REJECT --reject-with tcp-reset",
+ #"-p udp -j REJECT --reject-with icmp-port-unreachable",
+ #"-p icmp -j REJECT --reject-with icmp-host-unreachable",
+ #"-j REJECT --reject-with icmp-host-prohibited",
+ ],
+ 'PVEFW-Drop' => [
+ # same as shorewall 'Drop', which is equal to DROP,
+ # but REJECT/DROP some packages to reduce logging,
+ # and ACCEPT critical ICMP types
+ { action => 'PVEFW-reject', proto => 'tcp', dport => '43' }, # REJECT 'auth'
+ # we are not interested in BROADCAST/MULTICAST/ANYCAST
+ { action => 'PVEFW-DropBroadcast' },
+ # ACCEPT critical ICMP types
+ { action => 'ACCEPT', proto => 'icmpv6', dport => 'destination-unreachable' },
+ { action => 'ACCEPT', proto => 'icmpv6', dport => 'time-exceeded' },
+ { action => 'ACCEPT', proto => 'icmpv6', dport => 'packet-too-big' },
+
+ # Drop packets with INVALID state
+ "-m conntrack --ctstate INVALID -j DROP",
+ # Drop Microsoft SMB noise
+ { action => 'DROP', proto => 'udp', dport => '135,445', nbdport => 2 },
+ { action => 'DROP', proto => 'udp', dport => '137:139'},
+ { action => 'DROP', proto => 'udp', dport => '1024:65535', sport => 137 },
+ { action => 'DROP', proto => 'tcp', dport => '135,139,445', nbdport => 3 },
+ { action => 'DROP', proto => 'udp', dport => 1900 }, # UPnP
+ # Drop new/NotSyn traffic so that it doesn't get logged
+ "-p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j DROP",
+ # Drop DNS replies
+ { action => 'DROP', proto => 'udp', sport => 53 },
+ ],
+ 'PVEFW-Reject' => [
+ # same as shorewall 'Reject', which is equal to Reject,
+ # but REJECT/DROP some packages to reduce logging,
+ # and ACCEPT critical ICMP types
+ { action => 'PVEFW-reject', proto => 'tcp', dport => '43' }, # REJECT 'auth'
+ # we are not interested in BROADCAST/MULTICAST/ANYCAST
+ { action => 'PVEFW-DropBroadcast' },
+ # ACCEPT critical ICMP types
+ { action => 'ACCEPT', proto => 'icmpv6', dport => 'destination-unreachable' },
+ { action => 'ACCEPT', proto => 'icmpv6', dport => 'time-exceeded' },
+ { action => 'ACCEPT', proto => 'icmpv6', dport => 'packet-too-big' },
+
+ # Drop packets with INVALID state
+ "-m conntrack --ctstate INVALID -j DROP",
+ # Drop Microsoft SMB noise
+ { action => 'PVEFW-reject', proto => 'udp', dport => '135,445', nbdport => 2 },
+ { action => 'PVEFW-reject', proto => 'udp', dport => '137:139'},
+ { action => 'PVEFW-reject', proto => 'udp', dport => '1024:65535', sport => 137 },
+ { action => 'PVEFW-reject', proto => 'tcp', dport => '135,139,445', nbdport => 3 },
+ { action => 'DROP', proto => 'udp', dport => 1900 }, # UPnP
+ # Drop new/NotSyn traffic so that it doesn't get logged
+ "-p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j DROP",
+ # Drop DNS replies
+ { action => 'DROP', proto => 'udp', sport => 53 },
+ ],
+ 'PVEFW-tcpflags' => [
+ # same as shorewall tcpflags action.
+ # Packets arriving on this interface are checked for som illegal combinations of TCP flags
+ "-p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,PSH,URG -g PVEFW-logflags",
+ "-p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -g PVEFW-logflags",
+ "-p tcp -m tcp --tcp-flags SYN,RST SYN,RST -g PVEFW-logflags",
+ "-p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -g PVEFW-logflags",
+ "-p tcp -m tcp --sport 0 --tcp-flags FIN,SYN,RST,ACK SYN -g PVEFW-logflags",
+ ],
+ 'PVEFW-smurfs' => [
+ #does smurf attack works with ipv6, as broadcast not exist ???
+
+ # same as shorewall smurfs action
+ # Filter packets for smurfs (packets with a broadcast address as the source).
+ #"-s 0.0.0.0/32 -j RETURN",
+ #"-m addrtype --src-type BROADCAST -g PVEFW-smurflog",
+ #"-s 224.0.0.0/4 -g PVEFW-smurflog",
+ ],
+};
+
# iptables -p icmp -h
my $icmp_type_names = {
any => 1,
'address-mask-reply' => 1,
};
+# ip6tables -p icmpv6 -h
+
+my $icmpv6_type_names = {
+ 'any' => 1,
+ 'destination-unreachable' => 1,
+ 'no-route' => 1,
+ 'communication-prohibited' => 1,
+ 'address-unreachable' => 1,
+ 'port-unreachable' => 1,
+ 'packet-too-big' => 1,
+ 'time-exceeded' => 1,
+ 'ttl-zero-during-transit' => 1,
+ 'ttl-zero-during-reassembly' => 1,
+ 'parameter-problem' => 1,
+ 'bad-header' => 1,
+ 'unknown-header-type' => 1,
+ 'unknown-option' => 1,
+ 'echo-request' => 1,
+ 'echo-reply' => 1,
+ 'router-solicitation' => 1,
+ 'router-advertisement' => 1,
+ 'neighbour-solicitation' => 1,
+ 'neighbour-advertisement' => 1,
+ 'redirect' => 1,
+};
+
sub init_firewall_macros {
$pve_fw_parsed_macros = {};
close($fh);
+ # add special case for ICMP v6
+ $protocols->{byid}->{icmpv6}->{name} = "icmpv6";
+ $protocols->{byname}->{icmpv6} = $protocols->{byid}->{icmpv6};
+
$etc_protocols = $protocols;
return $etc_protocols;
} else {
if ($icmp_type_names->{$item}) {
$icmp_port = 1;
+ } elsif ($icmpv6_type_names->{$item}) {
+ $icmp_port = 1;
} else {
die "invalid port '$item'\n" if !$services->{byname}->{$item};
}
}
}
+ my $ipversion;
+ my $set_ip_version = sub {
+ my $vers = shift;
+ if ($vers) {
+ die "detected mixed ipv4/ipv6 adresses in rule\n"
+ if $ipversion && ($vers != $ipversion);
+ $ipversion = $vers;
+ }
+ };
+
if ($rule->{proto}) {
eval { pve_fw_verify_protocol_spec($rule->{proto}); };
&$add_error('proto', $@) if $@;
+ &$set_ip_version(4) if $rule->{proto} eq 'icmp';
+ &$set_ip_version(6) if $rule->{proto} eq 'icmpv6';
}
if ($rule->{dport}) {
if !$rule->{proto};
}
- my $ipversion;
-
if ($rule->{source}) {
- eval { $ipversion = parse_address_list($rule->{source}); };
+ eval {
+ my $source_ipversion = parse_address_list($rule->{source});
+ &$set_ip_version($source_ipversion);
+ };
&$add_error('source', $@) if $@;
&$check_ipset_or_alias_property('source', $ipversion);
}
if ($rule->{dest}) {
eval {
my $dest_ipversion = parse_address_list($rule->{dest});
- die "detected mixed ipv4/ipv6 adresses in rule\n"
- if $ipversion && $dest_ipversion && ($dest_ipversion != $ipversion);
- $ipversion = $dest_ipversion if $dest_ipversion;
+ &$set_ip_version($dest_ipversion);
};
&$add_error('dest', $@) if $@;
&$check_ipset_or_alias_property('dest', $ipversion);
run_command("/sbin/iptables-restore -n", input => $cmdlist);
}
+sub ip6tables_restore_cmdlist {
+ my ($cmdlist) = @_;
+
+ run_command("/sbin/ip6tables-restore -n", input => $cmdlist);
+}
+
sub ipset_restore_cmdlist {
my ($cmdlist) = @_;
}
sub iptables_get_chains {
+ my ($iptablescmd) = @_;
+
+ $iptablescmd = "iptables" if !$iptablescmd;
my $res = {};
}
};
- run_command("/sbin/iptables-save", outfunc => $parser);
+ run_command("/sbin/$iptablescmd-save", outfunc => $parser);
return wantarray ? ($res, $hooks) : $res;
}
# Note: we use dport to store --icmp-type
die "unknown icmp-type '$rule->{dport}'\n" if !defined($icmp_type_names->{$rule->{dport}});
push @cmd, "-m icmp --icmp-type $rule->{dport}";
+ } elsif ($rule->{proto} && $rule->{proto} eq 'icmpv6') {
+ # Note: we use dport to store --icmpv6-type
+ die "unknown icmpv6-type '$rule->{dport}'\n" if !defined($icmpv6_type_names->{$rule->{dport}});
+ push @cmd, "-m icmpv6 --icmpv6-type $rule->{dport}";
} else {
if ($nbdport > 1) {
if ($multiport == 2) {
}
my ($ruleset, $ipset_ruleset) = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, 4, $verbose);
- return ($ruleset, $ipset_ruleset);
+ my ($rulesetv6) = compile_iptables_filter($cluster_conf, $hostfw_conf, $vmfw_configs, $vmdata, 6, $verbose);
+
+ return ($ruleset, $ipset_ruleset, $rulesetv6);
}
sub compile_iptables_filter {
}
sub get_ruleset_cmdlist {
- my ($ruleset, $verbose) = @_;
+ my ($ruleset, $verbose, $iptablescmd) = @_;
my $cmdlist = "*filter\n"; # we pass this to iptables-restore;
- my ($active_chains, $hooks) = iptables_get_chains();
+ my ($active_chains, $hooks) = iptables_get_chains($iptablescmd);
my $statushash = get_ruleset_status($ruleset, $active_chains, \&iptables_chain_digest, $verbose);
# create missing chains first
}
sub apply_ruleset {
- my ($ruleset, $hostfw_conf, $ipset_ruleset, $verbose) = @_;
+ my ($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6, $verbose) = @_;
enable_bridge_firewall();
get_ipset_cmdlist($ipset_ruleset, undef, $verbose);
my ($cmdlist, $changes) = get_ruleset_cmdlist($ruleset, $verbose);
+ my ($cmdlistv6, $changesv6) = get_ruleset_cmdlist($rulesetv6, $verbose, "ip6tables");
if ($verbose) {
if ($ipset_changes) {
print "iptables changes:\n";
print $cmdlist;
}
+
+ if ($changesv6) {
+ print "ip6tables changes:\n";
+ print $cmdlistv6;
+ }
}
ipset_restore_cmdlist($ipset_create_cmdlist);
iptables_restore_cmdlist($cmdlist);
+ ip6tables_restore_cmdlist($cmdlistv6);
ipset_restore_cmdlist($ipset_delete_cmdlist) if $ipset_delete_cmdlist;
}
}
+ my $active_chainsv6 = iptables_get_chains("ip6tables");
+ my $statushashv6 = get_ruleset_status($rulesetv6, $active_chainsv6, \&iptables_chain_digest, 0);
+
+ foreach my $chain (sort keys %$rulesetv6) {
+ my $stat = $statushashv6->{$chain};
+ if ($stat->{action} ne 'exists') {
+ warn "unable to update chain '$chain'\n";
+ $errors = 1;
+ }
+ }
+
die "unable to apply firewall changes\n" if $errors;
update_nf_conntrack_max($hostfw_conf);
sub remove_pvefw_chains {
- my ($chash, $hooks) = iptables_get_chains();
+ PVE::Firewall::remove_pvefw_chains_iptables("iptables");
+ PVE::Firewall::remove_pvefw_chains_iptables("ip6tables");
+ PVE::Firewall::remove_pvefw_chains_ipset();
+
+}
+
+sub remove_pvefw_chains_iptables {
+ my ($iptablescmd) = @_;
+
+ my ($chash, $hooks) = iptables_get_chains($iptablescmd);
my $cmdlist = "*filter\n";
foreach my $h (qw(INPUT OUTPUT FORWARD)) {
}
$cmdlist .= "COMMIT\n";
- iptables_restore_cmdlist($cmdlist);
+ if($iptablescmd eq "ip6tables") {
+ ip6tables_restore_cmdlist($cmdlist);
+ } else {
+ iptables_restore_cmdlist($cmdlist);
+ }
+}
+
+sub remove_pvefw_chains_ipset {
my $ipset_chains = ipset_get_chains();
- $cmdlist = "";
+ my $sub_cmdlist = "";
+ my $cmdlist = "";
foreach my $chain (keys %$ipset_chains) {
- $cmdlist .= "flush $chain\n";
- $cmdlist .= "destroy $chain\n";
+ if ($chain =~ m/^PVEFW-\S+\-(v4|v6)$/) {
+ $sub_cmdlist .= "flush $chain\n";
+ $sub_cmdlist .= "destroy $chain\n";
+ }else{
+ $cmdlist .= "flush $chain\n";
+ $cmdlist .= "destroy $chain\n";
+ }
}
- ipset_restore_cmdlist($cmdlist) if $cmdlist;
+ $cmdlist .= $sub_cmdlist;
+
+ ipset_restore_cmdlist($cmdlist) if $cmdlist;
}
sub init {
my $hostfw_conf = load_hostfw_conf();
- my ($ruleset, $ipset_ruleset) = compile($cluster_conf, $hostfw_conf);
+ my ($ruleset, $ipset_ruleset, $rulesetv6) = compile($cluster_conf, $hostfw_conf);
- apply_ruleset($ruleset, $hostfw_conf, $ipset_ruleset);
+ apply_ruleset($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6);
};
run_locked($code);