--- /dev/null
+package PVE::API2::Firewall::AliasesBase;
+
+use strict;
+use warnings;
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::JSONSchema qw(get_standard_option);
+
+use PVE::Firewall;
+
+use base qw(PVE::RESTHandler);
+
+my $api_properties = {
+ cidr => {
+ description => "Network/IP specification in CIDR format.",
+ type => 'string', format => 'IPv4orCIDR',
+ },
+ name => get_standard_option('pve-fw-alias'),
+ comment => {
+ type => 'string',
+ optional => 1,
+ }
+};
+
+sub load_config {
+ my ($class, $param) = @_;
+
+ die "implement this in subclass";
+
+ #return ($fw_conf, $rules);
+}
+
+sub save_aliases {
+ my ($class, $param, $fw_conf, $aliases) = @_;
+
+ die "implement this in subclass";
+}
+
+my $additional_param_hash = {};
+
+sub additional_parameters {
+ my ($class, $new_value) = @_;
+
+ if (defined($new_value)) {
+ $additional_param_hash->{$class} = $new_value;
+ }
+
+ # return a copy
+ my $copy = {};
+ my $org = $additional_param_hash->{$class} || {};
+ foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
+ return $copy;
+}
+
+my $aliases_to_list = sub {
+ my ($aliases) = @_;
+
+ my $list = [];
+ foreach my $k (sort keys %$aliases) {
+ push @$list, $aliases->{$k};
+ }
+ return $list;
+};
+
+sub register_get_aliases {
+ my ($class) = @_;
+
+ my $properties = $class->additional_parameters();
+
+ $class->register_method({
+ name => 'get_aliases',
+ path => '',
+ method => 'GET',
+ description => "List aliases",
+ parameters => {
+ additionalProperties => 0,
+ properties => $properties,
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => {
+ name => { type => 'string' },
+ cidr => { type => 'string' },
+ comment => {
+ type => 'string',
+ optional => 1,
+ },
+ digest => get_standard_option('pve-config-digest', { optional => 0} ),
+ },
+ },
+ links => [ { rel => 'child', href => "{name}" } ],
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my ($fw_conf, $aliases) = $class->load_config($param);
+
+ my $list = &$aliases_to_list($aliases);
+
+ return PVE::Firewall::copy_list_with_digest($list);
+ }});
+}
+
+sub register_create_alias {
+ my ($class) = @_;
+
+ my $properties = $class->additional_parameters();
+
+ $properties->{name} = $api_properties->{name};
+ $properties->{cidr} = $api_properties->{cidr};
+ $properties->{comment} = $api_properties->{comment};
+
+ $class->register_method({
+ name => 'create_alias',
+ path => '',
+ method => 'POST',
+ description => "Create IP or Network Alias.",
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => $properties,
+ },
+ returns => { type => "null" },
+ code => sub {
+ my ($param) = @_;
+
+ my ($fw_conf, $aliases) = $class->load_config($param);
+
+ my $name = lc($param->{name});
+
+ raise_param_exc({ name => "alias '$param->{name}' already exists" })
+ if defined($aliases->{$name});
+
+ my $data = { name => $param->{name}, cidr => $param->{cidr} };
+ $data->{comment} = $param->{comment} if $param->{comment};
+
+ $aliases->{$name} = $data;
+
+ $class->save_aliases($param, $fw_conf, $aliases);
+
+ return undef;
+ }});
+}
+
+sub register_read_alias {
+ my ($class) = @_;
+
+ my $properties = $class->additional_parameters();
+
+ $properties->{name} = $api_properties->{name};
+ $properties->{cidr} = $api_properties->{cidr};
+
+ $class->register_method({
+ name => 'read_alias',
+ path => '{name}',
+ method => 'GET',
+ description => "Read alias.",
+ parameters => {
+ additionalProperties => 0,
+ properties => $properties,
+ },
+ returns => { type => "object" },
+ code => sub {
+ my ($param) = @_;
+
+ my ($fw_conf, $aliases) = $class->load_config($param);
+
+ my $name = lc($param->{name});
+
+ raise_param_exc({ name => "no such alias" })
+ if !defined($aliases->{$name});
+
+ return $aliases->{$name};
+ }});
+}
+
+sub register_update_alias {
+ my ($class) = @_;
+
+ my $properties = $class->additional_parameters();
+
+ $properties->{name} = $api_properties->{name};
+ $properties->{cidr} = $api_properties->{cidr};
+ $properties->{comment} = $api_properties->{comment};
+ $properties->{digest} = get_standard_option('pve-config-digest');
+
+ $class->register_method({
+ name => 'update_alias',
+ path => '{name}',
+ method => 'PUT',
+ description => "Update IP or Network alias.",
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => $properties,
+ },
+ returns => { type => "null" },
+ code => sub {
+ my ($param) = @_;
+
+ my ($fw_conf, $aliases) = $class->load_config($param);
+
+ my $list = &$aliases_to_list($aliases);
+
+ my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
+
+ PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+ my $name = lc($param->{name});
+
+ raise_param_exc({ name => "no such alias" }) if !$aliases->{$name};
+
+ my $data = { name => $param->{name}, cidr => $param->{cidr} };
+ $data->{comment} = $param->{comment} if $param->{comment};
+
+ $aliases->{$name} = $data;
+
+ $class->save_aliases($param, $fw_conf, $aliases);
+ }});
+}
+
+sub register_delete_alias {
+ my ($class) = @_;
+
+ my $properties = $class->additional_parameters();
+
+ $properties->{name} = $api_properties->{name};
+ $properties->{cidr} = $api_properties->{cidr};
+ $properties->{digest} = get_standard_option('pve-config-digest');
+
+ $class->register_method({
+ name => 'remove_alias',
+ path => '{name}',
+ method => 'DELETE',
+ description => "Remove IP or Network alias.",
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => $properties,
+ },
+ returns => { type => "null" },
+ code => sub {
+ my ($param) = @_;
+
+ my ($fw_conf, $aliases) = $class->load_config($param);
+
+ my $list = &$aliases_to_list($aliases);
+ my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
+ PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+ my $name = lc($param->{name});
+ delete $aliases->{$name};
+
+ $class->save_aliases($param, $fw_conf, $aliases);
+
+ return undef;
+ }});
+}
+
+sub register_handlers {
+ my ($class) = @_;
+
+ $class->register_get_aliases();
+ $class->register_create_alias();
+ $class->register_read_alias();
+ $class->register_update_alias();
+ $class->register_delete_alias();
+}
+
+package PVE::API2::Firewall::ClusterAliases;
+
+use strict;
+use warnings;
+
+use base qw(PVE::API2::Firewall::AliasesBase);
+
+sub load_config {
+ my ($class, $param) = @_;
+
+ my $fw_conf = PVE::Firewall::load_clusterfw_conf();
+ my $aliases = $fw_conf->{aliases};
+
+ return ($fw_conf, $aliases);
+}
+
+sub save_aliases {
+ my ($class, $param, $fw_conf, $aliases) = @_;
+
+ $fw_conf->{aliases} = $aliases;
+ PVE::Firewall::save_clusterfw_conf($fw_conf);
+}
+
+__PACKAGE__->register_handlers();
+
+1;
maxLength => 20,
});
+PVE::JSONSchema::register_standard_option('pve-fw-alias', {
+ description => "Alias name.",
+ type => 'string',
+ pattern => '[A-Za-z][A-Za-z0-9\-\_]+',
+ minLength => 2,
+ maxLength => 20,
+});
+
PVE::JSONSchema::register_standard_option('pve-fw-loglevel' => {
description => "Log level.",
type => 'string',
die "invalid security group name '$source'\n";
}
} elsif ($source =~ m/^${ip_alias_pattern}$/){
- die "no such alias $source\n" if !$cluster_conf->{aliases}->{$source};
- push @cmd, "-s $cluster_conf->{aliases}->{$source}";
-
+ my $alias = lc($source);
+ my $e = $cluster_conf->{aliases}->{$alias};
+ die "no such alias $source\n" if !$e;
+ push @cmd, "-s $e->{cidr}";
} elsif ($source =~ m/\-/){
push @cmd, "-m iprange --src-range $source";
die "invalid security group name '$dest'\n";
}
} elsif ($dest =~ m/^${ip_alias_pattern}$/){
- die "no such alias $dest" if !$cluster_conf->{aliases}->{$dest};
- push @cmd, "-d $cluster_conf->{aliases}->{$dest}";
-
+ my $alias = lc($source);
+ my $e = $cluster_conf->{aliases}->{$alias};
+ die "no such alias $dest" if !$e;
+ push @cmd, "-d $e->{cidr}";
} elsif ($dest =~ m/^(\d+)\.(\d+).(\d+).(\d+)\-(\d+)\.(\d+).(\d+).(\d+)$/){
push @cmd, "-m iprange --dst-range $dest";
sub parse_clusterfw_alias {
my ($line) = @_;
- my ($opt, $value);
+ # we can add single line comments to the end of the line
+ my $comment = decode('utf8', $1) if $line =~ s/\s*#\s*(.*?)\s*$//;
+
if ($line =~ m/^(\S+)\s(\S+)$/) {
- $opt = lc($1);
- if($2){
- $2 =~ s|/32$||;
- pve_verify_ipv4_or_cidr($2) if $2;
- $value = $2;
- }
- } else {
- chomp $line;
- die "can't parse option '$line'\n";
+ my ($name, $cidr) = ($1, $2);
+ $cidr =~ s|/32$||;
+ pve_verify_ipv4_or_cidr($cidr);
+ my $data = {
+ name => $name,
+ cidr => $cidr,
+ };
+ $data->{comment} = $comment if $comment;
+ return $data;
}
- return ($opt, $value);
+ return undef;
}
sub parse_vm_fw_rules {
my $res = {
rules => [],
options => {},
+ aliases => {},
groups => {},
group_comments => {},
ipset => {} ,
warn "$prefix: $@" if $@;
} elsif ($section eq 'aliases') {
eval {
- my ($opt, $value) = parse_clusterfw_alias($line);
- $res->{aliases}->{$opt} = $value;
+ my $data = parse_clusterfw_alias($line);
+ $res->{aliases}->{lc($data->{name})} = $data;
};
warn "$prefix: $@" if $@;
} elsif ($section eq 'rules') {
$raw .= "[ALIASES]\n\n";
foreach my $k (keys %$aliases) {
- $raw .= "$k $aliases->{$k}\n";
+ my $e = $aliases->{$k};
+ $raw .= "$e->{name} $e->{cidr}";
+ $raw .= " # " . encode('utf8', $e->{comment})
+ if $e->{comment} && $e->{comment} !~ m/^\s*$/;
+ $raw .= "\n";
}
$raw .= "\n";
foreach my $entry (@$options) {
my $cidr = $entry->{cidr};
if ($cidr =~ m/^${ip_alias_pattern}$/) {
- if ($aliases->{$cidr}) {
- $entry->{cidr} = $aliases->{$cidr};
+ my $alias = lc($cidr);
+ if ($aliases->{$alias}) {
+ $entry->{cidr} = $aliases->{$alias}->{cidr};
} else {
- warn "no such alias '$cidr'\n" if !$aliases->{$cidr};
+ warn "no such alias '$cidr'\n" if !$aliases->{$alias};
}
}
$nethash->{$entry->{cidr}} = $entry;