X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2FPVE%2FFirewall.pm;h=83915df8728720eb9ce3177461699510a29efb71;hb=ff5d050e12644e08eafdcd3b476fee7a9f38beaf;hp=958927acdf70bbf906f49b29f4af1fbcb9a62e12;hpb=50f9a28d1aef2ed94f1c6c0e736542c8d082151e;p=pve-firewall.git diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 958927a..83915df 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -113,8 +113,6 @@ eval { }; -use Data::Dumper; - my $nodename = PVE::INotify::nodename(); my $pve_fw_lock_filename = "/var/lock/pvefw.lck"; @@ -132,6 +130,23 @@ my $log_level_hash = { emerg => 0, }; +# we need to overwrite some macros for ipv6 +my $pve_ipv6fw_macros = { + 'Ping' => [ + { action => 'PARAM', proto => 'icmpv6', dport => 'echo-request' }, + ], + 'NeighborDiscovery' => [ + "IPv6 neighbor solicitation, neighbor and router advertisement", + { action => 'PARAM', proto => 'icmpv6', dport => 'router-advertisement' }, + { action => 'PARAM', proto => 'icmpv6', dport => 'neighbor-solicitation' }, + { action => 'PARAM', proto => 'icmpv6', dport => 'neighbor-advertisement' }, + ], + 'Trcrt' => [ + { action => 'PARAM', proto => 'udp', dport => '33434:33524' }, + { action => 'PARAM', proto => 'icmpv6', dport => 'echo-request' }, + ], + }; + # imported/converted from: /usr/share/shorewall/macro.* my $pve_fw_macros = { 'Amanda' => [ @@ -157,6 +172,11 @@ my $pve_fw_macros = { { action => 'PARAM', proto => 'tcp', dport => '6881:6999' }, { action => 'PARAM', proto => 'udp', dport => '6881' }, ], + 'Ceph' => [ + "Ceph Storage Cluster traffic (Ceph Monitors, OSD & MDS Deamons)", + { action => 'PARAM', proto => 'tcp', dport => '6789' }, + { action => 'PARAM', proto => 'tcp', dport => '6800:7300' }, + ], 'CVS' => [ "Concurrent Versions System pserver traffic", { action => 'PARAM', proto => 'tcp', dport => '2401' }, @@ -486,6 +506,7 @@ my $pve_fw_macros = { my $pve_fw_parsed_macros; my $pve_fw_macro_descr; +my $pve_fw_macro_ipversion = {}; my $pve_fw_preferred_macro_names = {}; my $pve_std_chains = {}; @@ -724,7 +745,9 @@ my $icmpv6_type_names = { 'echo-reply' => 1, 'router-solicitation' => 1, 'router-advertisement' => 1, + 'neighbor-solicitation' => 1, 'neighbour-solicitation' => 1, + 'neighbor-advertisement' => 1, 'neighbour-advertisement' => 1, 'redirect' => 1, }; @@ -733,14 +756,32 @@ sub init_firewall_macros { $pve_fw_parsed_macros = {}; - foreach my $k (keys %$pve_fw_macros) { + my $parse = sub { + my ($k, $macro) = @_; my $lc_name = lc($k); - my $macro = $pve_fw_macros->{$k}; - if (!ref($macro->[0])) { - $pve_fw_macro_descr->{$k} = shift @$macro; + $pve_fw_macro_ipversion->{$k} = 0; + while (!ref($macro->[0])) { + my $desc = shift @$macro; + if ($desc eq 'ipv4only') { + $pve_fw_macro_ipversion->{$k} = 4; + } elsif ($desc eq 'ipv6only') { + $pve_fw_macro_ipversion->{$k} = 6; + } else { + $pve_fw_macro_descr->{$k} = $desc; + } } $pve_fw_preferred_macro_names->{$lc_name} = $k; $pve_fw_parsed_macros->{$k} = $macro; + }; + + foreach my $k (keys %$pve_fw_macros) { + &$parse($k, $pve_fw_macros->{$k}); + } + + foreach my $k (keys %$pve_ipv6fw_macros) { + next if $pve_fw_parsed_macros->{$k}; + &$parse($k, $pve_ipv6fw_macros->{$k}); + $pve_fw_macro_ipversion->{$k} = 6; } } @@ -858,14 +899,20 @@ sub local_network { my $testip = Net::IP->new($ip); - my $routes = PVE::ProcFSTools::read_proc_net_route(); + my $isv6 = $testip->version == 6; + my $routes = $isv6 ? PVE::ProcFSTools::read_proc_net_ipv6_route() + : PVE::ProcFSTools::read_proc_net_route(); foreach my $entry (@$routes) { - my $mask = $ipv4_mask_hash_localnet->{$entry->{mask}}; - next if !defined($mask); - return if $mask eq '0.0.0.0'; + my $mask; + if ($isv6) { + $mask = $entry->{prefix}; + } else { + $mask = $ipv4_mask_hash_localnet->{$entry->{mask}}; + next if !defined($mask); + } my $cidr = "$entry->{dest}/$mask"; my $testnet = Net::IP->new($cidr); - if ($testnet->overlaps($testip)) { + if ($testnet->overlaps($testip) == $Net::IP::IP_B_IN_A_OVERLAP) { $__local_network = $cidr; return; } @@ -1095,7 +1142,8 @@ my $rule_properties = { optional => 1, }, enable => { - type => 'boolean', + type => 'integer', + minimum => 0, optional => 1, }, sport => { @@ -1139,11 +1187,20 @@ sub delete_rule_properties { } my $apply_macro = sub { - my ($macro_name, $param, $verify) = @_; + my ($macro_name, $param, $verify, $ipversion) = @_; my $macro_rules = $pve_fw_parsed_macros->{$macro_name}; die "unknown macro '$macro_name'\n" if !$macro_rules; # should not happen + if ($ipversion && ($ipversion == 6) && $pve_ipv6fw_macros->{$macro_name}) { + $macro_rules = $pve_ipv6fw_macros->{$macro_name}; + } + + # skip macros which are specific to another ipversion + if ($ipversion && (my $required = $pve_fw_macro_ipversion->{$macro_name})) { + return if $ipversion != $required; + } + my $rules = []; foreach my $templ (@$macro_rules) { @@ -1243,8 +1300,7 @@ sub verify_rule { my $alias = lc($value); &$add_error($name, "no such alias '$value'") if !($cluster_conf->{aliases}->{$alias} || ($fw_conf && $fw_conf->{aliases}->{$alias})); - - my $e = $fw_conf->{aliases}->{$alias} if $fw_conf; + my $e = $fw_conf ? $fw_conf->{aliases}->{$alias} : undef; $e = $cluster_conf->{aliases}->{$alias} if !$e && $cluster_conf; &$set_ip_version($e->{ipversion}); @@ -1333,8 +1389,10 @@ sub verify_rule { &$check_ipset_or_alias_property('dest', $ipversion); } + $rule->{ipversion} = $ipversion if $ipversion; + if ($rule->{macro} && !$error_count) { - eval { &$apply_macro($rule->{macro}, $rule, 1); }; + eval { &$apply_macro($rule->{macro}, $rule, 1, $ipversion); }; if (my $err = $@) { if (ref($err) eq "PVE::Exception" && $err->{errors}) { my $eh = $err->{errors}; @@ -1348,7 +1406,6 @@ sub verify_rule { } $rule->{errors} = $errors if $error_count; - $rule->{ipversion} = $ipversion if $ipversion; return $rule; } @@ -1442,7 +1499,7 @@ sub ip6tables_restore_cmdlist { sub ipset_restore_cmdlist { my ($cmdlist) = @_; - run_command("/usr/sbin/ipset restore", input => $cmdlist, errmsg => "ipset_restore_cmdlist"); + run_command("/sbin/ipset restore", input => $cmdlist, errmsg => "ipset_restore_cmdlist"); } sub iptables_get_chains { @@ -1547,7 +1604,7 @@ sub ipset_get_chains { } }; - run_command("/usr/sbin/ipset save", outfunc => $parser); + run_command("/sbin/ipset save", outfunc => $parser); # compute digest for each chain foreach my $chain (keys %$chains) { @@ -1594,7 +1651,7 @@ sub ruleset_generate_cmdstr { } } elsif ($source =~ m/^${ip_alias_pattern}$/){ my $alias = lc($source); - my $e = $fw_conf->{aliases}->{$alias} if $fw_conf; + my $e = $fw_conf ? $fw_conf->{aliases}->{$alias} : undef; $e = $cluster_conf->{aliases}->{$alias} if !$e && $cluster_conf; die "no such alias '$source'\n" if !$e; push @cmd, "-s $e->{cidr}"; @@ -1623,7 +1680,7 @@ sub ruleset_generate_cmdstr { } } elsif ($dest =~ m/^${ip_alias_pattern}$/){ my $alias = lc($dest); - my $e = $fw_conf->{aliases}->{$alias} if $fw_conf; + my $e = $fw_conf ? $fw_conf->{aliases}->{$alias} : undef; $e = $cluster_conf->{aliases}->{$alias} if !$e && $cluster_conf; die "no such alias '$dest'\n" if !$e; push @cmd, "-d $e->{cidr}"; @@ -1697,7 +1754,7 @@ sub ruleset_generate_rule { my $rules; if ($rule->{macro}) { - $rules = &$apply_macro($rule->{macro}, $rule); + $rules = &$apply_macro($rule->{macro}, $rule, 0, $ipversion); } else { $rules = [ $rule ]; } @@ -2077,6 +2134,7 @@ sub enable_host_firewall { # add host rules first, so that cluster wide rules can be overwritten foreach my $rule (@$rules, @$cluster_rules) { next if !$rule->{enable} || $rule->{errors}; + next if $rule->{ipversion} && ($rule->{ipversion} != $ipversion); $rule->{iface_in} = $rule->{iface} if $rule->{iface}; @@ -2101,10 +2159,11 @@ sub enable_host_firewall { ruleset_addrule($ruleset, $chain, "$mngmntsrc -p tcp --dport 3128 -j $accept_action"); # SPICE Proxy ruleset_addrule($ruleset, $chain, "$mngmntsrc -p tcp --dport 22 -j $accept_action"); # SSH - my $localnet = local_network(); + my $localnet = $cluster_conf->{aliases}->{local_network}->{cidr}; + my $localnet_ver = $cluster_conf->{aliases}->{local_network}->{ipversion}; # corosync - if ($localnet) { + if ($localnet && ($ipversion == $localnet_ver)) { my $corosync_rule = "-p udp --dport 5404:5405 -j $accept_action"; ruleset_addrule($ruleset, $chain, "-s $localnet -d $localnet $corosync_rule"); ruleset_addrule($ruleset, $chain, "-s $localnet -m addrtype --dst-type MULTICAST $corosync_rule"); @@ -2132,6 +2191,7 @@ sub enable_host_firewall { # add host rules first, so that cluster wide rules can be overwritten foreach my $rule (@$rules, @$cluster_rules) { next if !$rule->{enable} || $rule->{errors}; + next if $rule->{ipversion} && ($rule->{ipversion} != $ipversion); $rule->{iface_out} = $rule->{iface} if $rule->{iface}; eval { @@ -2148,7 +2208,7 @@ sub enable_host_firewall { } # allow standard traffic on cluster network - if ($localnet) { + if ($localnet && ($ipversion == $localnet_ver)) { ruleset_addrule($ruleset, $chain, "-d $localnet -p tcp --dport 8006 -j $accept_action"); # PVE API ruleset_addrule($ruleset, $chain, "-d $localnet -p tcp --dport 22 -j $accept_action"); # SSH ruleset_addrule($ruleset, $chain, "-d $localnet -p tcp --dport 5900:5999 -j $accept_action"); # PVE VNC Console @@ -2342,9 +2402,12 @@ sub parse_clusterfw_option { my ($opt, $value); - if ($line =~ m/^(enable):\s*(0|1)\s*$/i) { + if ($line =~ m/^(enable):\s*(\d+)\s*$/i) { $opt = lc($1); $value = int($2); + if (($value > 1) && ((time() - $value) > 60)) { + $value = 0 + } } elsif ($line =~ m/^(policy_(in|out)):\s*(ACCEPT|DROP|REJECT)\s*$/i) { $opt = lc($1); $value = uc($3); @@ -2359,7 +2422,7 @@ sub resolve_alias { my ($clusterfw_conf, $fw_conf, $cidr) = @_; my $alias = lc($cidr); - my $e = $fw_conf->{aliases}->{$alias} if $fw_conf; + my $e = $fw_conf ? $fw_conf->{aliases}->{$alias} : undef; $e = $clusterfw_conf->{aliases}->{$alias} if !$e && $clusterfw_conf; die "no such alias '$cidr'\n" if !$e;; @@ -2897,8 +2960,7 @@ sub generate_ipset_chains { } #http://backreference.org/2013/03/01/ipv6-address-normalization/ if ($ver == 6) { - my $ipv6 = inet_pton(AF_INET6, lc($cidr)); - $cidr = inet_ntop(AF_INET6, $ipv6); + $cidr = lc(Net::IP::ip_compress_address($cidr, 6)); $cidr =~ s|/128$||; } else { $cidr =~ s|/32$||; @@ -3072,8 +3134,11 @@ sub compile_iptables_filter { if ($cluster_conf->{aliases}->{local_network}) { $localnet = $cluster_conf->{aliases}->{local_network}->{cidr}; } else { - $localnet = local_network() || '127.0.0.0/8'; - $cluster_conf->{aliases}->{local_network} = { cidr => $localnet }; + my $localnet_ver; + ($localnet, $localnet_ver) = parse_ip_or_cidr(local_network() || '127.0.0.0/8'); + + $cluster_conf->{aliases}->{local_network} = { + name => 'local_network', cidr => $localnet, ipversion => $localnet_ver }; } push @{$cluster_conf->{ipset}->{management}}, { cidr => $localnet }; @@ -3118,8 +3183,7 @@ sub compile_iptables_filter { my $ipset_ruleset = {}; - # currently pveproxy don't works with ipv6, so let's generate host fw ipv4 only for the moment - if ($hostfw_enable && ($ipversion == 4)) { + if ($hostfw_enable) { eval { enable_host_firewall($ruleset, $hostfw_conf, $cluster_conf, $ipversion); }; warn $@ if $@; # just to be sure - should not happen }