and just keep node to certificate path helper in PVE::Cluster. Signed-off-by: Fabian Grünbichler <f.gruenbich...@proxmox.com> ---
Notes: in peparation of moving it to pve-manager altogether in the next patch requires versioned breaks on pve-manager (HTTPServer.pm) data/PVE/Cluster/Makefile | 2 +- data/PVE/Cluster.pm | 82 ------------------------------- data/PVE/Cluster/CertCache.pm | 92 +++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 83 deletions(-) create mode 100644 data/PVE/Cluster/CertCache.pm diff --git a/data/PVE/Cluster/Makefile b/data/PVE/Cluster/Makefile index 0b25cfc..ec267a6 100644 --- a/data/PVE/Cluster/Makefile +++ b/data/PVE/Cluster/Makefile @@ -1,6 +1,6 @@ PVEDIR=${DESTDIR}/usr/share/perl5/PVE -SOURCES=IPCConst.pm Setup.pm +SOURCES=IPCConst.pm Setup.pm CertCache.pm .PHONY: install install: ${SOURCES} diff --git a/data/PVE/Cluster.pm b/data/PVE/Cluster.pm index b5157ba..5b017e4 100644 --- a/data/PVE/Cluster.pm +++ b/data/PVE/Cluster.pm @@ -978,58 +978,6 @@ cfs_register_file('datacenter.cfg', \&parse_datacenter_config, \&write_datacenter_config); -# 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 { 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 get_node_fingerprint { my ($node) = @_; @@ -1041,36 +989,6 @@ sub get_node_fingerprint { return PVE::Certificate::get_certificate_fingerprint($cert_path); } - -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; -} - # bash completion helpers sub complete_next_vmid { diff --git a/data/PVE/Cluster/CertCache.pm b/data/PVE/Cluster/CertCache.pm new file mode 100644 index 0000000..98d0ed3 --- /dev/null +++ b/data/PVE/Cluster/CertCache.pm @@ -0,0 +1,92 @@ +package PVE::Cluster::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; -- 2.20.1 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel