From: Stefan Hanreich <s.hanre...@proxmox.com>

Add a new cluster-wide lock for SDN that prevents any changes to the
configuration if the generated lock-secret is not provided. It works
by generating and storing a secret in sdn/.lock which gets checked by
lock_sdn_config on every invocation. If the lock file exists, then the
lock secret has to be supplied in order to make changes to the SDN
configuration.

Lock using the domain lock (`PVE::Cluster::cfs_lock_domain`) and "sdn"
string.

This is mainly a preparation for PDM, where PDM can take the lock and
prevent concurrent modifications to the SDN configuration from other
sources, even across multiple API calls.

Co-authored-by: Gabriel Goller <g.gol...@proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanre...@proxmox.com>
---
 src/PVE/Network/SDN.pm | 53 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 49 insertions(+), 4 deletions(-)

diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm
index 0e7d1dfd239c..efee21543387 100644
--- a/src/PVE/Network/SDN.pm
+++ b/src/PVE/Network/SDN.pm
@@ -13,7 +13,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file 
cfs_lock_file);
 use PVE::INotify;
 use PVE::RESTEnvironment qw(log_warn);
 use PVE::RPCEnvironment;
-use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Tools qw(file_get_contents file_set_contents extract_param 
dir_glob_regex run_command);
 
 use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
@@ -48,6 +48,8 @@ my $write_running_cfg = sub {
 
 PVE::Cluster::cfs_register_file($running_cfg, $parse_running_cfg, 
$write_running_cfg);
 
+my $LOCK_SECRET_FILE = "/etc/pve/sdn/.lock";
+
 # improve me : move status code inside plugins ?
 
 sub ifquery_check {
@@ -197,14 +199,57 @@ sub commit_config {
     cfs_write_file($running_cfg, $cfg);
 }
 
+sub generate_lock_secret {
+    my $min = ord('!'); # first printable ascii
+
+    my $rand_bytes = Crypt::OpenSSL::Random::random_bytes(32);
+    die "failed to generate lock secret!\n" if !$rand_bytes;
+
+    my $str = join('', map { chr((ord($_) & 0x3F) + $min) } split('', 
$rand_bytes));
+    return $str;
+}
+
+sub create_global_lock {
+    my $secret = generate_lock_secret();
+    PVE::Tools::file_set_contents($LOCK_SECRET_FILE, $secret);
+    return $secret;
+}
+
+sub delete_global_lock {
+    unlink $LOCK_SECRET_FILE if -e $LOCK_SECRET_FILE;
+}
+
 sub lock_sdn_config {
-    my ($code, $errmsg) = @_;
+    my ($code, $errmsg, $lock_secret_user) = @_;
 
-    cfs_lock_file($running_cfg, undef, $code);
+    my $lock_wrapper = sub {
+        my $lock_secret = undef;
+        if (-e $LOCK_SECRET_FILE) {
+            $lock_secret = PVE::Tools::file_get_contents($LOCK_SECRET_FILE);
+        }
+
+        if (
+            defined($lock_secret)
+            && (!defined($lock_secret_user) || $lock_secret ne 
$lock_secret_user)
+        ) {
+            die "invalid lock secret provided!";
+        }
+
+        return $code->();
+    };
 
-    if (my $err = $@) {
+    return lock_sdn_domain($lock_wrapper, $errmsg);
+}
+
+sub lock_sdn_domain {
+    my ($code, $errmsg) = @_;
+
+    my $res = PVE::Cluster::cfs_lock_domain("sdn", undef, $code);
+    my $err = $@;
+    if ($err) {
         $errmsg ? die "$errmsg: $err" : die $err;
     }
+    return $res;
 }
 
 sub get_local_vnets {
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to