]> git.proxmox.com Git - pve-manager.git/commitdiff
takeover CertCache from pve-cluster
authorFabian Grünbichler <f.gruenbichler@proxmox.com>
Mon, 11 Nov 2019 10:28:33 +0000 (11:28 +0100)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Mon, 18 Nov 2019 11:25:35 +0000 (12:25 +0100)
same code, same semantics, different file/module

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
PVE/CertCache.pm [new file with mode: 0644]
PVE/HTTPServer.pm
PVE/Makefile

diff --git a/PVE/CertCache.pm b/PVE/CertCache.pm
new file mode 100644 (file)
index 0000000..4eadab2
--- /dev/null
@@ -0,0 +1,92 @@
+package PVE::CertCache;
+
+use strict;
+use warnings;
+
+use Net::SSLeay;
+
+use PVE::Cluster;
+use PVE::SafeSyslog;
+
+# X509 Certificate cache helper
+
+my $cert_cache_nodes = {};
+my $cert_cache_timestamp = time();
+my $cert_cache_fingerprints = {};
+
+sub update_cert_cache {
+    my ($update_node, $clear) = @_;
+
+    syslog('info', "Clearing outdated entries from certificate cache")
+       if $clear;
+
+    $cert_cache_timestamp = time() if !defined($update_node);
+
+    my $node_list = defined($update_node) ?
+       [ $update_node ] : [ keys %$cert_cache_nodes ];
+
+    foreach my $node (@$node_list) {
+       my $clear_old = sub {
+           if (my $old_fp = $cert_cache_nodes->{$node}) {
+               # distrust old fingerprint
+               delete $cert_cache_fingerprints->{$old_fp};
+               # ensure reload on next proxied request
+               delete $cert_cache_nodes->{$node};
+           }
+       };
+
+       my $fp = eval { PVE::Cluster::get_node_fingerprint($node) };
+       if (my $err = $@) {
+           warn "$err\n";
+           &$clear_old() if $clear;
+           next;
+       }
+
+       my $old_fp = $cert_cache_nodes->{$node};
+       $cert_cache_fingerprints->{$fp} = 1;
+       $cert_cache_nodes->{$node} = $fp;
+
+       if (defined($old_fp) && $fp ne $old_fp) {
+           delete $cert_cache_fingerprints->{$old_fp};
+       }
+    }
+}
+
+# load and cache cert fingerprint once
+sub initialize_cert_cache {
+    my ($node) = @_;
+
+    update_cert_cache($node)
+       if defined($node) && !defined($cert_cache_nodes->{$node});
+}
+
+sub check_cert_fingerprint {
+    my ($cert) = @_;
+
+    # clear cache every 30 minutes at least
+    update_cert_cache(undef, 1) if time() - $cert_cache_timestamp >= 60*30;
+
+    # get fingerprint of server certificate
+    my $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
+    return 0 if !defined($fp) || $fp eq ''; # error
+
+    my $check = sub {
+       for my $expected (keys %$cert_cache_fingerprints) {
+           return 1 if $fp eq $expected;
+       }
+       return 0;
+    };
+
+    return 1 if &$check();
+
+    # clear cache and retry at most once every minute
+    if (time() - $cert_cache_timestamp >= 60) {
+       syslog ('info', "Could not verify remote node certificate '$fp' with list of pinned certificates, refreshing cache");
+       update_cert_cache();
+       return &$check();
+    }
+
+    return 0;
+}
+
+1;
index ce8957258d2b0806f9ef0fc9858a22a69409c502..e9572c71ac8f06226bf39c69a9462afdedf8746d 100755 (executable)
@@ -11,6 +11,7 @@ use PVE::Exception qw(raise_param_exc raise);
 
 use PVE::RPCEnvironment;
 use PVE::AccessControl;
+use PVE::CertCache;
 use PVE::Cluster;
 use PVE::API2Tools;
 
@@ -199,13 +200,13 @@ sub rest_handler {
 sub check_cert_fingerprint {
     my ($self, $cert) = @_;
 
-    return PVE::Cluster::check_cert_fingerprint($cert);
+    return PVE::CertCache::check_cert_fingerprint($cert);
 }
 
 sub initialize_cert_cache {
     my ($self, $node) = @_;
 
-    PVE::Cluster::initialize_cert_cache($node);
+    PVE::CertCache::initialize_cert_cache($node);
 }
 
 sub remote_node_ip {
index 71c91b9c7cafa566e38ea5f7a6aa4339936a2c86..0071fab1b4cb92ef27e6fcae1e04f5b7bb97abc9 100644 (file)
@@ -7,6 +7,7 @@ PERLSOURCE =                    \
        API2Tools.pm            \
        APLInfo.pm              \
        AutoBalloon.pm          \
+       CertCache.pm            \
        CertHelpers.pm          \
        ExtMetric.pm            \
        HTTPServer.pm           \