+ return wantarray ? ($res->{key}, $res->{mtime}) : $res->{key};
+}
+
+sub check_authkey {
+ my ($quiet) = @_;
+
+ # skip check if non-quorate, as rotation is not possible anyway
+ return 1 if !PVE::Cluster::check_cfs_quorum(1);
+
+ my ($pub_key, $mtime) = get_pubkey();
+ if (!$pub_key) {
+ warn "auth key pair missing, generating new one..\n" if !$quiet;
+ return 0;
+ } else {
+ if (time() - $mtime >= $authkey_lifetime) {
+ warn "auth key pair too old, rotating..\n" if !$quiet;;
+ return 0;
+ } else {
+ warn "auth key new enough, skipping rotation\n" if !$quiet;;
+ return 1;
+ }
+ }
+}
+
+sub rotate_authkey {
+ return if $authkey_lifetime == 0;
+
+ PVE::Cluster::cfs_lock_authkey(undef, sub {
+ # re-check with lock to avoid double rotation in clusters
+ return if check_authkey();
+
+ my $old = get_pubkey();
+
+ if ($old) {
+ eval {
+ my $pem = $old->get_public_key_x509_string();
+ PVE::Tools::file_set_contents($pve_auth_key_files->{pubold}, $pem);
+ };
+ die "Failed to store old auth key: $@\n" if $@;
+ }
+
+ my $new = Crypt::OpenSSL::RSA->generate_key(2048);
+ eval {
+ my $pem = $new->get_public_key_x509_string();
+ PVE::Tools::file_set_contents($pve_auth_key_files->{pub}, $pem);
+ };
+ if ($@) {
+ if ($old) {
+ warn "Failed to store new auth key - $@\n";
+ warn "Reverting to previous auth key\n";
+ eval {
+ my $pem = $old->get_public_key_x509_string();
+ PVE::Tools::file_set_contents($pve_auth_key_files->{pub}, $pem);
+ };
+ die "Failed to restore old auth key: $@\n" if $@;
+ } else {
+ die "Failed to store new auth key - $@\n";
+ }
+ }
+
+ eval {
+ my $pem = $new->get_private_key_string();
+ PVE::Tools::file_set_contents($pve_auth_key_files->{priv}, $pem);
+ };
+ if ($@) {
+ warn "Failed to store new auth key - $@\n";
+ warn "Deleting auth key to force regeneration\n";
+ unlink $pve_auth_key_files->{pub};
+ unlink $pve_auth_key_files->{priv};
+ }
+ });
+ die $@ if $@;