duplicate iptables rules to ip6tables rules. -currently only support vms port rules. -stdchains are a little differents, as ipv6 don't support broadcast and icmp types are differents. (don't known what's shorewall generate for ipv6)
fixme ----- - ip address at formatv6 not implemented) - ipset v6 not yet implemented - aliases v6 not yet implemented - host rules ipv6 not implemented (do we need it ? pve-proxy don't work with ipv6 for example) Signed-off-by: Alexandre Derumier <[email protected]> --- src/PVE/Firewall.pm | 313 ++++++++++++++++++++++++++++++++++++++------------- src/pve-firewall | 15 +-- 2 files changed, 243 insertions(+), 85 deletions(-) diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 27cf1e6..65154b7 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -576,6 +576,99 @@ my $pve_std_chains = { ], }; +my $pve_std_chainsv6 = { + '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, @@ -617,6 +710,32 @@ my $icmp_type_names = { '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 = {}; @@ -811,7 +930,7 @@ sub parse_address_list { } sub parse_port_name_number_or_range { - my ($str) = @_; + my ($str, $ipv6) = @_; my $services = PVE::Firewall::get_etc_services(); my $count = 0; @@ -827,7 +946,10 @@ sub parse_port_name_number_or_range { my $port = $1; die "invalid port '$port'\n" if $port > 65535; } else { - if ($icmp_type_names->{$item}) { + + if ($icmp_type_names->{$item} && !$ipv6) { + $icmp_port = 1; + } elsif ($icmpv6_type_names->{$item} && $ipv6) { $icmp_port = 1; } else { die "invalid port '$item'\n" if !$services->{byname}->{$item}; @@ -1064,7 +1186,7 @@ my $rule_env_iface_lookup = { }; sub verify_rule { - my ($rule, $cluster_conf, $fw_conf, $rule_env, $noerr) = @_; + my ($rule, $cluster_conf, $fw_conf, $rule_env, $noerr, $ipv6) = @_; my $allow_groups = $rule_env eq 'group' ? 0 : 1; @@ -1151,14 +1273,14 @@ sub verify_rule { } if ($rule->{dport}) { - eval { parse_port_name_number_or_range($rule->{dport}); }; + eval { parse_port_name_number_or_range($rule->{dport}, $ipv6); }; &$add_error('dport', $@) if $@; &$add_error('proto', "missing property - 'dport' requires this property") if !$rule->{proto}; } if ($rule->{sport}) { - eval { parse_port_name_number_or_range($rule->{sport}); }; + eval { parse_port_name_number_or_range($rule->{sport}, $ipv6); }; &$add_error('sport', $@) if $@; &$add_error('proto', "missing property - 'sport' requires this property") if !$rule->{proto}; @@ -1230,9 +1352,10 @@ sub enable_bridge_firewall { my $rule_format = "%-15s %-30s %-30s %-15s %-15s %-15s\n"; sub iptables_restore_cmdlist { - my ($cmdlist) = @_; + my ($cmdlist, $ipv6) = @_; - run_command("/sbin/iptables-restore -n", input => $cmdlist); + my $iptablescmd = $ipv6 ? "/sbin/ip6tables-restore" : "/sbin/iptables-restore"; + run_command("$iptablescmd -n", input => $cmdlist); } sub ipset_restore_cmdlist { @@ -1242,6 +1365,7 @@ sub ipset_restore_cmdlist { } sub iptables_get_chains { + my ($ipv6) = @_; my $res = {}; @@ -1296,7 +1420,8 @@ sub iptables_get_chains { } }; - run_command("/sbin/iptables-save", outfunc => $parser); + my $iptablescmd = $ipv6 ? "/sbin/ip6tables-save" : "/sbin/iptables-save"; + run_command($iptablescmd, outfunc => $parser); return wantarray ? ($res, $hooks) : $res; } @@ -1351,15 +1476,15 @@ sub ipset_get_chains { } sub ruleset_generate_cmdstr { - my ($ruleset, $chain, $rule, $actions, $goto, $cluster_conf, $fw_conf) = @_; + my ($ruleset, $chain, $rule, $actions, $goto, $cluster_conf, $fw_conf, $ipv6) = @_; return if defined($rule->{enable}) && !$rule->{enable}; return if $rule->{errors}; die "unable to emit macro - internal error" if $rule->{macro}; # should not happen - my $nbdport = defined($rule->{dport}) ? parse_port_name_number_or_range($rule->{dport}) : 0; - my $nbsport = defined($rule->{sport}) ? parse_port_name_number_or_range($rule->{sport}) : 0; + my $nbdport = defined($rule->{dport}) ? parse_port_name_number_or_range($rule->{dport}, $ipv6) : 0; + my $nbsport = defined($rule->{sport}) ? parse_port_name_number_or_range($rule->{sport}, $ipv6) : 0; my @cmd = (); @@ -1440,10 +1565,11 @@ sub ruleset_generate_cmdstr { if ($multiport == 2) && ($rule->{dport} ne $rule->{sport}); if ($rule->{dport}) { - if ($rule->{proto} && $rule->{proto} eq 'icmp') { + if ($rule->{proto} && ($rule->{proto} eq 'icmp' || $rule->{proto} eq 'icmpv6')) { # 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}"; + die "unknown icmp-type '$rule->{dport}'\n" if (!defined($icmp_type_names->{$rule->{dport}}) && !$ipv6); + die "unknown icmp-type '$rule->{dport}'\n" if (!defined($icmpv6_type_names->{$rule->{dport}}) && $ipv6); + push @cmd, "-m $rule->{proto} --$rule->{proto}-type $rule->{dport}"; } else { if ($nbdport > 1) { if ($multiport == 2) { @@ -1481,7 +1607,7 @@ sub ruleset_generate_cmdstr { } sub ruleset_generate_rule { - my ($ruleset, $chain, $rule, $actions, $goto, $cluster_conf, $fw_conf) = @_; + my ($ruleset, $chain, $rule, $actions, $goto, $cluster_conf, $fw_conf, $ipv6) = @_; my $rules; @@ -1495,7 +1621,7 @@ sub ruleset_generate_rule { my @cmds = (); foreach my $tmp (@$rules) { - if (my $cmdstr = ruleset_generate_cmdstr($ruleset, $chain, $tmp, $actions, $goto, $cluster_conf, $fw_conf)) { + if (my $cmdstr = ruleset_generate_cmdstr($ruleset, $chain, $tmp, $actions, $goto, $cluster_conf, $fw_conf, $ipv6)) { push @cmds, $cmdstr; } } @@ -1589,6 +1715,7 @@ sub ruleset_add_chain_policy { ruleset_addrule($ruleset, $chain, "-j DROP"); } elsif ($policy eq 'REJECT') { + ruleset_addrule($ruleset, $chain, "-j PVEFW-Reject"); ruleset_addlog($ruleset, $chain, $vmid, "policy $policy: ", $loglevel); @@ -1630,18 +1757,23 @@ sub ruleset_chain_add_input_filters { } sub ruleset_create_vm_chain { - my ($ruleset, $chain, $options, $macaddr, $ipfilter_ipset, $direction) = @_; + my ($ruleset, $chain, $options, $macaddr, $ipfilter_ipset, $direction, $ipv6) = @_; ruleset_create_chain($ruleset, $chain); my $accept = generate_nfqueue($options); if (!(defined($options->{dhcp}) && $options->{dhcp} == 0)) { if ($direction eq 'OUT') { + my $sport = $ipv6 ? "546" : "68"; + my $dport = $ipv6 ? "547" : "67"; ruleset_generate_rule($ruleset, $chain, { action => 'PVEFW-SET-ACCEPT-MARK', - proto => 'udp', sport => 68, dport => 67 }); + proto => 'udp', sport => $sport, dport => $dport }); } else { + my $sport = $ipv6 ? "547" : "67"; + my $dport = $ipv6 ? "546" : "68"; + ruleset_generate_rule($ruleset, $chain, { action => 'ACCEPT', - proto => 'udp', sport => 67, dport => 68 }); + proto => 'udp', sport => $sport, dport => $dport }); } } @@ -1657,12 +1789,12 @@ sub ruleset_create_vm_chain { } sub ruleset_add_group_rule { - my ($ruleset, $cluster_conf, $chain, $rule, $direction, $action) = @_; + my ($ruleset, $cluster_conf, $chain, $rule, $direction, $action, $ipv6) = @_; my $group = $rule->{action}; my $group_chain = "GROUP-$group-$direction"; if(!ruleset_chain_exist($ruleset, $group_chain)){ - generate_group_rules($ruleset, $cluster_conf, $group); + generate_group_rules($ruleset, $cluster_conf, $group, $ipv6); } if ($direction eq 'OUT' && $rule->{iface_out}) { @@ -1677,7 +1809,7 @@ sub ruleset_add_group_rule { } sub ruleset_generate_vm_rules { - my ($ruleset, $rules, $cluster_conf, $vmfw_conf, $chain, $netid, $direction, $options) = @_; + my ($ruleset, $rules, $cluster_conf, $vmfw_conf, $chain, $netid, $direction, $options, $ipv6) = @_; my $lc_direction = lc($direction); @@ -1688,18 +1820,18 @@ sub ruleset_generate_vm_rules { next if !$rule->{enable} || $rule->{errors}; if ($rule->{type} eq 'group') { ruleset_add_group_rule($ruleset, $cluster_conf, $chain, $rule, $direction, - $direction eq 'OUT' ? 'RETURN' : $in_accept); + $direction eq 'OUT' ? 'RETURN' : $in_accept, $ipv6); } else { next if $rule->{type} ne $lc_direction; eval { if ($direction eq 'OUT') { ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, - undef, $cluster_conf, $vmfw_conf); + undef, $cluster_conf, $vmfw_conf, $ipv6); } else { ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => $in_accept , REJECT => "PVEFW-reject" }, - undef, $cluster_conf, $vmfw_conf); + undef, $cluster_conf, $vmfw_conf, $ipv6); } }; warn $@ if $@; @@ -1741,7 +1873,7 @@ sub ruleset_generate_vm_ipsrules { } sub generate_venet_rules_direction { - my ($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, $direction) = @_; + my ($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, $direction, $ipv6) = @_; my $lc_direction = lc($direction); @@ -1752,9 +1884,9 @@ sub generate_venet_rules_direction { my $chain = "venet0-$vmid-$direction"; - ruleset_create_vm_chain($ruleset, $chain, $options, undef, undef, $direction); + ruleset_create_vm_chain($ruleset, $chain, $options, undef, undef, $direction, $ipv6); - ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $vmfw_conf, $chain, 'venet', $direction); + ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $vmfw_conf, $chain, 'venet', $direction, undef, $ipv6); # implement policy my $policy; @@ -1783,7 +1915,7 @@ sub generate_venet_rules_direction { } sub generate_tap_rules_direction { - my ($ruleset, $cluster_conf, $iface, $netid, $macaddr, $vmfw_conf, $vmid, $direction) = @_; + my ($ruleset, $cluster_conf, $iface, $netid, $macaddr, $vmfw_conf, $vmid, $direction, $ipv6) = @_; my $lc_direction = lc($direction); @@ -1799,10 +1931,10 @@ sub generate_tap_rules_direction { if $vmfw_conf->{ipset}->{$ipfilter_name}; # create chain with mac and ip filter - ruleset_create_vm_chain($ruleset, $tapchain, $options, $macaddr, $ipfilter_ipset, $direction); + ruleset_create_vm_chain($ruleset, $tapchain, $options, $macaddr, $ipfilter_ipset, $direction, $ipv6); if ($options->{enable}) { - ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $vmfw_conf, $tapchain, $netid, $direction, $options); + ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $vmfw_conf, $tapchain, $netid, $direction, $options, $ipv6); ruleset_generate_vm_ipsrules($ruleset, $options, $direction, $iface); @@ -1949,7 +2081,7 @@ sub enable_host_firewall { } sub generate_group_rules { - my ($ruleset, $cluster_conf, $group) = @_; + my ($ruleset, $cluster_conf, $group, $ipv6) = @_; my $rules = $cluster_conf->{groups}->{$group}; @@ -1965,7 +2097,7 @@ sub generate_group_rules { foreach my $rule (@$rules) { next if $rule->{type} ne 'in'; - ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, undef, $cluster_conf); + ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, undef, $cluster_conf, $ipv6); } $chain = "GROUP-${group}-OUT"; @@ -1978,7 +2110,7 @@ sub generate_group_rules { # we use PVEFW-SET-ACCEPT-MARK (Instead of ACCEPT) because we need to # check also other tap rules later ruleset_generate_rule($ruleset, $chain, $rule, - { ACCEPT => 'PVEFW-SET-ACCEPT-MARK', REJECT => "PVEFW-reject" }, undef, $cluster_conf); + { ACCEPT => 'PVEFW-SET-ACCEPT-MARK', REJECT => "PVEFW-reject" }, undef, $cluster_conf, $ipv6); } } @@ -1989,7 +2121,7 @@ for (my $i = 0; $i < $MAX_NETS; $i++) { } sub parse_fw_rule { - my ($prefix, $line, $cluster_conf, $fw_conf, $rule_env, $verbose) = @_; + my ($prefix, $line, $cluster_conf, $fw_conf, $rule_env, $verbose, $ipv6) = @_; my $orig_line = $line; @@ -2053,7 +2185,7 @@ sub parse_fw_rule { die "unable to parse rule parameters: $line\n" if length($line); - $rule = verify_rule($rule, $cluster_conf, $fw_conf, $rule_env, 1); + $rule = verify_rule($rule, $cluster_conf, $fw_conf, $rule_env, 1, $ipv6); if ($verbose && $rule->{errors}) { warn "$prefix - errors in rule parameters: $orig_line\n"; foreach my $p (keys %{$rule->{errors}}) { @@ -2168,7 +2300,7 @@ sub parse_alias { } sub generic_fw_config_parser { - my ($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env) = @_; + my ($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env, $ipv6) = @_; my $section; my $group; @@ -2265,7 +2397,7 @@ sub generic_fw_config_parser { warn "$prefix: $@" if $@; } elsif ($section eq 'rules') { my $rule; - eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, $res, $rule_env, $verbose); }; + eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, $res, $rule_env, $verbose, $ipv6); }; if (my $err = $@) { warn "$prefix: $err"; next; @@ -2273,7 +2405,7 @@ sub generic_fw_config_parser { push @{$res->{$section}}, $rule; } elsif ($section eq 'groups') { my $rule; - eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, undef, 'group', $verbose); }; + eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, undef, 'group', $verbose, $ipv6); }; if (my $err = $@) { warn "$prefix: $err"; next; @@ -2328,15 +2460,15 @@ sub generic_fw_config_parser { } sub parse_hostfw_config { - my ($filename, $fh, $cluster_conf, $verbose) = @_; + my ($filename, $fh, $cluster_conf, $verbose, $ipv6) = @_; my $empty_conf = { rules => [], options => {}}; - return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, 'host'); + return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, 'host', $ipv6); } sub parse_vmfw_config { - my ($filename, $fh, $cluster_conf, $rule_env, $verbose) = @_; + my ($filename, $fh, $cluster_conf, $rule_env, $verbose, $ipv6) = @_; my $empty_conf = { rules => [], @@ -2346,11 +2478,11 @@ sub parse_vmfw_config { ipset_comments => {}, }; - return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env); + return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env, $ipv6); } sub parse_clusterfw_config { - my ($filename, $fh, $verbose) = @_; + my ($filename, $fh, $verbose, $ipv6) = @_; my $section; my $group; @@ -2365,7 +2497,7 @@ sub parse_clusterfw_config { ipset_comments => {}, }; - return generic_fw_config_parser($filename, $fh, $verbose, $empty_conf, $empty_conf, 'cluster'); + return generic_fw_config_parser($filename, $fh, $verbose, $empty_conf, $empty_conf, 'cluster', $ipv6); } sub run_locked { @@ -2417,7 +2549,7 @@ sub read_local_vm_config { }; sub load_vmfw_conf { - my ($cluster_conf, $rule_env, $vmid, $dir, $verbose) = @_; + my ($cluster_conf, $rule_env, $vmid, $dir, $verbose, $ipv6) = @_; my $vmfw_conf = {}; @@ -2425,7 +2557,7 @@ sub load_vmfw_conf { my $filename = "$dir/$vmid.fw"; if (my $fh = IO::File->new($filename, O_RDONLY)) { - $vmfw_conf = parse_vmfw_config($filename, $fh, $cluster_conf, $rule_env, $verbose); + $vmfw_conf = parse_vmfw_config($filename, $fh, $cluster_conf, $rule_env, $verbose, $ipv6); $vmfw_conf->{vmid} = $vmid; } @@ -2562,17 +2694,17 @@ sub save_vmfw_conf { } sub read_vm_firewall_configs { - my ($cluster_conf, $vmdata, $dir, $verbose) = @_; + my ($cluster_conf, $vmdata, $dir, $verbose, $ipv6) = @_; my $vmfw_configs = {}; foreach my $vmid (keys %{$vmdata->{qemu}}) { - my $vmfw_conf = load_vmfw_conf($cluster_conf, 'vm', $vmid, $dir, $verbose); + my $vmfw_conf = load_vmfw_conf($cluster_conf, 'vm', $vmid, $dir, $verbose, $ipv6); next if !$vmfw_conf->{options}; # skip if file does not exists $vmfw_configs->{$vmid} = $vmfw_conf; } foreach my $vmid (keys %{$vmdata->{openvz}}) { - my $vmfw_conf = load_vmfw_conf($cluster_conf, 'ct', $vmid, $dir, $verbose); + my $vmfw_conf = load_vmfw_conf($cluster_conf, 'ct', $vmid, $dir, $verbose, $ipv6); next if !$vmfw_conf->{options}; # skip if file does not exists $vmfw_configs->{$vmid} = $vmfw_conf; } @@ -2598,7 +2730,7 @@ sub get_option_log_level { } sub generate_std_chains { - my ($ruleset, $options) = @_; + my ($ruleset, $options, $pve_std_chains, $ipv6) = @_; my $loglevel = get_option_log_level($options, 'smurf_log_level'); @@ -2622,7 +2754,7 @@ sub generate_std_chains { ruleset_create_chain($ruleset, $chain); foreach my $rule (@{$pve_std_chains->{$chain}}) { if (ref($rule)) { - ruleset_generate_rule($ruleset, $chain, $rule); + ruleset_generate_rule($ruleset, $chain, $rule, undef, undef, undef, undef, $ipv6); } else { ruleset_addrule($ruleset, $chain, $rule); } @@ -2689,13 +2821,13 @@ sub round_powerof2 { } sub load_clusterfw_conf { - my ($filename, $verbose) = @_; + my ($filename, $verbose, $ipv6) = @_; $filename = $clusterfw_conf_filename if !defined($filename); my $cluster_conf = {}; if (my $fh = IO::File->new($filename, O_RDONLY)) { - $cluster_conf = parse_clusterfw_config($filename, $fh, $verbose); + $cluster_conf = parse_clusterfw_config($filename, $fh, $verbose, $ipv6); } return $cluster_conf; @@ -2741,13 +2873,13 @@ sub save_clusterfw_conf { } sub load_hostfw_conf { - my ($cluster_conf, $filename, $verbose) = @_; + my ($cluster_conf, $filename, $verbose, $ipv6) = @_; $filename = $hostfw_conf_filename if !defined($filename); my $hostfw_conf = {}; if (my $fh = IO::File->new($filename, O_RDONLY)) { - $hostfw_conf = parse_hostfw_config($filename, $fh, $cluster_conf, $verbose); + $hostfw_conf = parse_hostfw_config($filename, $fh, $cluster_conf, $verbose, $ipv6); } return $hostfw_conf; } @@ -2771,26 +2903,26 @@ sub save_hostfw_conf { } sub compile { - my ($cluster_conf, $hostfw_conf, $vmdata, $verbose) = @_; + my ($cluster_conf, $hostfw_conf, $vmdata, $verbose, $ipv6) = @_; my $vmfw_configs; if ($vmdata) { # test mode my $testdir = $vmdata->{testdir} || die "no test directory specified"; my $filename = "$testdir/cluster.fw"; - $cluster_conf = load_clusterfw_conf($filename, $verbose); + $cluster_conf = load_clusterfw_conf($filename, $verbose, $ipv6); $filename = "$testdir/host.fw"; - $hostfw_conf = load_hostfw_conf($cluster_conf, $filename, $verbose); + $hostfw_conf = load_hostfw_conf($cluster_conf, $filename, $verbose, $ipv6); - $vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, $testdir, $verbose); + $vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, $testdir, $verbose, $ipv6); } else { # normal operation - $cluster_conf = load_clusterfw_conf(undef, $verbose) if !$cluster_conf; + $cluster_conf = load_clusterfw_conf(undef, $verbose, $ipv6) if !$cluster_conf; - $hostfw_conf = load_hostfw_conf($cluster_conf, undef, $verbose) if !$hostfw_conf; + $hostfw_conf = load_hostfw_conf($cluster_conf, undef, $verbose, $ipv6) if !$hostfw_conf; $vmdata = read_local_vm_config(); - $vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, undef, $verbose); + $vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, undef, $verbose, $ipv6); } $cluster_conf->{ipset}->{venet0} = []; @@ -2840,13 +2972,17 @@ sub compile { ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o venet0 -m set --match-set ${venet0_ipset_chain} dst -j PVEFW-VENET-IN"); - generate_std_chains($ruleset, $hostfw_options); + if($ipv6){ + generate_std_chains($ruleset, $hostfw_options, $pve_std_chainsv6, $ipv6); + }else{ + generate_std_chains($ruleset, $hostfw_options, $pve_std_chains); + } my $hostfw_enable = !(defined($hostfw_options->{enable}) && ($hostfw_options->{enable} == 0)); my $ipset_ruleset = {}; - if ($hostfw_enable) { + if ($hostfw_enable && !$ipv6) { eval { enable_host_firewall($ruleset, $hostfw_conf, $cluster_conf); }; warn $@ if $@; # just to be sure - should not happen } @@ -2870,9 +3006,9 @@ sub compile { my $macaddr = $net->{macaddr}; generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, - $vmfw_conf, $vmid, 'IN'); + $vmfw_conf, $vmid, 'IN', $ipv6); generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, - $vmfw_conf, $vmid, 'OUT'); + $vmfw_conf, $vmid, 'OUT', $ipv6); } }; warn $@ if $@; # just to be sure - should not happen @@ -2902,8 +3038,8 @@ sub compile { push @{$cluster_conf->{ipset}->{venet0}}, $venet0ipset; } - generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'IN'); - generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'OUT'); + generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'IN', $ipv6); + generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'OUT', $ipv6); } } @@ -2916,9 +3052,9 @@ sub compile { my $macaddr = $d->{mac}; my $iface = $d->{host_ifname}; generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, - $vmfw_conf, $vmid, 'IN'); + $vmfw_conf, $vmid, 'IN', $ipv6); generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr, - $vmfw_conf, $vmid, 'OUT'); + $vmfw_conf, $vmid, 'OUT', $ipv6); } } }; @@ -2980,11 +3116,11 @@ sub print_sig_rule { } sub get_ruleset_cmdlist { - my ($ruleset, $verbose) = @_; + my ($ruleset, $verbose, $ipv6) = @_; my $cmdlist = "*filter\n"; # we pass this to iptables-restore; - my ($active_chains, $hooks) = iptables_get_chains(); + my ($active_chains, $hooks) = iptables_get_chains($ipv6); my $statushash = get_ruleset_status($ruleset, $active_chains, \&iptables_chain_digest, $verbose); # create missing chains first @@ -3095,7 +3231,7 @@ sub get_ipset_cmdlist { } sub apply_ruleset { - my ($ruleset, $hostfw_conf, $ipset_ruleset, $verbose) = @_; + my ($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6, $verbose) = @_; enable_bridge_firewall(); @@ -3103,6 +3239,7 @@ sub apply_ruleset { get_ipset_cmdlist($ipset_ruleset, undef, $verbose); my ($cmdlist, $changes) = get_ruleset_cmdlist($ruleset, $verbose); + my ($cmdlistv6, $changesv6) = get_ruleset_cmdlist($rulesetv6, $verbose, 1); if ($verbose) { if ($ipset_changes) { @@ -3115,11 +3252,17 @@ sub apply_ruleset { print "iptables changes:\n"; print $cmdlist; } + + if ($changesv6) { + print "ip6tables changes:\n"; + print $cmdlistv6; + } } ipset_restore_cmdlist($ipset_create_cmdlist); iptables_restore_cmdlist($cmdlist); + iptables_restore_cmdlist($cmdlistv6, 1); ipset_restore_cmdlist($ipset_delete_cmdlist) if $ipset_delete_cmdlist; @@ -3136,6 +3279,18 @@ sub apply_ruleset { } } + # test ipv6 + my $active_chainsv6 = iptables_get_chains(1); + 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); @@ -3179,8 +3334,9 @@ sub update_nf_conntrack_tcp_timeout_established { } sub remove_pvefw_chains { + my ($ipv6) = @_; - my ($chash, $hooks) = iptables_get_chains(); + my ($chash, $hooks) = iptables_get_chains($ipv6); my $cmdlist = "*filter\n"; foreach my $h (qw(INPUT OUTPUT FORWARD)) { @@ -3198,7 +3354,7 @@ sub remove_pvefw_chains { } $cmdlist .= "COMMIT\n"; - iptables_restore_cmdlist($cmdlist); + iptables_restore_cmdlist($cmdlist, $ipv6); my $ipset_chains = ipset_get_chains(); @@ -3230,14 +3386,15 @@ sub update { if (!$cluster_options->{enable}) { PVE::Firewall::remove_pvefw_chains(); + PVE::Firewall::remove_pvefw_chains(1); return; } my $hostfw_conf = load_hostfw_conf(); my ($ruleset, $ipset_ruleset) = compile($cluster_conf, $hostfw_conf); - - apply_ruleset($ruleset, $hostfw_conf, $ipset_ruleset); + my ($rulesetv6) = compile($cluster_conf, $hostfw_conf, undef, undef, 1); + apply_ruleset($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6); }; run_locked($code); diff --git a/src/pve-firewall b/src/pve-firewall index befee44..cc15c83 100755 --- a/src/pve-firewall +++ b/src/pve-firewall @@ -153,7 +153,7 @@ sub run_server { 1 while (waitpid(-1, POSIX::WNOHANG()) > 0); syslog('info' , "clear firewall rules"); - eval { PVE::Firewall::remove_pvefw_chains(); die "STOP";}; + eval { PVE::Firewall::remove_pvefw_chains(); PVE::Firewall::remove_pvefw_chains(1); die "STOP";}; warn $@ if $@; cleanup(); @@ -345,12 +345,12 @@ __PACKAGE__->register_method ({ if ($status eq 'running') { my ($ruleset, $ipset_ruleset) = PVE::Firewall::compile($cluster_conf, undef, undef, $verbose); - + my ($rulesetv6) = PVE::Firewall::compile($cluster_conf, undef, undef, $verbose, 1); $verbose = 0; # do not show iptables details my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset, $verbose); my ($test, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset, $verbose); - - $res->{changes} = ($ipset_changes || $ruleset_changes) ? 1 : 0; + my ($testv6, $ruleset6_changes) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, $verbose, 1); + $res->{changes} = ($ipset_changes == 1 || $ruleset_changes == 1 || $rulesetv6 == 1) ? 1 : 0; } return $res; @@ -377,15 +377,16 @@ __PACKAGE__->register_method ({ my $code = sub { - my $verbose = 1; + my $verbose = 0; my $cluster_conf = PVE::Firewall::load_clusterfw_conf(undef, $verbose); my ($ruleset, $ipset_ruleset) = PVE::Firewall::compile($cluster_conf, undef, undef, $verbose); + my ($rulesetv6) = PVE::Firewall::compile($cluster_conf, undef, undef, $verbose, 1); my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset, $verbose); my (undef, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset, $verbose); - - if ($ipset_changes || $ruleset_changes) { + my (undef, $ruleset6_changes) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, $verbose, 1); + if ($ipset_changes || $ruleset_changes || $ruleset6_changes) { print "detected changes\n"; } else { print "no changes\n"; -- 1.7.10.4 _______________________________________________ pve-devel mailing list [email protected] http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
