X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=src%2FPVE%2FFirewall.pm;h=5168093d9fca75ff0cd42d9d46865ff984449fa7;hp=9a8561e0cf2018a3b1a0ed0243149a6f1d2eec93;hb=6e299ae385402df8019ecf34db56f05a0e9ffeae;hpb=7ca36671e958d6b740f60c31d034aff188f4ed92 diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 9a8561e..5168093 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -694,6 +694,8 @@ sub parse_port_name_number_or_range { my $services = PVE::Firewall::get_etc_services(); my $count = 0; + my $icmp_port = 0; + foreach my $item (split(/,/, $str)) { $count++; if ($item =~ m/^(\d+):(\d+)$/) { @@ -704,10 +706,16 @@ sub parse_port_name_number_or_range { my $port = $1; die "invalid port '$port'\n" if $port > 65535; } else { - die "invalid port '$item'\n" if !$services->{byname}->{$item}; + if ($icmp_type_names->{$item}) { + $icmp_port = 1; + } else { + die "invalid port '$item'\n" if !$services->{byname}->{$item}; + } } } + die "ICPM ports not allowed in port range\n" if $icmp_port && $count > 1; + return $count; } @@ -847,6 +855,57 @@ sub delete_rule_properties { return $rule; } +my $apply_macro = sub { + my ($macro_name, $param, $verify) = @_; + + my $macro_rules = $pve_fw_parsed_macros->{$macro_name}; + die "unknown macro '$macro_name'\n" if !$macro_rules; # should not happen + + my $rules = []; + + foreach my $templ (@$macro_rules) { + my $rule = {}; + my $param_used = {}; + foreach my $k (keys %$templ) { + my $v = $templ->{$k}; + if ($v eq 'PARAM') { + $v = $param->{$k}; + $param_used->{$k} = 1; + } elsif ($v eq 'DEST') { + $v = $param->{dest}; + $param_used->{dest} = 1; + } elsif ($v eq 'SOURCE') { + $v = $param->{source}; + $param_used->{source} = 1; + } + + if (!defined($v)) { + my $msg = "missing parameter '$k' in macro '$macro_name'"; + raise_param_exc({ macro => $msg }) if $verify; + die "$msg\n"; + } + $rule->{$k} = $v; + } + foreach my $k (keys %$param) { + next if $k eq 'macro'; + next if !defined($param->{$k}); + next if $param_used->{$k}; + if (defined($rule->{$k})) { + if ($rule->{$k} ne $param->{$k}) { + my $msg = "parameter '$k' already define in macro (value = '$rule->{$k}')"; + raise_param_exc({ $k => $msg }) if $verify; + die "$msg\n"; + } + } else { + $rule->{$k} = $param->{$k}; + } + } + push @$rules, $rule; + } + + return $rules; +}; + sub verify_rule { my ($rule, $allow_groups) = @_; @@ -895,6 +954,10 @@ sub verify_rule { raise_param_exc({ dest => $@ }) if $@; } + if ($rule->{macro}) { + &$apply_macro($rule->{macro}, $rule, 1); + } + return $rule; } @@ -1154,50 +1217,6 @@ sub ruleset_generate_cmdstr { return scalar(@cmd) ? join(' ', @cmd) : undef; } -my $apply_macro = sub { - my ($macro_name, $param) = @_; - - my $macro_rules = $pve_fw_parsed_macros->{$macro_name}; - die "unknown macro '$macro_name'\n" if !$macro_rules; # should not happen - - my $rules = []; - - foreach my $templ (@$macro_rules) { - my $rule = {}; - my $param_used = {}; - foreach my $k (keys %$templ) { - my $v = $templ->{$k}; - if ($v eq 'PARAM') { - $v = $param->{$k}; - $param_used->{$k} = 1; - } elsif ($v eq 'DEST') { - $v = $param->{dest}; - $param_used->{dest} = 1; - } elsif ($v eq 'SOURCE') { - $v = $param->{source}; - $param_used->{source} = 1; - } - - die "missing parameter '$k' in macro '$macro_name'\n" if !defined($v); - $rule->{$k} = $v; - } - foreach my $k (keys %$param) { - next if $k eq 'macro'; - next if !defined($param->{$k}); - next if $param_used->{$k}; - if (defined($rule->{$k})) { - die "parameter '$k' already define in macro (value = '$rule->{$k}')\n" - if $rule->{$k} ne $param->{$k}; - } else { - $rule->{$k} = $param->{$k}; - } - } - push @$rules, $rule; - } - - return $rules; -}; - sub ruleset_generate_rule { my ($ruleset, $chain, $rule, $actions, $goto, $cluster_conf) = @_; @@ -1986,29 +2005,26 @@ sub parse_cluster_fw_rules { } push @{$res->{$section}->{$group}}, $rule; } elsif ($section eq 'ipset') { - chomp $line; - $line =~ m/^(\!)?\s*((\d+)\.(\d+)\.(\d+)\.(\d+)(\/(\d+))?)/; + # we can add single line comments to the end of the rule + my $comment = decode('utf8', $1) if $line =~ s/#\s*(.*?)\s*$//; + + $line =~ m/^(\!)?\s*((?:\d+)\.(?:\d+)\.(?:\d+)\.(?:\d+))(?:\/(\d+))?\s*$/; my $nomatch = $1; - my $ip = $2; + my $cidr = $2; - if(!$ip){ - warn "$prefix: $line is not an valid ip address\n"; - next; - } - if (!Net::IP->new($ip)) { - warn "$prefix: $line is not an valid ip address\n"; + $cidr .= "/$3" if defined($3) && $3 != 32; + + if (!Net::IP->new($cidr)) { + my $err = Net::IP::Error(); + warn "$prefix: $cidr - $err\n"; next; } - if ($nomatch) { - if ($feature_ipset_nomatch) { - push @{$res->{$section}->{$group}}, "$ip nomatch"; - } else { - warn "$prefix: ignore $line - nomatch not supported by kernel\n"; - } - } else { - push @{$res->{$section}->{$group}}, $ip; - } + my $entry = { cidr => $cidr }; + $entry->{nomatch} = 1 if $nomatch; + $entry->{comment} = $comment if $comment; + + push @{$res->{$section}->{$group}}, $entry; } } @@ -2122,6 +2138,28 @@ my $format_options = sub { return $raw; }; +my $format_ipset = sub { + my ($options) = @_; + + my $raw = ''; + + my $nethash = {}; + foreach my $entry (@$options) { + $nethash->{$entry->{cidr}} = $entry; + } + + foreach my $cidr (sort keys %$nethash) { + my $entry = $nethash->{$cidr}; + my $line = $entry->{nomatch} ? '!' : ''; + $line .= $entry->{cidr}; + $line .= " # " . encode('utf8', $entry->{comment}) + if $entry->{comment} && $entry->{comment} !~ m/^\s*$/; + $raw .= "$line\n"; + } + + return $raw; +}; + sub save_vmfw_conf { my ($vmid, $vmfw_conf) = @_; @@ -2221,8 +2259,18 @@ sub generate_ipset { push @{$ipset_ruleset->{$name}}, "create $name hash:net family inet hashsize $hashsize maxelem $hashsize"; - foreach my $ip (@$options) { - push @{$ipset_ruleset->{$name}}, "add $name $ip"; + foreach my $entry (@$options) { + my $cidr = $entry->{cidr}; + my $cmd = "add $name $cidr"; + if ($entry->{nomatch}) { + if ($feature_ipset_nomatch) { + push @{$ipset_ruleset->{$name}}, "$cmd nomatch"; + } else { + warn "ignore !$cidr - nomatch not supported by kernel\n"; + } + } else { + push @{$ipset_ruleset->{$name}}, $cmd; + } } } @@ -2304,7 +2352,12 @@ sub save_clusterfw_conf { my $options = $cluster_conf->{options}; $raw .= &$format_options($options) if scalar(keys %$options); - # fixme: save ipset + foreach my $ipset (sort keys %{$cluster_conf->{ipset}}) { + $raw .= "[IPSET $ipset]\n\n"; + my $options = $cluster_conf->{ipset}->{$ipset}; + $raw .= &$format_ipset($options); + $raw .= "\n"; + } my $rules = $cluster_conf->{rules}; if (scalar(@$rules)) {