]> git.proxmox.com Git - pve-manager.git/blob - PVE/CertCache.pm
api: pool update: rename 'transfer' parameter to 'allow-move'
[pve-manager.git] / PVE / CertCache.pm
1 package PVE::CertCache;
2
3 use strict;
4 use warnings;
5
6 use Net::SSLeay;
7
8 use PVE::Cluster;
9 use PVE::SafeSyslog;
10
11 # X509 Certificate cache helper
12
13 my $cert_cache_nodes = {};
14 my $cert_cache_timestamp = time();
15 my $cert_cache_fingerprints = {};
16
17 sub update_cert_cache {
18 my ($update_node, $clear) = @_;
19
20 syslog('info', "Clearing outdated entries from certificate cache")
21 if $clear;
22
23 $cert_cache_timestamp = time() if !defined($update_node);
24
25 my $node_list = defined($update_node) ?
26 [ $update_node ] : [ keys %$cert_cache_nodes ];
27
28 foreach my $node (@$node_list) {
29 my $clear_old = sub {
30 if (my $old_fp = $cert_cache_nodes->{$node}) {
31 # distrust old fingerprint
32 delete $cert_cache_fingerprints->{$old_fp};
33 # ensure reload on next proxied request
34 delete $cert_cache_nodes->{$node};
35 }
36 };
37
38 my $fp = eval { PVE::Cluster::get_node_fingerprint($node) };
39 if (my $err = $@) {
40 warn "$err\n";
41 &$clear_old() if $clear;
42 next;
43 }
44
45 my $old_fp = $cert_cache_nodes->{$node};
46 $cert_cache_fingerprints->{$fp} = 1;
47 $cert_cache_nodes->{$node} = $fp;
48
49 if (defined($old_fp) && $fp ne $old_fp) {
50 delete $cert_cache_fingerprints->{$old_fp};
51 }
52 }
53 }
54
55 # load and cache cert fingerprint once
56 sub initialize_cert_cache {
57 my ($node) = @_;
58
59 update_cert_cache($node)
60 if defined($node) && !defined($cert_cache_nodes->{$node});
61 }
62
63 sub check_cert_fingerprint {
64 my ($cert) = @_;
65
66 # clear cache every 30 minutes at least
67 update_cert_cache(undef, 1) if time() - $cert_cache_timestamp >= 60*30;
68
69 # get fingerprint of server certificate
70 my $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
71 return 0 if !defined($fp) || $fp eq ''; # error
72
73 my $check = sub {
74 for my $expected (keys %$cert_cache_fingerprints) {
75 return 1 if $fp eq $expected;
76 }
77 return 0;
78 };
79
80 return 1 if &$check();
81
82 # clear cache and retry at most once every minute
83 if (time() - $cert_cache_timestamp >= 60) {
84 syslog ('info', "Could not verify remote node certificate '$fp' with list of pinned certificates, refreshing cache");
85 update_cert_cache();
86 return &$check();
87 }
88
89 return 0;
90 }
91
92 1;