5
0
mirror of git://git.proxmox.com/git/pve-firewall.git synced 2025-01-04 09:17:58 +03:00

api: add vnet endpoints

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
This commit is contained in:
Stefan Hanreich 2024-11-19 13:22:49 +01:00 committed by Thomas Lamprecht
parent aa6aa578e8
commit 49d2d028e5
5 changed files with 264 additions and 0 deletions

View File

@ -47,4 +47,18 @@ sub get_allowed_vms {
];
}
sub check_vnet_access {
my ($vnetid, $privileges) = @_;
my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid, 1)
or die "invalid vnet specified";
my $zoneid = $vnet->{zone};
my $rpcenv = PVE::RPCEnvironment::get();
my $authuser = $rpcenv->get_user();
$rpcenv->check_any($authuser, "/sdn/zones/$zoneid/$vnetid", $privileges);
};
1;

View File

@ -10,6 +10,7 @@ LIB_SOURCES= \
Cluster.pm \
Host.pm \
VM.pm \
Vnet.pm \
Groups.pm
all:

View File

@ -19,6 +19,25 @@ my $api_properties = {
},
};
=head3 check_privileges_for_method($class, $method_name, $param)
If the permission checks from the register_method() call are not sufficient,
this function can be overriden for performing additional permission checks
before API methods are executed. If the permission check fails, this function
should die with an appropriate error message. The name of the method calling
this function is provided by C<$method_name> and the parameters of the API
method are provided by C<$param>
Default implementation is a no-op to preserve backwards compatibility with
existing subclasses, since this got added later on. It preserves existing
behavior without having to change every subclass.
=cut
sub check_privileges_for_method {
my ($class, $method_name, $param) = @_;
}
sub lock_config {
my ($class, $param, $code) = @_;
@ -94,6 +113,8 @@ sub register_get_rules {
code => sub {
my ($param) = @_;
$class->check_privileges_for_method('get_rules', $param);
my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
my ($list, $digest) = PVE::Firewall::copy_list_with_digest($rules);
@ -191,6 +212,8 @@ sub register_get_rule {
code => sub {
my ($param) = @_;
$class->check_privileges_for_method('get_rule', $param);
my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
my ($list, $digest) = PVE::Firewall::copy_list_with_digest($rules);
@ -231,6 +254,8 @@ sub register_create_rule {
code => sub {
my ($param) = @_;
$class->check_privileges_for_method('create_rule', $param);
$class->lock_config($param, sub {
my ($param) = @_;
@ -304,6 +329,8 @@ sub register_update_rule {
code => sub {
my ($param) = @_;
$class->check_privileges_for_method('update_rule', $param);
$class->lock_config($param, sub {
my ($param) = @_;
@ -382,6 +409,8 @@ sub register_delete_rule {
code => sub {
my ($param) = @_;
$class->check_privileges_for_method('delete_rule', $param);
$class->lock_config($param, sub {
my ($param) = @_;
@ -657,4 +686,59 @@ sub save_rules {
__PACKAGE__->register_handlers();
package PVE::API2::Firewall::VnetRules;
use strict;
use warnings;
use PVE::JSONSchema qw(get_standard_option);
use base qw(PVE::API2::Firewall::RulesBase);
__PACKAGE__->additional_parameters({
vnet => get_standard_option('pve-sdn-vnet-id'),
});
sub check_privileges_for_method {
my ($class, $method_name, $param) = @_;
if ($method_name eq 'get_rule' || $method_name eq 'get_rules') {
PVE::API2::Firewall::Helpers::check_vnet_access($param->{vnet}, ['SDN.Audit', 'SDN.Allocate']);
} elsif ($method_name =~ '(update|create|delete)_rule') {
PVE::API2::Firewall::Helpers::check_vnet_access($param->{vnet}, ['SDN.Allocate']);
} else {
die "unknown method: $method_name";
}
}
sub rule_env {
my ($class, $param) = @_;
return 'vnet';
}
sub lock_config {
my ($class, $param, $code) = @_;
PVE::Firewall::lock_vnetfw_conf($param->{vnet}, 10, $code, $param);
}
sub load_config {
my ($class, $param) = @_;
my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
my $fw_conf = PVE::Firewall::load_vnetfw_conf($cluster_conf, 'vnet', $param->{vnet});
my $rules = $fw_conf->{rules};
return ($cluster_conf, $fw_conf, $rules);
}
sub save_rules {
my ($class, $param, $fw_conf, $rules) = @_;
$fw_conf->{rules} = $rules;
PVE::Firewall::save_vnetfw_conf($param->{vnet}, $fw_conf);
}
__PACKAGE__->register_handlers();
1;

View File

@ -0,0 +1,155 @@
package PVE::API2::Firewall::Vnet;
use strict;
use warnings;
use Storable qw(dclone);
use PVE::Exception qw(raise_param_exc);
use PVE::JSONSchema qw(get_standard_option);
use PVE::RPCEnvironment;
use PVE::Firewall;
use PVE::API2::Firewall::Rules;
use PVE::API2::Firewall::Helpers;
use base qw(PVE::RESTHandler);
__PACKAGE__->register_method ({
subclass => "PVE::API2::Firewall::VnetRules",
path => 'rules',
});
__PACKAGE__->register_method({
name => 'index',
path => '',
method => 'GET',
description => "Directory index.",
parameters => {
additionalProperties => 0,
properties => {
vnet => get_standard_option('pve-sdn-vnet-id'),
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {},
},
links => [ { rel => 'child', href => "{name}" } ],
},
code => sub {
my ($param) = @_;
my $result = [
{ name => 'rules' },
{ name => 'options' },
];
return $result;
}});
my $option_properties = dclone($PVE::Firewall::vnet_option_properties);
my sub add_option_properties {
my ($properties) = @_;
foreach my $k (keys %$option_properties) {
$properties->{$k} = $option_properties->{$k};
}
return $properties;
};
__PACKAGE__->register_method({
name => 'get_options',
path => 'options',
method => 'GET',
description => "Get vnet firewall options.",
permissions => {
description => "Needs SDN.Audit or SDN.Allocate permissions on '/sdn/zones/<zone>/<vnet>'",
user => 'all',
},
parameters => {
additionalProperties => 0,
properties => {
vnet => get_standard_option('pve-sdn-vnet-id'),
},
},
returns => {
type => "object",
properties => $option_properties,
},
code => sub {
my ($param) = @_;
PVE::API2::Firewall::Helpers::check_vnet_access($param->{vnet}, ['SDN.Allocate', 'SDN.Audit']);
my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
my $vnetfw_conf = PVE::Firewall::load_vnetfw_conf($cluster_conf, 'vnet', $param->{vnet});
return PVE::Firewall::copy_opject_with_digest($vnetfw_conf->{options});
}});
__PACKAGE__->register_method({
name => 'set_options',
path => 'options',
method => 'PUT',
description => "Set Firewall options.",
protected => 1,
permissions => {
description => "Needs SDN.Allocate permissions on '/sdn/zones/<zone>/<vnet>'",
user => 'all',
},
parameters => {
additionalProperties => 0,
properties => add_option_properties({
vnet => get_standard_option('pve-sdn-vnet-id'),
delete => {
type => 'string', format => 'pve-configid-list',
description => "A list of settings you want to delete.",
optional => 1,
},
digest => get_standard_option('pve-config-digest'),
}),
},
returns => { type => "null" },
code => sub {
my ($param) = @_;
PVE::API2::Firewall::Helpers::check_vnet_access($param->{vnet}, ['SDN.Allocate']);
PVE::Firewall::lock_vnetfw_conf($param->{vnet}, 10, sub {
my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
my $vnetfw_conf = PVE::Firewall::load_vnetfw_conf($cluster_conf, 'vnet', $param->{vnet});
my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($vnetfw_conf->{options});
PVE::Tools::assert_if_modified($digest, $param->{digest});
if ($param->{delete}) {
for my $opt (PVE::Tools::split_list($param->{delete})) {
raise_param_exc({ delete => "no such option '$opt'" })
if !$option_properties->{$opt};
delete $vnetfw_conf->{options}->{$opt};
}
}
if (defined($param->{enable})) {
$param->{enable} = $param->{enable} ? 1 : 0;
}
for my $k (keys %$option_properties) {
next if !defined($param->{$k});
$vnetfw_conf->{options}->{$k} = $param->{$k};
}
PVE::Firewall::save_vnetfw_conf($param->{vnet}, $vnetfw_conf);
});
return undef;
}});
1;

View File

@ -1918,6 +1918,11 @@ sub rules_modify_permissions {
return {
check => ['perm', '/vms/{vmid}', [ 'VM.Config.Network' ]],
}
} elsif ($rule_env eq 'vnet') {
return {
description => "Needs SDN.Allocate permissions on '/sdn/zones/<zone>/<vnet>'",
user => 'all',
}
}
return undef;
@ -1938,6 +1943,11 @@ sub rules_audit_permissions {
return {
check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
}
} elsif ($rule_env eq 'vnet') {
return {
description => "Needs SDN.Audit or SDN.Allocate permissions on '/sdn/zones/<zone>/<vnet>'",
user => 'all',
}
}
return undef;