pve-manager/PVE/CertCache.pm
Fabian Grünbichler 869df6202f takeover CertCache from pve-cluster
same code, same semantics, different file/module

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2019-11-18 12:25:35 +01:00

93 lines
2.1 KiB
Perl

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;