]> git.proxmox.com Git - pmg-api.git/blame - PMG/Cluster.pm
use 'die' instead of 'carp'
[pmg-api.git] / PMG / Cluster.pm
CommitLineData
0854fb22
DM
1package PMG::Cluster;
2
3use strict;
4use warnings;
45e68618 5use Data::Dumper;
0854fb22 6use Socket;
45e68618 7
0854fb22
DM
8use PVE::Tools;
9use PVE::INotify;
10
9f67f5b3 11use PMG::ClusterConfig;
0854fb22 12
8737f93a
DM
13sub remote_node_ip {
14 my ($nodename, $noerr) = @_;
15
16 my $cinfo = PVE::INotify::read_file("cluster.conf");
17
45e68618 18 foreach my $entry (values %{$cinfo->{ids}}) {
8737f93a
DM
19 if ($entry->{name} eq $nodename) {
20 my $ip = $entry->{ip};
21 return $ip if !wantarray;
22 my $family = PVE::Tools::get_host_address_family($ip);
23 return ($ip, $family);
24 }
25 }
26
27 # fallback: try to get IP by other means
9f67f5b3 28 return PMG::Utils::lookup_node_ip($nodename, $noerr);
8737f93a
DM
29}
30
d2e43f9e
DM
31sub get_master_node {
32 my ($cinfo) = @_;
33
9f67f5b3 34 $cinfo = PVE::INotify::read_file("cluster.conf");
d2e43f9e
DM
35
36 return $cinfo->{master}->{name} if defined($cinfo->{master});
37
38 return 'localhost';
39}
40
0854fb22
DM
41# X509 Certificate cache helper
42
43my $cert_cache_nodes = {};
44my $cert_cache_timestamp = time();
45my $cert_cache_fingerprints = {};
46
47sub update_cert_cache {
48 my ($update_node, $clear) = @_;
49
50 syslog('info', "Clearing outdated entries from certificate cache")
51 if $clear;
52
53 $cert_cache_timestamp = time() if !defined($update_node);
54
55 my $node_list = defined($update_node) ?
56 [ $update_node ] : [ keys %$cert_cache_nodes ];
57
58 my $clear_node = sub {
59 my ($node) = @_;
60 if (my $old_fp = $cert_cache_nodes->{$node}) {
61 # distrust old fingerprint
62 delete $cert_cache_fingerprints->{$old_fp};
63 # ensure reload on next proxied request
64 delete $cert_cache_nodes->{$node};
65 }
66 };
67
68 my $nodename = PVE::INotify::nodename();
69
70 foreach my $node (@$node_list) {
71
72 if ($node ne $nodename) {
73 &$clear_node($node) if $clear;
74 next;
75 }
76
3278b571 77 my $cert_path = "/etc/pmg/pmg-api.pem";
0854fb22
DM
78
79 my $cert;
80 eval {
81 my $bio = Net::SSLeay::BIO_new_file($cert_path, 'r');
82 $cert = Net::SSLeay::PEM_read_bio_X509($bio);
83 Net::SSLeay::BIO_free($bio);
84 };
85 my $err = $@;
86 if ($err || !defined($cert)) {
87 &$clear_node($node) if $clear;
88 next;
89 }
90
91 my $fp;
92 eval {
93 $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
94 };
95 $err = $@;
96 if ($err || !defined($fp) || $fp eq '') {
97 &$clear_node($node) if $clear;
98 next;
99 }
100
101 my $old_fp = $cert_cache_nodes->{$node};
102 $cert_cache_fingerprints->{$fp} = 1;
103 $cert_cache_nodes->{$node} = $fp;
104
105 if (defined($old_fp) && $fp ne $old_fp) {
106 delete $cert_cache_fingerprints->{$old_fp};
107 }
108 }
109}
110
111# load and cache cert fingerprint once
112sub initialize_cert_cache {
113 my ($node) = @_;
114
115 update_cert_cache($node)
116 if defined($node) && !defined($cert_cache_nodes->{$node});
117}
118
119sub check_cert_fingerprint {
120 my ($cert) = @_;
121
122 # clear cache every 30 minutes at least
123 update_cert_cache(undef, 1) if time() - $cert_cache_timestamp >= 60*30;
124
125 # get fingerprint of server certificate
126 my $fp;
127 eval {
128 $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
129 };
130 return 0 if $@ || !defined($fp) || $fp eq ''; # error
131
132 my $check = sub {
133 for my $expected (keys %$cert_cache_fingerprints) {
134 return 1 if $fp eq $expected;
135 }
136 return 0;
137 };
138
139 return 1 if &$check();
140
141 # clear cache and retry at most once every minute
142 if (time() - $cert_cache_timestamp >= 60) {
143 syslog ('info', "Could not verify remote node certificate '$fp' with list of pinned certificates, refreshing cache");
144 update_cert_cache();
145 return &$check();
146 }
147
148 return 0;
149}
150
1511;