X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=src%2FPVE%2FFirewall.pm;h=66f4b91a25aaf2f2b733dccf0a4edd9c143efb9d;hp=3b6b24588274829e639e314cdb9cebacb19e1afb;hb=6c22157652291ed6b3b5e9e8c6ee614a3b5f92eb;hpb=e2c627332f86e357b06773208feb5e235b53e307 diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 3b6b245..66f4b91 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -35,6 +35,14 @@ eval { $have_pve_manager = 1; }; +my $security_group_name_pattern = '[A-Za-z][A-Za-z0-9\-\_]+'; +my $ipset_name_pattern = '[A-Za-z][A-Za-z0-9\-\_]+'; +my $ip_alias_pattern = '[A-Za-z][A-Za-z0-9\-\_]+'; + +my $max_alias_name_length = 64; +my $max_ipset_name_length = 64; +my $max_group_name_length = 20; + PVE::JSONSchema::register_format('IPv4orCIDR', \&pve_verify_ipv4_or_cidr); sub pve_verify_ipv4_or_cidr { my ($cidr, $noerr) = @_; @@ -48,20 +56,35 @@ sub pve_verify_ipv4_or_cidr { die "value does not look like a valid IP address or CIDR network\n"; } +PVE::JSONSchema::register_format('IPv4orCIDRorAlias', \&pve_verify_ipv4_or_cidr_or_alias); +sub pve_verify_ipv4_or_cidr_or_alias { + my ($cidr, $noerr) = @_; + + return if $cidr =~ m/^(?:$ip_alias_pattern)$/; + + if ($cidr =~ m!^(?:$IPV4RE)(/(\d+))?$!) { + return $cidr if Net::IP->new($cidr); + return undef if $noerr; + die Net::IP::Error() . "\n"; + } + return undef if $noerr; + die "value does not look like a valid IP address or CIDR network\n"; +} + PVE::JSONSchema::register_standard_option('ipset-name', { description => "IP set name.", type => 'string', - pattern => '[A-Za-z][A-Za-z0-9\-\_]+', + pattern => $ipset_name_pattern, minLength => 2, - maxLength => 20, + maxLength => $max_ipset_name_length, }); PVE::JSONSchema::register_standard_option('pve-fw-alias', { description => "Alias name.", type => 'string', - pattern => '[A-Za-z][A-Za-z0-9\-\_]+', + pattern => $ip_alias_pattern, minLength => 2, - maxLength => 20, + maxLength => $max_alias_name_length, }); PVE::JSONSchema::register_standard_option('pve-fw-loglevel' => { @@ -71,19 +94,12 @@ PVE::JSONSchema::register_standard_option('pve-fw-loglevel' => { optional => 1, }); -my $security_group_name_pattern = '[A-Za-z][A-Za-z0-9\-\_]+'; -my $ip_alias_pattern = '[A-Za-z][A-Za-z0-9\-\_]+'; - -my $max_alias_name_length = 64; -my $max_ipset_name_length = 64; - - PVE::JSONSchema::register_standard_option('pve-security-group-name', { description => "Security Group name.", type => 'string', pattern => $security_group_name_pattern, minLength => 2, - maxLength => 20, + maxLength => $max_group_name_length, }); my $feature_ipset_nomatch = 0; @@ -1065,12 +1081,12 @@ sub verify_rule { if (my $value = $rule->{$name}) { if ($value =~ m/^\+/) { - if ($value =~ m/^\+(${security_group_name_pattern})$/) { + if ($value =~ m/^\+(${ipset_name_pattern})$/) { &$add_error($name, "no such ipset '$1'") if !($cluster_conf->{ipset}->{$1} || ($fw_conf && $fw_conf->{ipset}->{$1})); } else { - &$add_error($name, "invalid security group name '$value'"); + &$add_error($name, "invalid ipset name '$value'"); } } elsif ($value =~ m/^${ip_alias_pattern}$/){ my $alias = lc($value); @@ -1348,7 +1364,7 @@ sub ruleset_generate_cmdstr { if ($source) { if ($source =~ m/^\+/) { - if ($source =~ m/^\+(${security_group_name_pattern})$/) { + if ($source =~ m/^\+(${ipset_name_pattern})$/) { my $name = $1; if ($fw_conf && $fw_conf->{ipset}->{$name}) { my $ipset_chain = compute_ipset_chain_name($fw_conf->{vmid}, $name); @@ -1377,7 +1393,7 @@ sub ruleset_generate_cmdstr { if ($dest) { if ($dest =~ m/^\+/) { - if ($dest =~ m/^\+(${security_group_name_pattern})$/) { + if ($dest =~ m/^\+(${ipset_name_pattern})$/) { my $name = $1; if ($fw_conf && $fw_conf->{ipset}->{$name}) { my $ipset_chain = compute_ipset_chain_name($fw_conf->{vmid}, $name); @@ -2100,6 +2116,21 @@ sub parse_clusterfw_option { return ($opt, $value); } +sub resolve_alias { + my ($clusterfw_conf, $fw_conf, $cidr) = @_; + + if ($cidr !~ m/^\d/) { + my $alias = lc($cidr); + my $e = $fw_conf->{aliases}->{$alias} if $fw_conf; + $e = $clusterfw_conf->{aliases}->{$alias} if !$e && $clusterfw_conf; + return $e->{cidr} if $e; + + die "no such alias '$cidr'\n"; + } + + return $cidr; +} + sub parse_alias { my ($line) = @_; @@ -2121,7 +2152,7 @@ sub parse_alias { return undef; } -sub generic_fw_rules_parser { +sub generic_fw_config_parser { my ($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env) = @_; my $section; @@ -2129,6 +2160,8 @@ sub generic_fw_rules_parser { my $res = $empty_conf; + my $ipset_option = get_standard_option('ipset-name'); + while (defined(my $line = <$fh>)) { next if $line =~ m/^#/; next if $line =~ m/^\s*$/; @@ -2150,6 +2183,16 @@ sub generic_fw_rules_parser { $section = 'groups'; $group = lc($1); my $comment = $2; + eval { + die "security group name too long\n" if length($group) > $max_group_name_length; + die "invalid security group name '$group'\n" if $group !~ m/^${security_group_name_pattern}$/; + }; + if (my $err = $@) { + ($section, $group, $comment) = undef; + warn "$prefix: $err"; + next; + } + $res->{$section}->{$group} = []; $res->{group_comments}->{$group} = decode('utf8', $comment) if $comment; @@ -2165,6 +2208,16 @@ sub generic_fw_rules_parser { $section = 'ipset'; $group = lc($1); my $comment = $2; + eval { + die "ipset name too long\n" if length($group) > $max_ipset_name_length; + die "invalid ipset name '$group'\n" if $group !~ m/^${ipset_name_pattern}$/; + }; + if (my $err = $@) { + ($section, $group, $comment) = undef; + warn "$prefix: $err"; + next; + } + $res->{$section}->{$group} = []; $res->{ipset_comments}->{$group} = decode('utf8', $comment) if $comment; @@ -2221,7 +2274,6 @@ sub generic_fw_rules_parser { if($cidr !~ m/^${ip_alias_pattern}$/) { $cidr =~ s|/32$||; - eval { pve_verify_ipv4_or_cidr($cidr); }; if (my $err = $@) { warn "$prefix: $cidr - $err"; @@ -2243,15 +2295,15 @@ sub generic_fw_rules_parser { return $res; } -sub parse_host_fw_rules { +sub parse_hostfw_config { my ($filename, $fh, $cluster_conf, $verbose) = @_; my $empty_conf = { rules => [], options => {}}; - return generic_fw_rules_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, 'host'); + return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, 'host'); } -sub parse_vm_fw_rules { +sub parse_vmfw_config { my ($filename, $fh, $cluster_conf, $rule_env, $verbose) = @_; my $empty_conf = { @@ -2262,10 +2314,10 @@ sub parse_vm_fw_rules { ipset_comments => {}, }; - return generic_fw_rules_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env); + return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env); } -sub parse_cluster_fw_rules { +sub parse_clusterfw_config { my ($filename, $fh, $verbose) = @_; my $section; @@ -2281,7 +2333,7 @@ sub parse_cluster_fw_rules { ipset_comments => {}, }; - return generic_fw_rules_parser($filename, $fh, $verbose, $empty_conf, $empty_conf, 'cluster'); + return generic_fw_config_parser($filename, $fh, $verbose, $empty_conf, $empty_conf, 'cluster'); } sub run_locked { @@ -2341,7 +2393,7 @@ sub load_vmfw_conf { my $filename = "$dir/$vmid.fw"; if (my $fh = IO::File->new($filename, O_RDONLY)) { - $vmfw_conf = parse_vm_fw_rules($filename, $fh, $cluster_conf, $rule_env, $verbose); + $vmfw_conf = parse_vmfw_config($filename, $fh, $cluster_conf, $rule_env, $verbose); $vmfw_conf->{vmid} = $vmid; } @@ -2570,20 +2622,11 @@ sub generate_ipset { # remove duplicates my $nethash = {}; foreach my $entry (@$options) { - my $cidr = $entry->{cidr}; - if ($cidr =~ m/^${ip_alias_pattern}$/) { - my $alias = lc($cidr); - my $e = $fw_conf->{aliases}->{$alias} if $fw_conf; - $e = $clusterfw_conf->{aliases}->{$alias} if !$e && $clusterfw_conf; - if ($e) { - $entry->{cidr} = $e->{cidr}; - $nethash->{$entry->{cidr}} = $entry; - } else { - warn "no such alias '$cidr'\n"; - } - } else { - $nethash->{$entry->{cidr}} = $entry; - } + eval { + my $cidr = resolve_alias($clusterfw_conf, $fw_conf, $entry->{cidr}); + $nethash->{$cidr} = { cidr => $cidr, nomatch => $entry->{nomatch} }; + }; + warn $@ if $@; } foreach my $cidr (sort keys %$nethash) { @@ -2617,7 +2660,7 @@ sub load_clusterfw_conf { my $cluster_conf = {}; if (my $fh = IO::File->new($filename, O_RDONLY)) { - $cluster_conf = parse_cluster_fw_rules($filename, $fh, $verbose); + $cluster_conf = parse_clusterfw_config($filename, $fh, $verbose); } return $cluster_conf; @@ -2666,7 +2709,7 @@ sub load_hostfw_conf { my $hostfw_conf = {}; if (my $fh = IO::File->new($filename, O_RDONLY)) { - $hostfw_conf = parse_host_fw_rules($filename, $fh, $cluster_conf, $verbose); + $hostfw_conf = parse_hostfw_config($filename, $fh, $cluster_conf, $verbose); } return $hostfw_conf; }