BuildRequires: libaio-devel
BuildRequires: libblkid-devel >= 2.17
BuildRequires: libcurl-devel
+BuildRequires: libcap-ng-devel
BuildRequires: libudev-devel
BuildRequires: libnl3-devel
BuildRequires: liboath-devel
libblkid-dev (>= 2.17),
# Crimson libc-ares-dev,
# Crimson libcrypto++-dev,
+ libcap-ng-dev,
libcunit1-dev,
libcurl4-openssl-dev,
libexpat1-dev,
Synopsis
========
-| **mount.ceph** *monaddr1*\ [,\ *monaddr2*\ ,...]:/[*subdir*] *dir* [
+| **mount.ceph** [*monaddr1*\ ,\ *monaddr2*\ ,...]:/[*subdir*] *dir* [
-o *options* ]
Description
===========
-**mount.ceph** is a simple helper for mounting the Ceph file system on
+**mount.ceph** is a helper for mounting the Ceph file system on
a Linux host. It serves to resolve monitor hostname(s) into IP
addresses and read authentication keys from disk; the Linux kernel
client component does most of the real work. In fact, it is possible
good idea to specify more than one in case one happens to be down at
the time of mount.
+If the host portion of the device is left blank, then **mount.ceph** will
+attempt to determine monitor addresses using local configuration files
+and/or DNS SRV records.
+
A subdirectory subdir may be specified if a subset of the file system
is to be mounted.
:command:`noasyncreaddir`
no dcache readdir
+:command:`conf`
+ Path to a ceph.conf file. This is used to initialize the ceph context
+ for autodiscovery of monitor addresses and auth secrets. The default is
+ to use the standard search path for ceph.conf files.
+
+Mount Secrets
+=============
+If the `secret` and `secretfile` options are not specified on the command-line
+then the mount helper will spawn a child process that will use the standard
+ceph library routines to find a keyring and fetch the secret from it.
Examples
========
mount.ceph monhost1:7000,monhost2:7000,monhost3:7000:/ /mnt/foo
+To automatically determine the monitor addresses from local configuration::
+
+ mount.ceph :/ /mnt/foo
+
To mount only part of the namespace::
mount.ceph monhost1:/some/small/thing /mnt/thing
add_subdirectory(client)
if(WITH_LIBCEPHFS)
+ find_package(PkgConfig QUIET REQUIRED)
+ pkg_check_modules(CAPNG REQUIRED libcap-ng)
set(libcephfs_srcs libcephfs.cc)
add_library(cephfs ${CEPH_SHARED} ${libcephfs_srcs})
target_link_libraries(cephfs PRIVATE client ceph-common
set(mount_ceph_srcs
- mount.ceph.c)
+ mount.ceph.c conf.cc)
add_executable(mount.ceph ${mount_ceph_srcs}
$<TARGET_OBJECTS:parse_secret_objs>
$<TARGET_OBJECTS:common_mountcephfs_objs>)
-set_target_properties(mount.ceph PROPERTIES
- INSTALL_RPATH "")
-target_link_libraries(mount.ceph keyutils::keyutils)
+target_link_libraries(mount.ceph keyutils::keyutils ${CAPNG_LIBRARIES} global ceph-common)
install(TARGETS mount.ceph DESTINATION ${CMAKE_INSTALL_SBINDIR})
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <string>
+#include <vector>
+#include <cstring>
+#include <map>
+
+#include "common/ceph_context.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "common/config.h"
+#include "auth/KeyRing.h"
+#include "mount.ceph.h"
+#include "mon/MonClient.h"
+
+extern "C" void mount_ceph_get_config_info(const char *config_file,
+ const char *name,
+ struct ceph_config_info *cci)
+{
+ int err;
+ KeyRing keyring;
+ CryptoKey secret;
+ std::string secret_str;
+ std::string monaddrs;
+ vector<const char *> args = { "--name", name };
+ bool first = true;
+
+ if (config_file) {
+ args.push_back("--conf");
+ args.push_back(config_file);
+ }
+
+ /* Create CephContext */
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DAEMON_ACTIONS|CINIT_FLAG_NO_MON_CONFIG);
+ auto& conf = cct->_conf;
+
+ conf.parse_env(cct->get_module_type()); // environment variables override
+ conf.apply_changes(nullptr);
+
+ MonClient monc = MonClient(cct.get());
+ err = monc.build_initial_monmap();
+ if (err)
+ goto scrape_keyring;
+
+ for (const auto& mon : monc.monmap.addr_mons) {
+ auto& eaddr = mon.first;
+
+ // For now, kernel client only accepts legacy addrs
+ if (!eaddr.is_legacy())
+ continue;
+
+ std::string addr;
+ addr += eaddr.ip_only_to_str();
+ addr += ":";
+ addr += std::to_string(eaddr.get_port());
+ /* If this will overrun cci_mons, stop here */
+ if (monaddrs.length() + 1 + addr.length() + 1 > sizeof(cci->cci_mons))
+ break;
+
+ if (first)
+ first = false;
+ else
+ monaddrs += ",";
+
+ monaddrs += addr;
+ }
+
+ if (monaddrs.length())
+ strcpy(cci->cci_mons, monaddrs.c_str());
+ else
+ mount_ceph_debug("Could not discover monitor addresses");
+
+scrape_keyring:
+ err = keyring.from_ceph_context(cct.get());
+ if (err) {
+ mount_ceph_debug("keyring.from_ceph_context failed: %d\n", err);
+ return;
+ }
+
+ if (!keyring.get_secret(conf->name, secret)) {
+ mount_ceph_debug("keyring.get_secret failed\n");
+ return;
+ }
+
+ secret.encode_base64(secret_str);
+
+ if (secret_str.length() + 1 > sizeof(cci->cci_secret)) {
+ mount_ceph_debug("secret is too long\n");
+ return;
+ }
+ strcpy(cci->cci_secret, secret_str.c_str());
+}
#include <errno.h>
#include <sys/mount.h>
#include <stdbool.h>
+#include <sys/mman.h>
+#include <wait.h>
+#include <cap-ng.h>
#include "common/module.h"
#include "common/secret.h"
#include "include/addr_parsing.h"
+#include "mount.ceph.h"
#ifndef MS_RELATIME
# define MS_RELATIME (1<<21)
char *cmi_name;
char *cmi_path;
char *cmi_mons;
+ char *cmi_conf;
char *cmi_opts;
int cmi_opts_len;
char cmi_secret[SECRET_BUFSIZE];
fprintf(stderr, "source mount path was not specified\n");
return -EINVAL;
}
+
len = mount_path - orig_str;
- if (len == 0) {
- fprintf(stderr, "server address expected\n");
- return -EINVAL;
+ if (len != 0) {
+ cmi->cmi_mons = strndup(orig_str, len);
+ if (!cmi->cmi_mons)
+ return -ENOMEM;
}
- cmi->cmi_mons = strndup(orig_str, len);
- if (!cmi->cmi_mons)
- return -ENOMEM;
-
mount_path++;
cmi->cmi_path = strdup(mount_path);
if (!cmi->cmi_path)
return src;
}
+static int
+drop_capabilities()
+{
+ capng_setpid(getpid());
+ capng_clear(CAPNG_SELECT_BOTH);
+ if (capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH)) {
+ fprintf(stderr, "Unable to update permitted capability set.\n");
+ return EX_SYSERR;
+ }
+ if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_READ_SEARCH)) {
+ fprintf(stderr, "Unable to update effective capability set.\n");
+ return EX_SYSERR;
+ }
+ if (capng_apply(CAPNG_SELECT_BOTH)) {
+ fprintf(stderr, "Unable to apply new capability set.\n");
+ return EX_SYSERR;
+ }
+ return 0;
+}
+
+/*
+ * Attempt to fetch info from the local config file, if one is present. Since
+ * this involves activity that may be dangerous for a privileged task, we
+ * fork(), have the child drop privileges and do the processing and then hand
+ * back the results via memory shared with the parent.
+ */
+static int fetch_config_info(struct ceph_mount_info *cmi)
+{
+ int ret = 0;
+ pid_t pid;
+ struct ceph_config_info *cci;
+
+ /* Don't do anything if we already have requisite info */
+ if (cmi->cmi_secret[0] && cmi->cmi_mons)
+ return 0;
+
+ cci = mmap((void *)0, sizeof(*cci), PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (cci == MAP_FAILED) {
+ mount_ceph_debug("Unable to allocate memory: %s\n",
+ strerror(errno));
+ return EX_SYSERR;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ mount_ceph_debug("fork() failure: %s\n", strerror(errno));
+ ret = EX_SYSERR;
+ goto out;
+ }
+
+ if (pid == 0) {
+ /* child */
+ ret = drop_capabilities();
+ if (ret)
+ exit(1);
+ mount_ceph_get_config_info(cmi->cmi_conf, cmi->cmi_name, cci);
+ exit(0);
+ } else {
+ /* parent */
+ pid = wait(&ret);
+ if (!WIFEXITED(ret)) {
+ mount_ceph_debug("Child process terminated abnormally.\n");
+ ret = EX_SYSERR;
+ goto out;
+ }
+ ret = WEXITSTATUS(ret);
+ if (ret) {
+ mount_ceph_debug("Child exited with status %d\n", ret);
+ ret = EX_SYSERR;
+ goto out;
+ }
+
+ /*
+ * Copy values from MAP_SHARED buffer to cmi if we didn't
+ * already find anything and we got something from the child.
+ */
+ size_t len;
+ if (!cmi->cmi_secret[0] && cci->cci_secret[0]) {
+
+ len = strnlen(cci->cci_secret, SECRET_BUFSIZE);
+ if (len < SECRET_BUFSIZE) {
+ memcpy(cmi->cmi_secret, cci->cci_secret, len + 1);
+ } else {
+ mount_ceph_debug("secret is too long (len=%zu max=%zu)!\n", len, SECRET_BUFSIZE);
+ }
+ }
+ if (!cmi->cmi_mons && cci->cci_mons[0]) {
+ len = strnlen(cci->cci_mons, MON_LIST_BUFSIZE);
+ if (len < MON_LIST_BUFSIZE)
+ cmi->cmi_mons = strndup(cci->cci_mons, len + 1);
+ }
+ }
+out:
+ munmap(cci, sizeof(*cci));
+ return ret;
+}
+
/*
* this one is partially based on parse_options() from cifs.mount.c
*/
len = strnlen(value, sizeof(cmi->cmi_secret)) + 1;
if (len <= sizeof(cmi->cmi_secret))
memcpy(cmi->cmi_secret, value, len);
+ } else if (strncmp(data, "conf", 4) == 0) {
+ if (!value || !*value) {
+ fprintf(stderr, "mount option conf requires a value.\n");
+ return -EINVAL;
+ }
+ /* keep pointer to value */
+ cmi->cmi_conf = strdup(value);
+ if (!cmi->cmi_conf)
+ return -ENOMEM;
} else if (strncmp(data, "name", 4) == 0) {
if (!value || !*value) {
fprintf(stderr, "mount option name requires a value.\n");
free(cmi->cmi_name);
free(cmi->cmi_path);
free(cmi->cmi_mons);
+ free(cmi->cmi_conf);
}
static int finalize_options(struct ceph_mount_info *cmi)
goto out;
}
- /* Ensure the ceph key_type is available */
- modprobe();
+ /* We don't care if this errors out, since this is best-effort */
+ fetch_config_info(&cmi);
+
+ if (!cmi.cmi_mons) {
+ fprintf(stderr, "unable to determine mon addresses\n");
+ retval = EX_USAGE;
+ goto out;
+ }
rsrc = finalize_src(&cmi);
if (!rsrc) {
goto out;
}
+ /* Ensure the ceph key_type is available */
+ modprobe();
+
retval = finalize_options(&cmi);
if (retval) {
fprintf(stderr, "couldn't finalize options: %d\n", retval);
/* Buffer size for secret= option */
#define SECRET_OPTION_BUFSIZE (sizeof("secret=") + MAX_SECRET_LEN + 1)
+/* 2k should be enough for anyone? */
+#define MON_LIST_BUFSIZE 2048
+
void mount_ceph_debug(const char *fmt, ...);
+struct ceph_config_info {
+ char cci_secret[SECRET_BUFSIZE]; // auth secret
+ char cci_mons[MON_LIST_BUFSIZE]; // monitor addrs
+};
+
+void mount_ceph_get_config_info(const char *config_file, const char *name,
+ struct ceph_config_info *cci);
+
#ifdef __cplusplus
}
#endif