$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) = @_;
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' => {
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\-\_]+';
-
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;
return $__local_network;
}
-my $max_ipset_name_length = 27;
+my $max_iptables_ipset_name_length = 27;
sub compute_ipset_chain_name {
my ($vmid, $ipset_name) = @_;
my $id = "$vmid-${ipset_name}";
- if ((length($id) + 6) > $max_ipset_name_length) {
+ if ((length($id) + 6) > $max_iptables_ipset_name_length) {
$id = PVE::Tools::fnv31a_hex($id);
}
sub parse_address_list {
my ($str) = @_;
- return if $str =~ m/^(\+)(\S+)$/; # ipset ref
- return if $str =~ m/^${ip_alias_pattern}$/;
+ if ($str =~ m/^(\+)(\S+)$/) { # ipset ref
+ die "ipset name too long\n" if length($str) > ($max_ipset_name_length + 1);
+ return;
+ }
+
+ if ($str =~ m/^${ip_alias_pattern}$/) {
+ die "alias name too long\n" if length($str) > $max_alias_name_length;
+ return;
+ }
my $count = 0;
my $iprange = 0;
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);
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);
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);
- push @cmd, "-m set --match-set ${ipset_chain} src";
+ push @cmd, "-m set --match-set ${ipset_chain} dst";
} elsif ($cluster_conf && $cluster_conf->{ipset}->{$name}) {
my $ipset_chain = compute_ipset_chain_name(0, $name);
push @cmd, "-m set --match-set ${ipset_chain} dst";
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) = @_;
return undef;
}
-sub generic_fw_rules_parser {
+sub generic_fw_config_parser {
my ($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env) = @_;
my $section;
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*$/;
$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;
$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;
if($cidr !~ m/^${ip_alias_pattern}$/) {
$cidr =~ s|/32$||;
-
eval { pve_verify_ipv4_or_cidr($cidr); };
if (my $err = $@) {
warn "$prefix: $cidr - $err";
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 = {
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;
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 {
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;
}
# 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) {
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;
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;
}