11 use PMG
::ClusterConfig
;
14 my ($nodename, $noerr) = @_;
16 my $cinfo = PVE
::INotify
::read_file
("cluster.conf");
18 foreach my $entry (values %{$cinfo->{ids
}}) {
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);
27 # fallback: try to get IP by other means
28 return PMG
::Utils
::lookup_node_ip
($nodename, $noerr);
34 $cinfo = PVE
::INotify
::read_file
("cluster.conf") if !$cinfo;
36 return $cinfo->{master
}->{name
} if defined($cinfo->{master
});
41 sub read_local_ssl_cert_fingerprint
{
42 my $cert_path = "/etc/pmg/pmg-api.pem";
46 my $bio = Net
::SSLeay
::BIO_new_file
($cert_path, 'r');
47 $cert = Net
::SSLeay
::PEM_read_bio_X509
($bio);
48 Net
::SSLeay
::BIO_free
($bio);
51 die "unable to read certificate '$cert_path' - $err\n";
54 if (!defined($cert)) {
55 die "unable to read certificate '$cert_path' - got empty value\n";
60 $fp = Net
::SSLeay
::X509_get_fingerprint
($cert, 'sha256');
63 die "unable to get fingerprint for '$cert_path' - $err\n";
66 if (!defined($fp) || $fp eq '') {
67 die "unable to get fingerprint for '$cert_path' - got empty value\n";
73 my $hostrsapubkey_fn = '/etc/ssh/ssh_host_rsa_key.pub';
74 my $rootrsakey_fn = '/root/.ssh/id_rsa';
75 my $rootrsapubkey_fn = '/root/.ssh/id_rsa.pub';
77 sub read_local_cluster_info
{
81 my $hostrsapubkey = PVE
::Tools
::file_read_firstline
($hostrsapubkey_fn);
82 $hostrsapubkey =~ s/^.*ssh-rsa\s+//i;
83 $hostrsapubkey =~ s/\s+root\@\S+\s*$//i;
85 die "unable to parse ${hostrsapubkey_fn}\n"
86 if $hostrsapubkey !~ m/^[A-Za-z0-9\.\/\
+]{200,}$/;
88 my $nodename = PVE
::INotify
::nodename
();
90 $res->{name
} = $nodename;
92 $res->{ip
} = PMG
::Utils
::lookup_node_ip
($nodename);
94 $res->{hostrsapubkey
} = $hostrsapubkey;
96 if (! -f
$rootrsapubkey_fn) {
97 unlink $rootrsakey_fn;
98 my $cmd = ['ssh-keygen', '-t', 'rsa', '-N', '', '-b', '2048',
99 '-f', $rootrsakey_fn];
100 PVE
::Tools
::run_command
($cmd);
103 my $rootrsapubkey = PVE
::Tools
::file_read_firstline
($rootrsapubkey_fn);
104 $rootrsapubkey =~ s/^.*ssh-rsa\s+//i;
105 $rootrsapubkey =~ s/\s+root\@\S+\s*$//i;
107 die "unable to parse ${rootrsapubkey_fn}\n"
108 if $rootrsapubkey !~ m/^[A-Za-z0-9\.\/\
+]{200,}$/;
110 $res->{rootrsapubkey
} = $rootrsapubkey;
112 $res->{fingerprint
} = read_local_ssl_cert_fingerprint
();
117 # X509 Certificate cache helper
119 my $cert_cache_nodes = {};
120 my $cert_cache_timestamp = time();
121 my $cert_cache_fingerprints = {};
123 sub update_cert_cache
{
125 $cert_cache_timestamp = time();
127 $cert_cache_fingerprints = {};
128 $cert_cache_nodes = {};
130 my $cinfo = PVE
::INotify
::read_file
("cluster.conf");
132 foreach my $entry (values %{$cinfo->{ids
}}) {
133 my $node = $entry->{name
};
134 my $fp = $entry->{fingerprint
};
136 $cert_cache_fingerprints->{$fp} = 1;
137 $cert_cache_nodes->{$node} = $fp;
142 # load and cache cert fingerprint once
143 sub initialize_cert_cache
{
147 if defined($node) && !defined($cert_cache_nodes->{$node});
150 sub check_cert_fingerprint
{
153 # clear cache every 30 minutes at least
154 update_cert_cache
() if time() - $cert_cache_timestamp >= 60*30;
156 # get fingerprint of server certificate
159 $fp = Net
::SSLeay
::X509_get_fingerprint
($cert, 'sha256');
161 return 0 if $@ || !defined($fp) || $fp eq ''; # error
164 for my $expected (keys %$cert_cache_fingerprints) {
165 return 1 if $fp eq $expected;
170 return 1 if $check->();
172 # clear cache and retry at most once every minute
173 if (time() - $cert_cache_timestamp >= 60) {
174 syslog
('info', "Could not verify remote node certificate '$fp' with list of pinned certificates, refreshing cache");