From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 23A6E1FF166 for ; Wed, 11 Sep 2024 11:32:21 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 319BEA234; Wed, 11 Sep 2024 11:31:32 +0200 (CEST) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Date: Wed, 11 Sep 2024 11:31:11 +0200 Message-Id: <20240911093116.112960-11-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240911093116.112960-1-s.hanreich@proxmox.com> References: <20240911093116.112960-1-s.hanreich@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.264 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pve-devel] [PATCH pve-firewall 10/15] api: add vnet endpoints X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" Signed-off-by: Stefan Hanreich --- src/PVE/API2/Firewall/Makefile | 1 + src/PVE/API2/Firewall/Rules.pm | 71 ++++++++++++++ src/PVE/API2/Firewall/Vnet.pm | 166 +++++++++++++++++++++++++++++++++ src/PVE/Firewall.pm | 10 ++ 4 files changed, 248 insertions(+) create mode 100644 src/PVE/API2/Firewall/Vnet.pm diff --git a/src/PVE/API2/Firewall/Makefile b/src/PVE/API2/Firewall/Makefile index e916755..325c4d3 100644 --- a/src/PVE/API2/Firewall/Makefile +++ b/src/PVE/API2/Firewall/Makefile @@ -9,6 +9,7 @@ LIB_SOURCES= \ Cluster.pm \ Host.pm \ VM.pm \ + Vnet.pm \ Groups.pm all: diff --git a/src/PVE/API2/Firewall/Rules.pm b/src/PVE/API2/Firewall/Rules.pm index ebb51af..906c6d7 100644 --- a/src/PVE/API2/Firewall/Rules.pm +++ b/src/PVE/API2/Firewall/Rules.pm @@ -18,6 +18,10 @@ my $api_properties = { }, }; +sub before_method { + my ($class, $method_name, $param) = @_; +} + sub lock_config { my ($class, $param, $code) = @_; @@ -93,6 +97,7 @@ sub register_get_rules { }, code => sub { my ($param) = @_; + $class->before_method('get_rules', $param); my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param); @@ -191,6 +196,7 @@ sub register_get_rule { }, code => sub { my ($param) = @_; + $class->before_method('get_rule', $param); my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param); @@ -231,6 +237,7 @@ sub register_create_rule { returns => { type => "null" }, code => sub { my ($param) = @_; + $class->before_method('create_rule', $param); $class->lock_config($param, sub { my ($param) = @_; @@ -292,6 +299,7 @@ sub register_update_rule { returns => { type => "null" }, code => sub { my ($param) = @_; + $class->before_method('update_rule', $param); $class->lock_config($param, sub { my ($param) = @_; @@ -358,6 +366,7 @@ sub register_delete_rule { returns => { type => "null" }, code => sub { my ($param) = @_; + $class->before_method('delete_rule', $param); $class->lock_config($param, sub { my ($param) = @_; @@ -636,4 +645,66 @@ 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 before_method { + my ($class, $method_name, $param) = @_; + + my $privs; + + if ($method_name eq 'get_rule' || $method_name eq 'get_rules') { + $privs = ['SDN.Audit', 'SDN.Allocate']; + } elsif ($method_name eq 'update_rule' + || $method_name eq 'create_rule' + || $method_name eq 'delete_rule' + ) { + $privs = ['SDN.Allocate']; + } else { + die "unknown method: $method_name"; + } + + PVE::API2::Firewall::Vnet::check_vnet_access($param->{vnet}, $privs); +} + +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(undef, 1); + 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; diff --git a/src/PVE/API2/Firewall/Vnet.pm b/src/PVE/API2/Firewall/Vnet.pm new file mode 100644 index 0000000..bdfa163 --- /dev/null +++ b/src/PVE/API2/Firewall/Vnet.pm @@ -0,0 +1,166 @@ +package PVE::API2::Firewall::Vnet; + +use strict; +use warnings; + +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 base qw(PVE::RESTHandler); + +sub check_vnet_access { + my ($vnetid, $privileges) = @_; + + my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid, 1); + die "invalid vnet specified" if !$vnet; + + my $zoneid = $vnet->{zone}; + + my $rpcenv = PVE::RPCEnvironment::get(); + my $authuser = $rpcenv->get_user(); + + $rpcenv->check_any($authuser, "/sdn/zones/$zoneid/$vnetid", $privileges); +}; + +__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 = $PVE::Firewall::vnet_option_properties; + +my $add_option_properties = sub { + 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//'", + user => 'all', + }, + parameters => { + additionalProperties => 0, + properties => { + vnet => get_standard_option('pve-sdn-vnet-id'), + }, + }, + returns => { + type => "object", + properties => $option_properties, + }, + code => sub { + my ($param) = @_; + + 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//'", + 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) = @_; + + 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}) { + foreach 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; + } + + foreach 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; diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index d2b3dbb..4117d59 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -1906,6 +1906,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//'", + user => 'all', + } } return undef; @@ -1926,6 +1931,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//'", + user => 'all', + } } return undef; -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel