]> git.proxmox.com Git - pve-manager.git/commitdiff
fix #4759: ceph: configure ceph-crash.service and its key
authorMax Carrara <m.carrara@proxmox.com>
Tue, 2 Apr 2024 14:55:22 +0000 (16:55 +0200)
committerFabian Grünbichler <f.gruenbichler@proxmox.com>
Thu, 11 Apr 2024 08:42:11 +0000 (10:42 +0200)
Due to Ceph dropping privileges when running the 'ceph-crash' daemon
[0], it is necessary to allow the daemon to authenticate with its
cluster in a safe manner.

In order to avoid exposing sensitive keyrings or somehow escalating
its privileges again, 'ceph-crash' is therefore provided with its own
keyring in the '/etc/pve/ceph' directory. This directory, due to being
on 'pmxcfs', may be read by members of the 'www-data' group, which
'ceph-crash' is made part of [1].

Expected Configuration
----------------------

 1. A keyring file named '/etc/pve/ceph/ceph.client.crash.keyring'
    exists
 2. A section named 'client.crash' exists in '/etc/pve/ceph.conf'
 3. The 'client.crash' section has a key named 'keyring' which
    references the keyring file as '/etc/pve/ceph/$cluster.$name.keyring'
 4. The 'client.crash' section has *no* key named 'key'

New Clusters
------------

The keyring file is created and the conf file is updated after the first
monitor has been created (when calling `pveceph mon create`).

Existing Clusters
-----------------

A new helper script creates and configures the 'client.crash' keyring in
`postinst`, if:
 * Ceph is installed
 * Ceph is initialized ('/etc/pve/ceph.conf' and '/etc/pve/ceph' exist)
 * Connection to RADOS is successful

If the above conditions are met, the helper script ensures that the
existing configuration matches the expected configuration mentioned
above.

The configuration is not changed if it is already as expected.

The helper script may be called again manually if the `postinst` hook
fails. It is installed to '/usr/share/pve-manager/helpers/pve-init-ceph-crash'.

Existing `client.crash` Key
---------------------------

If a key named 'client.crash' already exists within the cluster, it is
reused and not regenerated.

[0]: https://github.com/ceph/ceph/pull/48713
[1]: https://git.proxmox.com/?p=ceph.git;a=commitdiff;h=f72c698a55905d93e9a0b7b95674616547deba8a

Signed-off-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Friedrich Weber <f.weber@proxmox.com>
PVE/API2/Ceph/MON.pm
PVE/Ceph/Tools.pm
bin/Makefile
bin/pve-init-ceph-crash [new file with mode: 0755]
debian/postinst

index 00b8f5030e628e24f7c87363de4146602aef0016..60c8d8dd58a1f7cb4e955413f588630d5ffbd745 100644 (file)
@@ -451,6 +451,14 @@ __PACKAGE__->register_method ({
                        )
                    };
                    warn "$@" if $@;
+
+                   print "Configuring keyring for ceph-crash.service\n";
+                   eval {
+                       PVE::Ceph::Tools::create_or_update_crash_keyring_file();
+                       $cfg->{'client.crash'}->{keyring} = '/etc/pve/ceph/$cluster.$name.keyring';
+                       cfs_write_file('ceph.conf', $cfg);
+                   };
+                   warn "Unable to configure keyring for ceph-crash.service: $@" if $@;
                }
 
                eval { PVE::Ceph::Services::ceph_service_cmd('enable', $monsection) };
index 735bb11645934109663dcecdf7da517052643eb6..9b97171e106e81d03ab196afe5194cf13f776e4a 100644 (file)
@@ -20,6 +20,7 @@ my $pve_ceph_cfgpath = "/etc/pve/$ccname.conf";
 my $ceph_cfgpath = "$ceph_cfgdir/$ccname.conf";
 my $pve_ceph_cfgdir = "/etc/pve/ceph";
 
+my $pve_ceph_crash_key_path = "$pve_ceph_cfgdir/$ccname.client.crash.keyring";
 my $pve_mon_key_path = "/etc/pve/priv/$ccname.mon.keyring";
 my $pve_ckeyring_path = "/etc/pve/priv/$ccname.client.admin.keyring";
 my $ckeyring_path = "/etc/ceph/ceph.client.admin.keyring";
