--- /dev/null
+package PMG::CLI::pmgcm;
+
+use strict;
+use warnings;
+use Data::Dumper;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::INotify;
+use PVE::CLIHandler;
+
+use PMG::DBTools;
+use PMG::Cluster;
+use PMG::ClusterConfig;
+use PMG::API2::ClusterConfig;
+
+use base qw(PVE::CLIHandler);
+
+my $format_nodelist = sub {
+ my $res = shift;
+
+ print "NAME(CID)--------------IPADDRESS----ROLE-STATE---------UPTIME---LOAD----MEM---DISK\n";
+ foreach my $ni (@$res) {
+ my $state = '?';
+
+ printf "%-20s %-15s %-6s %1s %15s %6s %5s%% %5s%%\n",
+ "$ni->{name}($ni->{cid})", $ni->{ip}, $ni->{type},
+ $state, '-', '-', '-', '-';
+ }
+};
+
+our $cmddef = {
+ nodes => [ 'PMG::API2::ClusterConfig', 'index', [], {}, $format_nodelist],
+ create => [ 'PMG::API2::ClusterConfig', 'create_master', []],
+};
+
+1;
sub get_master_node {
my ($cinfo) = @_;
- $cinfo = PVE::INotify::read_file("cluster.conf");
+ $cinfo = PVE::INotify::read_file("cluster.conf") if !$cinfo;
return $cinfo->{master}->{name} if defined($cinfo->{master});
return 'localhost';
}
-# X509 Certificate cache helper
+sub read_local_ssl_cert_fingerprint {
+ my $cert_path = "/etc/pmg/pmg-api.pem";
-my $cert_cache_nodes = {};
-my $cert_cache_timestamp = time();
-my $cert_cache_fingerprints = {};
+ my $cert;
+ eval {
+ my $bio = Net::SSLeay::BIO_new_file($cert_path, 'r');
+ $cert = Net::SSLeay::PEM_read_bio_X509($bio);
+ Net::SSLeay::BIO_free($bio);
+ };
+ if (my $err = $@) {
+ die "unable to read certificate '$cert_path' - $err\n";
+ }
-sub update_cert_cache {
- my ($update_node, $clear) = @_;
+ if (!defined($cert)) {
+ die "unable to read certificate '$cert_path' - got empty value\n";
+ }
+
+ my $fp;
+ eval {
+ $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
+ };
+ if (my $err = $@) {
+ die "unable to get fingerprint for '$cert_path' - $err\n";
+ }
- syslog('info', "Clearing outdated entries from certificate cache")
- if $clear;
+ if (!defined($fp) || $fp eq '') {
+ die "unable to get fingerprint for '$cert_path' - got empty value\n";
+ }
- $cert_cache_timestamp = time() if !defined($update_node);
+ return $fp;
+}
- my $node_list = defined($update_node) ?
- [ $update_node ] : [ keys %$cert_cache_nodes ];
+my $hostrsapubkey_fn = '/etc/ssh/ssh_host_rsa_key.pub';
+my $rootrsakey_fn = '/root/.ssh/id_rsa';
+my $rootrsapubkey_fn = '/root/.ssh/id_rsa.pub';
- my $clear_node = sub {
- my ($node) = @_;
- 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};
- }
- };
+sub read_local_cluster_info {
+
+ my $res = {};
+
+ my $hostrsapubkey = PVE::Tools::file_read_firstline($hostrsapubkey_fn);
+ $hostrsapubkey =~ s/^.*ssh-rsa\s+//i;
+ $hostrsapubkey =~ s/\s+root\@\S+\s*$//i;
+
+ die "unable to parse ${hostrsapubkey_fn}\n"
+ if $hostrsapubkey !~ m/^[A-Za-z0-9\.\/\+]{200,}$/;
my $nodename = PVE::INotify::nodename();
- foreach my $node (@$node_list) {
+ $res->{name} = $nodename;
- if ($node ne $nodename) {
- &$clear_node($node) if $clear;
- next;
- }
+ $res->{ip} = PMG::Utils::lookup_node_ip($nodename);
- my $cert_path = "/etc/pmg/pmg-api.pem";
-
- my $cert;
- eval {
- my $bio = Net::SSLeay::BIO_new_file($cert_path, 'r');
- $cert = Net::SSLeay::PEM_read_bio_X509($bio);
- Net::SSLeay::BIO_free($bio);
- };
- my $err = $@;
- if ($err || !defined($cert)) {
- &$clear_node($node) if $clear;
- next;
- }
+ $res->{hostrsapubkey} = $hostrsapubkey;
- my $fp;
- eval {
- $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
- };
- $err = $@;
- if ($err || !defined($fp) || $fp eq '') {
- &$clear_node($node) if $clear;
- next;
- }
+ if (! -f $rootrsapubkey_fn) {
+ unlink $rootrsakey_fn;
+ my $cmd = ['ssh-keygen', '-t', 'rsa', '-N', '', '-b', '2048',
+ '-f', $rootrsakey_fn];
+ PVE::Tools::run_command($cmd);
+ }
+
+ my $rootrsapubkey = PVE::Tools::file_read_firstline($rootrsapubkey_fn);
+ $rootrsapubkey =~ s/^.*ssh-rsa\s+//i;
+ $rootrsapubkey =~ s/\s+root\@\S+\s*$//i;
+
+ die "unable to parse ${rootrsapubkey_fn}\n"
+ if $rootrsapubkey !~ m/^[A-Za-z0-9\.\/\+]{200,}$/;
+
+ $res->{rootrsapubkey} = $rootrsapubkey;
+
+ $res->{fingerprint} = read_local_ssl_cert_fingerprint();
+
+ return $res;
+}
+
+# X509 Certificate cache helper
+
+my $cert_cache_nodes = {};
+my $cert_cache_timestamp = time();
+my $cert_cache_fingerprints = {};
- my $old_fp = $cert_cache_nodes->{$node};
- $cert_cache_fingerprints->{$fp} = 1;
- $cert_cache_nodes->{$node} = $fp;
+sub update_cert_cache {
- if (defined($old_fp) && $fp ne $old_fp) {
- delete $cert_cache_fingerprints->{$old_fp};
+ $cert_cache_timestamp = time();
+
+ $cert_cache_fingerprints = {};
+ $cert_cache_nodes = {};
+
+ my $cinfo = PVE::INotify::read_file("cluster.conf");
+
+ foreach my $entry (values %{$cinfo->{ids}}) {
+ my $node = $entry->{name};
+ my $fp = $entry->{fingerprint};
+ if ($node && $fp) {
+ $cert_cache_fingerprints->{$fp} = 1;
+ $cert_cache_nodes->{$node} = $fp;
}
}
}
sub initialize_cert_cache {
my ($node) = @_;
- update_cert_cache($node)
+ update_cert_cache()
if defined($node) && !defined($cert_cache_nodes->{$node});
}
my ($cert) = @_;
# clear cache every 30 minutes at least
- update_cert_cache(undef, 1) if time() - $cert_cache_timestamp >= 60*30;
+ update_cert_cache() if time() - $cert_cache_timestamp >= 60*30;
# get fingerprint of server certificate
my $fp;
return 0;
};
- return 1 if &$check();
+ 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 $check->();
}
return 0;