@@ -45,6 +46,7 @@ my $config_values = {
 
 my $config_files = {
     pve_ceph_cfgpath => $pve_ceph_cfgpath,
+    pve_ceph_crash_key_path => $pve_ceph_crash_key_path,
     pve_mon_key_path => $pve_mon_key_path,
     pve_ckeyring_path => $pve_ckeyring_path,
     ceph_bootstrap_osd_keyring => $ceph_bootstrap_osd_keyring,
@@ -420,6 +422,39 @@ sub get_or_create_admin_keyring {
     return $pve_ckeyring_path;
 }
 
+# is also used in `pve-init-ceph-crash` helper
+sub create_or_update_crash_keyring_file {
+    my ($rados) = @_;
+
+    if (!defined($rados)) {
+       $rados = PVE::RADOS->new();
+    }
+
+    my $output = $rados->mon_command({
+       prefix => 'auth get-or-create',
+       entity => 'client.crash',
+       caps => [
+           mon => 'profile crash',
+           mgr => 'profile crash',
+       ],
+       format => 'plain',
+    });
+
+    if (-f $pve_ceph_crash_key_path) {
+       my $contents = PVE::Tools::file_get_contents($pve_ceph_crash_key_path);
+
+       if ($contents ne $output) {
+           PVE::Tools::file_set_contents($pve_ceph_crash_key_path, $output);
+           return 1;
+       }
+    } else {
+       PVE::Tools::file_set_contents($pve_ceph_crash_key_path, $output);
+       return 1;
+    }
+
+    return 0;
+}
+
 # get ceph-volume managed osds
 sub ceph_volume_list {
     my $result = {};
index 06d8148eee9994bc76805abfae3453618375ff82..b221e4b17e478cb7b991ea140293787323a39d78 100644 (file)
@@ -83,6 +83,7 @@ install: $(SCRIPTS) $(CLI_MANS) $(SERVICE_MANS) $(BASH_COMPLETIONS) $(ZSH_COMPLE
        install -m 0755 $(SCRIPTS) $(BINDIR)
        install -d $(USRSHARE)/helpers
        install -m 0755 pve-startall-delay $(USRSHARE)/helpers
+       install -m 0755 pve-init-ceph-crash $(USRSHARE)/helpers
        install -d $(MAN1DIR)
        install -m 0644 $(CLI_MANS) $(MAN1DIR)
        install -d $(MAN8DIR)
diff --git a/bin/pve-init-ceph-crash b/bin/pve-init-ceph-crash
new file mode 100755 (executable)
index 0000000..89170b2
--- /dev/null
@@ -0,0 +1,142 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use List::Util qw(first);
+
+use PVE::Ceph::Tools;
+use PVE::Cluster;
+use PVE::RADOS;
+use PVE::RPCEnvironment;
+
+my $ceph_cfg_file = 'ceph.conf';
+my $keyring_value = '/etc/pve/ceph/$cluster.$name.keyring';
+
+
+sub try_adapt_cfg {
+    my ($cfg) = @_;
+
+    my $entity = 'client.crash';
+    my $removed_key = 0;
+
+    print("Checking whether the configuration for '$entity' needs to be updated.\n");
+
+    my $add_keyring = sub {
+       print("Setting keyring path to '$keyring_value'.\n");
+       $cfg->{$entity}->{keyring} = $keyring_value;
+    };
+
+    if (!exists($cfg->{$entity})) {
+       print("Adding missing section for '$entity'.\n");
+       $add_keyring->();
+       return 1;
+    }
+
+    if (exists($cfg->{$entity}->{key})) {
+       print("Removing existing usage of key.\n");
+       delete($cfg->{$entity}->{key});
+       $removed_key = 1;
+    }
+
+    if (!exists($cfg->{$entity}->{keyring})) {
+       print("Keyring path is missing from configuration.\n");
+       $add_keyring->();
+       return 1;
+    }
+
+    my $current_keyring_value = $cfg->{$entity}->{keyring};
+    if ($current_keyring_value ne $keyring_value) {
+       print("Current keyring path differs from expected path.\n");
+       $add_keyring->();
+       return 1;
+    }
+
+    return $removed_key;
+}
+
+
+sub main {
+    # PVE::RADOS expects an active RPC Environment because it forks itself
+    # and may want to clean up after
+    my $rpcenv = PVE::RPCEnvironment->setup_default_cli_env();
+
+    if (!PVE::Ceph::Tools::check_ceph_installed('ceph_bin', 1)) {
+        print("Ceph is not installed. No action required.\n");
+        exit 0;
+    }
+
+    eval {
+        PVE::Ceph::Tools::check_ceph_inited();
+    };
+    if ($@) {
+        print("Ceph is not initialized. No action required.\n");
+        exit 0;
+    }
+
+    my $rados = eval { PVE::RADOS->new() };
+    my $ceph_crash_key_path = PVE::Ceph::Tools::get_config('pve_ceph_crash_key_path');
+
+    my $inner_err = '';
+
+    my $rval = PVE::Cluster::cfs_lock_file($ceph_cfg_file, undef, sub {
+       eval {
+           my $cfg = PVE::Cluster::cfs_read_file($ceph_cfg_file);
+
+           if (!defined($rados)) {
+               my $has_mon_host = defined($cfg->{global}) && defined($cfg->{global}->{mon_host});
+               if ($has_mon_host && $cfg->{global}->{mon_host} ne '') {
+                   die "Connection to RADOS failed even though a monitor is configured.\n" .
+                       "Please verify whether your configuration in '$ceph_cfg_file' is correct.\n"
+               }
+
+               print(
+                   "Connection to RADOS failed and no monitor is configured in '$ceph_cfg_file'.\n".
+                   "Assuming that things are fine. No action required.\n"
+               );
+               return;
+           }
+
+           my $updated_keyring = PVE::Ceph::Tools::create_or_update_crash_keyring_file($rados);
+
+           if ($updated_keyring) {
+               print("Keyring file '$ceph_crash_key_path' was updated.\n");
+           }
+
+           my $changed = try_adapt_cfg($cfg);
+
+           if ($changed) {
+               print("Committing updated configuration to '$ceph_cfg_file'.\n");
+               PVE::Cluster::cfs_write_file($ceph_cfg_file, $cfg);
+               print("Successfully updated configuration for 'ceph-crash.service'.\n");
+           } else {
+               print("Configuration in '$ceph_cfg_file' does not need to be updated.\n");
+           }
+       };
+       $inner_err = $@;
+
+       return 1;
+    });
+
+    # cfs_lock_file sets $@ explicitly to undef
+    my $err = $@ // '';
+
+    my $has_err = !defined($rval) || $inner_err || $err;
+
+    if ($has_err) {
+       $err =~ s/\n*$//;
+       $inner_err =~ s/\n*$//;
+
+       if (!defined($rval)) {
+           warn("Error while acquiring or releasing lock for '$ceph_cfg_file'.\n");
+           warn("Error: $err\n") if $err ne '';
+       }
+
+       warn("Failed to configure keyring for 'ceph-crash.service'.\nError: $inner_err\n")
+           if $inner_err ne '';
+
+       exit 1;
+    }
+}
+
+main();
index 58f2713f5a399f0e9f6a460e61387d1efd4cd7fb..2bd71459ce2903b461507a3aa6959c13b4db9539 100755 (executable)
@@ -82,10 +82,23 @@ EOF
 
 update_ceph_conf() {
     CEPH_CONF_DIR='/etc/pve/ceph'
+    UNIT='ceph-crash.service'
+
+    echo ""
 
     if test ! -d "${CEPH_CONF_DIR}"; then
         mkdir -p "${CEPH_CONF_DIR}"
     fi
+
+    # Don't fail in case user has "exotic" configuration where RADOS
+    # isn't available on all nodes for some reason
+    /usr/share/pve-manager/helpers/pve-init-ceph-crash || true
+
+    if systemctl -q is-enabled "$UNIT" 2> /dev/null; then
+        deb-systemd-invoke restart "$UNIT" || true
+    fi
+
+    echo ""
 }
 
 migrate_apt_auth_conf() {