From: Douglas Fuller Date: Fri, 1 Mar 2019 16:41:45 +0000 (-0500) Subject: mon/MDSMonitor: add mon auth caps for CephFS names X-Git-Tag: v16.1.0~1118^2~14 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=811e48d6ffd841ecd93a57eb00c6aceae476f4f8;p=ceph.git mon/MDSMonitor: add mon auth caps for CephFS names Add a 'fsname' clause to mon auth caps to restrict a client's view of the FSMap. Example: mon 'allow rw fsname=cephfs2' This would restrict the client's view of the FSMap to the MDSMap for cephfs2. Any MDS allocated to a different filesystem will be invisible. Global standby daemons are always visible. To allow multiple CephFSs, add multiple caps: mon 'allow rw fsname=cephfs2, allow rw fsname=cephfs2' Fixes: http://tracker.ceph.com/issues/15070 Signed-off-by: Douglas Fuller Signed-off-by: Rishabh Dave --- diff --git a/PendingReleaseNotes b/PendingReleaseNotes index 55e9bfa6b80e..858c720ef744 100644 --- a/PendingReleaseNotes +++ b/PendingReleaseNotes @@ -152,3 +152,8 @@ - ``ceph osd dump`` - ``ceph osd. dump_blocklist`` + +* caps: MON and MDS caps can now be used to restrict client's ability to view + and operate on specific Ceph file systems. The FS can be specificed using + ``fsname`` in caps. This also affects subcommand ``fs authorize``, the caps + produce by it will be specific to the FS name passed in its arguments. diff --git a/doc/cephfs/client-auth.rst b/doc/cephfs/client-auth.rst index 7807b64d990e..33ab7a7a53b7 100644 --- a/doc/cephfs/client-auth.rst +++ b/doc/cephfs/client-auth.rst @@ -81,9 +81,8 @@ the overall amount of space used on the cluster. If you would like the client to report the overall usage of the file system, and not just the quota usage on the sub-directory mounted, then set the -following config option on the client: +following config option on the client:: -:: client quota df = false @@ -100,9 +99,7 @@ with a "ceph." prefix, as well as restricting other means of setting these fields (such as openc operations with layouts). For example, in the following snippet client.0 can modify layouts and quotas -on the file system cephfs_a, but client.1 cannot. - -:: +on the file system cephfs_a, but client.1 cannot:: client.0 key: AQAz7EVWygILFRAAdIcuJ12opU/JKyfFmxhuaw== @@ -125,9 +122,7 @@ Note that when capability string also contains the 'p' flag, the 's' flag must appear after it (all flags except 'rw' must be specified in alphabetical order). For example, in the following snippet client.0 can create or delete snapshots -in the ``bar`` directory of file system ``cephfs_a``. - -:: +in the ``bar`` directory of file system ``cephfs_a``:: client.0 key: AQAz7EVWygILFRAAdIcuJ12opU/JKyfFmxhuaw== @@ -153,3 +148,39 @@ The optional ``{network/prefix}`` is a standard network name and prefix length in CIDR notation (e.g., ``10.3.0.0/16``). If present, the use of this capability is restricted to clients connecting from this network. + +File system Information Restriction +=================================== + +If desired, the monitor cluster can present a limited view of the file systems +available. In this case, the monitor cluster will only inform clients about +file systems specified by the administrator. Other file systems will not be +reported and commands affecting them will fail as if the file systems do +not exist. + +Consider following example. The Ceph cluster has 2 FSs:: + + $ ceph fs ls + name: cephfs, metadata pool: cephfs_metadata, data pools: [cephfs_data ] + name: cephfs2, metadata pool: cephfs2_metadata, data pools: [cephfs2_data ] + +But we authorize client ``someuser`` for only one FS:: + + $ ceph fs authorize cephfs client.someuser / rw + [client.someuser] + key = AQAmthpf89M+JhAAiHDYQkMiCq3x+J0n9e8REQ== + $ cat ceph.client.someuser.keyring + [client.someuser] + key = AQAmthpf89M+JhAAiHDYQkMiCq3x+J0n9e8REQ== + caps mds = "allow rw fsname=cephfs" + caps mon = "allow r fsname=cephfs" + caps osd = "allow rw tag cephfs data=cephfs" + +And the client can only see the FS that it has authorization for:: + + $ ceph fs ls -n client.someuser -k ceph.client.someuser.keyring + name: cephfs, metadata pool: cephfs_metadata, data pools: [cephfs_data ] + +Standby MDS daemons will always be displayed. Note that the information about +restricted MDS daemons and file systems may become available by other means, +such as ``ceph health detail``. diff --git a/src/mds/FSMap.cc b/src/mds/FSMap.cc index 5612b9f994fd..797325d6e874 100644 --- a/src/mds/FSMap.cc +++ b/src/mds/FSMap.cc @@ -1014,6 +1014,28 @@ std::vector FSMap::stop(mds_gid_t who) * Parse into a mds_role_t. The rank-only form is only valid * if legacy_client_ns is set. */ + +int FSMap::parse_role( + std::string_view role_str, + mds_role_t *role, + std::ostream &ss, + const std::vector &filter) const +{ + int r = parse_role(role_str, role, ss); + + string_view fs_name = get_filesystem(role->fscid)->mds_map.get_fs_name(); + + if (!filter.empty() && + std::find(filter.begin(), filter.end(), fs_name) == filter.end()) { + if (r >= 0) { + ss << "Invalid file system"; + } + return -ENOENT; + } + + return r; +} + int FSMap::parse_role( std::string_view role_str, mds_role_t *role, diff --git a/src/mds/FSMap.h b/src/mds/FSMap.h index 9b426f02584a..84e98de69843 100644 --- a/src/mds/FSMap.h +++ b/src/mds/FSMap.h @@ -244,6 +244,29 @@ public: const CompatSet &get_compat() const {return compat;} + void filter(const std::vector& allowed) + { + if (allowed.empty()) { + return; + } + + for (auto &f : filesystems) { + string_view fs_name = f.second->mds_map.get_fs_name(); + if (std::find(allowed.begin(), allowed.end(), fs_name) == allowed.end()) { + filesystems.erase(f.first); + } + } + + for (auto r : mds_roles) { + string_view fs_name = fs_name_from_gid(r.first); + if (std::find(allowed.begin(), allowed.end(), fs_name) == allowed.end()) { + mds_roles.erase(r.first); + } + } + + legacy_client_fscid = filesystems.begin()->first; + } + void set_enable_multiple(const bool v) { enable_multiple = v; @@ -294,9 +317,15 @@ public: /** * Does a daemon exist with this GID? */ - bool gid_exists(mds_gid_t gid) const + bool gid_exists(mds_gid_t gid, + const std::vector& in = {}) const { - return mds_roles.count(gid) > 0; + try { + string_view m = fs_name_from_gid(gid); + return in.empty() || std::find(in.begin(), in.end(), m) != in.end(); + } catch (const std::out_of_range&) { + return false; + } } /** @@ -307,15 +336,20 @@ public: return gid_exists(gid) && mds_roles.at(gid) != FS_CLUSTER_ID_NONE; } - fs_cluster_id_t gid_fscid(mds_gid_t gid) const - { + /** + * Which filesystem owns this GID? + */ + fs_cluster_id_t fscid_from_gid(mds_gid_t gid) const { + if (!gid_exists(gid)) { + return FS_CLUSTER_ID_NONE; + } return mds_roles.at(gid); } /** * Insert a new MDS daemon, as a standby */ - void insert(const mds_info_t& new_info); + void insert(const MDSMap::mds_info_t &new_info); /** * Assign an MDS cluster standby replay rank to a standby daemon @@ -430,6 +464,16 @@ public: } } + std::string_view fs_name_from_gid(mds_gid_t gid) const + { + auto fscid = mds_roles.at(gid); + if (fscid == FS_CLUSTER_ID_NONE or !filesystem_exists(fscid)) { + return std::string_view(); + } else { + return get_filesystem(fscid)->mds_map.get_fs_name(); + } + } + bool is_standby_replay(mds_gid_t who) const { return filesystems.at(mds_roles.at(who))->is_standby_replay(who); @@ -484,6 +528,12 @@ public: Filesystem::const_ref *result ) const; + int parse_role( + std::string_view role_str, + mds_role_t *role, + std::ostream &ss, + const std::vector &filter) const; + int parse_role( std::string_view role_str, mds_role_t *role, diff --git a/src/mon/FSCommands.cc b/src/mon/FSCommands.cc index 28a2fd015598..f92a76cabecf 100644 --- a/src/mon/FSCommands.cc +++ b/src/mon/FSCommands.cc @@ -64,7 +64,7 @@ class FlagSetHandler : public FileSystemCommandHandler int handle( Monitor *mon, - FSMap &fsmap, + FSMap& fsmap, MonOpRequestRef op, const cmdmap_t& cmdmap, std::stringstream &ss) override @@ -126,10 +126,6 @@ class FailHandler : public FileSystemCommandHandler } auto fs = fsmap.get_filesystem(fs_name); - if (fs == nullptr) { - ss << "Not found: '" << fs_name << "'"; - return -ENOENT; - } auto f = [](auto fs) { fs->mds_map.set_flag(CEPH_MDSMAP_NOT_JOINABLE); @@ -169,7 +165,7 @@ class FsNewHandler : public FileSystemCommandHandler int handle( Monitor *mon, - FSMap &fsmap, + FSMap& fsmap, MonOpRequestRef op, const cmdmap_t& cmdmap, std::stringstream &ss) override @@ -323,7 +319,7 @@ public: int handle( Monitor *mon, - FSMap &fsmap, + FSMap& fsmap, MonOpRequestRef op, const cmdmap_t& cmdmap, std::stringstream &ss) override @@ -335,11 +331,6 @@ public: } auto fs = fsmap.get_filesystem(fs_name); - if (fs == nullptr) { - ss << "Not found: '" << fs_name << "'"; - return -ENOENT; - } - string var; if (!cmd_getval(cmdmap, "var", var) || var.empty()) { ss << "Invalid variable"; @@ -364,6 +355,7 @@ public: ss << "You must specify at least one MDS"; return -EINVAL; } + if (n > 1 && n > fs->mds_map.get_max_mds()) { if (fs->mds_map.was_snaps_ever_allowed() && !fs->mds_map.allows_multimds_snaps()) { @@ -738,7 +730,7 @@ class AddDataPoolHandler : public FileSystemCommandHandler int handle( Monitor *mon, - FSMap &fsmap, + FSMap& fsmap, MonOpRequestRef op, const cmdmap_t& cmdmap, std::stringstream &ss) override @@ -755,12 +747,6 @@ class AddDataPoolHandler : public FileSystemCommandHandler return -EINVAL; } - auto fs = fsmap.get_filesystem(fs_name); - if (fs == nullptr) { - ss << "Not found: '" << fs_name << "'"; - return -ENOENT; - } - int64_t poolid = mon->osdmon()->osdmap.lookup_pg_pool_name(poolname); if (poolid < 0) { string err; @@ -776,6 +762,7 @@ class AddDataPoolHandler : public FileSystemCommandHandler return r; } + auto fs = fsmap.get_filesystem(fs_name); // no-op when the data_pool already on fs if (fs->mds_map.is_data_pool(poolid)) { ss << "data pool " << poolid << " is already on fs " << fs_name; @@ -817,7 +804,7 @@ class SetDefaultHandler : public FileSystemCommandHandler int handle( Monitor *mon, - FSMap &fsmap, + FSMap& fsmap, MonOpRequestRef op, const cmdmap_t& cmdmap, std::stringstream &ss) override @@ -844,7 +831,7 @@ class RemoveFilesystemHandler : public FileSystemCommandHandler int handle( Monitor *mon, - FSMap &fsmap, + FSMap& fsmap, MonOpRequestRef op, const cmdmap_t& cmdmap, std::stringstream &ss) override @@ -918,7 +905,7 @@ class ResetFilesystemHandler : public FileSystemCommandHandler int handle( Monitor *mon, - FSMap &fsmap, + FSMap& fsmap, MonOpRequestRef op, const cmdmap_t& cmdmap, std::stringstream &ss) override @@ -963,7 +950,7 @@ class RemoveDataPoolHandler : public FileSystemCommandHandler int handle( Monitor *mon, - FSMap &fsmap, + FSMap& fsmap, MonOpRequestRef op, const cmdmap_t& cmdmap, std::stringstream &ss) override @@ -978,12 +965,6 @@ class RemoveDataPoolHandler : public FileSystemCommandHandler return -EINVAL; } - auto fs = fsmap.get_filesystem(fs_name); - if (fs == nullptr) { - ss << "Not found: '" << fs_name << "'"; - return -ENOENT; - } - int64_t poolid = mon->osdmon()->osdmap.lookup_pg_pool_name(poolname); if (poolid < 0) { string err; @@ -999,12 +980,12 @@ class RemoveDataPoolHandler : public FileSystemCommandHandler ceph_assert(poolid >= 0); // Checked by parsing code above + auto fs = fsmap.get_filesystem(fs_name); if (fs->mds_map.get_first_data_pool() == poolid) { ss << "cannot remove default data pool"; return -EINVAL; } - int r = 0; fsmap.modify_filesystem(fs->fscid, [&r, poolid](std::shared_ptr fs) @@ -1040,11 +1021,11 @@ class AliasHandler : public T alias_prefix = new_prefix; } - std::string const &get_prefix() override {return alias_prefix;} + std::string const &get_prefix() const override {return alias_prefix;} int handle( Monitor *mon, - FSMap &fsmap, + FSMap& fsmap, MonOpRequestRef op, const cmdmap_t& cmdmap, std::stringstream &ss) override @@ -1355,3 +1336,27 @@ int FileSystemCommandHandler::_check_pool( return 0; } +int FileSystemCommandHandler::is_op_allowed( + const MonOpRequestRef& op, const FSMap& fsmap, const cmdmap_t& cmdmap, + std::stringstream &ss) const +{ + string fs_name; + cmd_getval(cmdmap, "fs_name", fs_name); + + // so that fsmap can filtered and the original copy is untouched. + FSMap fsmap_copy = fsmap; + fsmap_copy.filter(op->get_session()->get_allowed_fs_names()); + + auto fs = fsmap_copy.get_filesystem(fs_name); + if (fs == nullptr) { + ss << "Filesystem not found: '" << fs_name << "'"; + return -ENOENT; + } + + if (!op->get_session()->fs_name_capable(fs_name, MON_CAP_W)) { + ss << "Permission denied: '" << fs_name << "'"; + return -EPERM; + } + + return 1; +} diff --git a/src/mon/FSCommands.h b/src/mon/FSCommands.h index 66e0286a465f..726ff281a1bc 100644 --- a/src/mon/FSCommands.h +++ b/src/mon/FSCommands.h @@ -49,7 +49,7 @@ protected: bool force, std::stringstream *ss) const; - virtual std::string const &get_prefix() {return prefix;} + virtual std::string const &get_prefix() const {return prefix;} public: FileSystemCommandHandler(const std::string &prefix_) @@ -59,9 +59,21 @@ public: virtual ~FileSystemCommandHandler() {} - bool can_handle(std::string const &prefix_) + int is_op_allowed(const MonOpRequestRef& op, const FSMap& fsmap, + const cmdmap_t& cmdmap, std::stringstream &ss) const; + + int can_handle(std::string const &prefix_, MonOpRequestRef& op, FSMap& fsmap, + const cmdmap_t& cmdmap, std::stringstream &ss) const { - return get_prefix() == prefix_; + if (get_prefix() != prefix_) { + return 0; + } + + if (get_prefix() == "fs new" || get_prefix() == "fs flag set") { + return 1; + } + + return is_op_allowed(op, fsmap, cmdmap, ss); } static std::list > load(Paxos *paxos); diff --git a/src/mon/MDSMonitor.cc b/src/mon/MDSMonitor.cc index 2a8e2742e863..4010f1ee39fe 100644 --- a/src/mon/MDSMonitor.cc +++ b/src/mon/MDSMonitor.cc @@ -51,6 +51,7 @@ using std::ostringstream; using std::pair; using std::set; using std::string; +using std::string_view; using std::stringstream; using std::to_string; using std::vector; @@ -936,8 +937,6 @@ bool MDSMonitor::preprocess_command(MonOpRequestRef op) bufferlist rdata; stringstream ss, ds; - const auto &fsmap = get_fsmap(); - cmdmap_t cmdmap; if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) { // ss has reason for failure @@ -958,6 +957,11 @@ bool MDSMonitor::preprocess_command(MonOpRequestRef op) return true; } + // to use const qualifier filter fsmap beforehand + FSMap _fsmap_copy = get_fsmap(); + _fsmap_copy.filter(session->get_allowed_fs_names()); + const auto& fsmap = _fsmap_copy; + if (prefix == "mds stat") { if (f) { f->open_object_section("mds_stat"); @@ -1343,28 +1347,35 @@ bool MDSMonitor::prepare_command(MonOpRequestRef op) bool batched_propose = false; for (const auto &h : handlers) { - if (h->can_handle(prefix)) { - batched_propose = h->batched_propose(); - if (batched_propose) { - paxos->plug(); - } - r = h->handle(mon, pending, op, cmdmap, ss); - if (batched_propose) { - paxos->unplug(); - } + r = h->can_handle(prefix, op, pending, cmdmap, ss); + if (r == 1) { + ; // pass, since we got the right handler. + } else if (r == 0) { + continue; + } else { + goto out; + } - if (r == -EAGAIN) { - // message has been enqueued for retry; return. - dout(4) << __func__ << " enqueue for retry by prepare_command" << dendl; - return false; - } else { - if (r == 0) { - // On successful updates, print the updated map - print_map(pending); - } - // Successful or not, we're done: respond. - goto out; + batched_propose = h->batched_propose(); + if (batched_propose) { + paxos->plug(); + } + r = h->handle(mon, pending, op, cmdmap, ss); + if (batched_propose) { + paxos->unplug(); + } + + if (r == -EAGAIN) { + // message has been enqueued for retry; return. + dout(4) << __func__ << " enqueue for retry by prepare_command" << dendl; + return false; + } else { + if (r == 0) { + // On successful updates, print the updated map + print_map(pending); } + // Successful or not, we're done: respond. + goto out; } } @@ -1430,7 +1441,7 @@ int MDSMonitor::filesystem_command( << cmd_vartype_stringify(cmdmap.at("state")) << "'"; return -EINVAL; } - if (fsmap.gid_exists(gid)) { + if (fsmap.gid_exists(gid, op->get_session()->get_allowed_fs_names())) { fsmap.modify_daemon(gid, [state](auto& info) { info.state = state; }); @@ -1443,6 +1454,21 @@ int MDSMonitor::filesystem_command( cmd_getval(cmdmap, "role_or_gid", who); MDSMap::mds_info_t failed_info; + mds_gid_t gid = gid_from_arg(fsmap, who, ss); + if (gid == MDS_GID_NONE) { + return -EINVAL; + } + if(!fsmap.gid_exists(gid, op->get_session()->get_allowed_fs_names())) { + ss << "MDS named '" << who << "' does not exist, is not up or you " + << "lack the permission to see."; + return -EINVAL; + } + string_view fs_name = fsmap.fs_name_from_gid(gid); + if (!op->get_session()->fs_name_capable(fs_name, MON_CAP_W)) { + ss << "Permission denied."; + return -EPERM; + } + r = fail_mds(fsmap, ss, who, &failed_info); if (r < 0 && r == -EAGAIN) { mon->osdmon()->wait_for_writeable(op, new C_RetryMessage(this, op)); @@ -1461,21 +1487,25 @@ int MDSMonitor::filesystem_command( << cmd_vartype_stringify(cmdmap.at("gid")) << "'"; return -EINVAL; } - if (!fsmap.gid_exists(gid)) { + if (!fsmap.gid_exists(gid, op->get_session()->get_allowed_fs_names())) { ss << "mds gid " << gid << " does not exist"; - r = 0; + return 0; + } + string_view fs_name = fsmap.fs_name_from_gid(gid); + if (!op->get_session()->fs_name_capable(fs_name, MON_CAP_W)) { + ss << "Permission denied."; + return -EPERM; + } + const auto &info = fsmap.get_info_gid(gid); + MDSMap::DaemonState state = info.state; + if (state > 0) { + ss << "cannot remove active mds." << info.name + << " rank " << info.rank; + return -EBUSY; } else { - const auto &info = fsmap.get_info_gid(gid); - MDSMap::DaemonState state = info.state; - if (state > 0) { - ss << "cannot remove active mds." << info.name - << " rank " << info.rank; - return -EBUSY; - } else { - fsmap.erase(gid, {}); - ss << "removed mds gid " << gid; - return 0; - } + fsmap.erase(gid, {}); + ss << "removed mds gid " << gid; + return 0; } } else if (prefix == "mds rmfailed") { bool confirm = false; @@ -1489,11 +1519,17 @@ int MDSMonitor::filesystem_command( std::string role_str; cmd_getval(cmdmap, "role", role_str); mds_role_t role; - int r = fsmap.parse_role(role_str, &role, ss); + const auto fs_names = op->get_session()->get_allowed_fs_names(); + int r = fsmap.parse_role(role_str, &role, ss, fs_names); if (r < 0) { ss << "invalid role '" << role_str << "'"; return -EINVAL; } + string_view fs_name = fsmap.get_filesystem(role.fscid)->mds_map.get_fs_name(); + if (!op->get_session()->fs_name_capable(fs_name, MON_CAP_W)) { + ss << "Permission denied."; + return -EPERM; + } fsmap.modify_filesystem( role.fscid, @@ -1540,10 +1576,16 @@ int MDSMonitor::filesystem_command( std::string role_str; cmd_getval(cmdmap, "role", role_str); mds_role_t role; - r = fsmap.parse_role(role_str, &role, ss); + const auto fs_names = op->get_session()->get_allowed_fs_names(); + r = fsmap.parse_role(role_str, &role, ss, fs_names); if (r < 0) { return r; } + string_view fs_name = fsmap.get_filesystem(role.fscid)->mds_map.get_fs_name(); + if (!op->get_session()->fs_name_capable(fs_name, MON_CAP_W)) { + ss << "Permission denied."; + return -EPERM; + } bool modified = fsmap.undamaged(role.fscid, role.rank); if (modified) { @@ -1561,6 +1603,12 @@ int MDSMonitor::filesystem_command( return -EINVAL; } + string_view fs_name = fsmap.fs_name_from_gid(gid); + if (!op->get_session()->fs_name_capable(fs_name, MON_CAP_W)) { + ss << "Permission denied."; + return -EPERM; + } + bool freeze = false; { std::string str; @@ -1627,7 +1675,10 @@ void MDSMonitor::check_sub(Subscription *sub) { dout(20) << __func__ << ": " << sub->type << dendl; - const auto &fsmap = get_fsmap(); + // to use const qualifier filter fsmap beforehand + FSMap _fsmap_copy = get_fsmap(); + _fsmap_copy.filter(sub->session->get_allowed_fs_names()); + const auto& fsmap = _fsmap_copy; if (sub->type == "fsmap") { if (sub->next <= fsmap.get_epoch()) { @@ -2084,7 +2135,7 @@ bool MDSMonitor::check_health(FSMap& fsmap, bool* propose_osdmap) auto info = fsmap.get_info_gid(gid); const mds_info_t* rep_info = nullptr; if (info.rank >= 0) { - auto fscid = fsmap.gid_fscid(gid); + auto fscid = fsmap.fscid_from_gid(gid); rep_info = fsmap.find_replacement_for({fscid, info.rank}); } bool dropped = drop_mds(fsmap, gid, rep_info, propose_osdmap); diff --git a/src/mon/MDSMonitor.h b/src/mon/MDSMonitor.h index 02dec34d30f8..45be5106672d 100644 --- a/src/mon/MDSMonitor.h +++ b/src/mon/MDSMonitor.h @@ -89,8 +89,7 @@ class MDSMonitor : public PaxosService, public PaxosFSMap, protected CommandHand bool prepare_offload_targets(MonOpRequestRef op); int fail_mds(FSMap &fsmap, std::ostream &ss, - const std::string &arg, - mds_info_t *failed_info); + const std::string &arg, mds_info_t *failed_info); bool preprocess_command(MonOpRequestRef op); bool prepare_command(MonOpRequestRef op); diff --git a/src/mon/MonCap.cc b/src/mon/MonCap.cc index b550ad7dd3ec..f130f95bcc4c 100644 --- a/src/mon/MonCap.cc +++ b/src/mon/MonCap.cc @@ -139,7 +139,8 @@ BOOST_FUSION_ADAPT_STRUCT(MonCapGrant, (std::string, command) (kvmap, command_args) (mon_rwxa_t, allow) - (std::string, network)) + (std::string, network) + (std::string, fs_name)) BOOST_FUSION_ADAPT_STRUCT(StringConstraint, (StringConstraint::MatchType, match_type) @@ -539,6 +540,7 @@ struct MonCapParser : qi::grammar unquoted_word %= +char_("a-zA-Z0-9_./-"); str %= quoted_string | unquoted_word; network_str %= +char_("/.:a-fA-F0-9]["); + fs_name_str %= +char_("a-zA-Z0-9-_."); spaces = +(lit(' ') | lit('\n') | lit('\t')); @@ -579,7 +581,8 @@ struct MonCapParser : qi::grammar >> qi::attr(string()) >> qi::attr(string()) >> qi::attr(string()) >> qi::attr(map()) >> rwxa - >> -(spaces >> lit("network") >> spaces >> network_str); + >> -(spaces >> lit("network") >> spaces >> network_str) + >> -(spaces >> lit("fsname") >> (lit('=') | spaces) >> fs_name_str); // rwxa := * | [r][w][x] rwxa = @@ -605,6 +608,7 @@ struct MonCapParser : qi::grammar qi::rule quoted_string; qi::rule unquoted_word; qi::rule str, network_str; + qi::rule fs_name_str; qi::rule str_match, str_prefix, str_regex; qi::rule()> kv_pair; diff --git a/src/mon/MonCap.h b/src/mon/MonCap.h index edc1bda51a1f..d38ffc54be77 100644 --- a/src/mon/MonCap.h +++ b/src/mon/MonCap.h @@ -9,6 +9,7 @@ #include "include/common_fwd.h" #include "include/types.h" #include "common/entity_name.h" +#include "mds/mdstypes.h" static const __u8 MON_CAP_R = (1 << 1); // read static const __u8 MON_CAP_W = (1 << 2); // write @@ -53,7 +54,7 @@ std::ostream& operator<<(std::ostream& out, const StringConstraint& c); struct MonCapGrant { /* - * A grant can come in one of four forms: + * A grant can come in one of five forms: * * - a blanket allow ('allow rw', 'allow *') * - this will match against any service and the read/write/exec flags @@ -72,11 +73,16 @@ struct MonCapGrant { * of key/value pairs that constrain use of that command. if no pairs * are specified, any arguments are allowed; if a pair is specified, that * argument must be present and equal or match a prefix. + * + * - an fs name ('allow fsname foo') + * - this will restrict access to MDSMaps in the FSMap to the provided + * fs name. */ std::string service; std::string profile; std::string command; std::map command_args; + std::string fs_name; // restrict by network std::string network; @@ -105,6 +111,7 @@ struct MonCapGrant { MonCapGrant(std::string c, std::string a, StringConstraint co) : command(std::move(c)) { command_args[a] = co; } + MonCapGrant(mon_rwxa_t a, std::string fsname) : fs_name(fsname), allow(a) {} /** * check if given request parameters match our constraints @@ -127,7 +134,8 @@ struct MonCapGrant { allow == MON_CAP_ANY && service.length() == 0 && profile.length() == 0 && - command.length() == 0; + command.length() == 0 && + fs_name.empty(); } }; @@ -174,6 +182,34 @@ struct MonCap { void decode(ceph::buffer::list::const_iterator& bl); void dump(ceph::Formatter *f) const; static void generate_test_instances(std::list& ls); + + std::vector allowed_fs_names() const { + std::vector ret; + for (auto& g : grants) { + if (not g.fs_name.empty()) { + ret.push_back(g.fs_name); + } else { + return {}; + } + } + return ret; + } + + bool fs_name_capable(string_view fs_name, __u8 mask) { + for (auto& g: grants) { + if (g.is_allow_all()) { + return true; + } + if (g.fs_name.empty() || g.fs_name == fs_name) { + if (mask & g.allow) { + return true; + } + } + } + + return false; + } + }; WRITE_CLASS_ENCODER(MonCap) diff --git a/src/mon/Monitor.cc b/src/mon/Monitor.cc index 51d5c12c41e5..5df77ebc2bae 100644 --- a/src/mon/Monitor.cc +++ b/src/mon/Monitor.cc @@ -2923,11 +2923,14 @@ void Monitor::log_health( } } -void Monitor::get_cluster_status(stringstream &ss, Formatter *f) +void Monitor::get_cluster_status(stringstream &ss, Formatter *f, + MonSession *session) { if (f) f->open_object_section("status"); + const auto&& fs_names = session->get_allowed_fs_names(); + mono_clock::time_point now = mono_clock::now(); if (f) { f->dump_stream("fsid") << monmap->get_fsid(); @@ -2957,7 +2960,14 @@ void Monitor::get_cluster_status(stringstream &ss, Formatter *f) mgrstatmon()->print_summary(f, NULL); f->close_section(); f->open_object_section("fsmap"); - mdsmon()->get_fsmap().print_summary(f, NULL); + + FSMap fsmap_copy = mdsmon()->get_fsmap(); + if (!fs_names.empty()) { + fsmap_copy.filter(fs_names); + } + const FSMap *fsmapp = &fsmap_copy; + + fsmapp->print_summary(f, NULL); f->close_section(); f->open_object_section("mgrmap"); mgrmon()->get_map().print_summary(f, nullptr); @@ -3009,9 +3019,17 @@ void Monitor::get_cluster_status(stringstream &ss, Formatter *f) mgrmon()->get_map().print_summary(nullptr, &ss); ss << "\n"; } - if (mdsmon()->should_print_status()) { - ss << " mds: " << spacing << mdsmon()->get_fsmap() << "\n"; + + FSMap fsmap_copy = mdsmon()->get_fsmap(); + if (!fs_names.empty()) { + fsmap_copy.filter(fs_names); + } + const FSMap *fsmapp = &fsmap_copy; + + if (fsmapp->filesystem_count() > 0 and mdsmon()->should_print_status()){ + ss << " mds: " << spacing << *fsmapp << "\n"; } + ss << " osd: " << spacing; osdmon()->osdmap.print_summary(NULL, ss, string(maxlen + 6, ' ')); ss << "\n"; @@ -3551,7 +3569,7 @@ void Monitor::handle_command(MonOpRequestRef op) if (prefix == "status") { // get_cluster_status handles f == NULL - get_cluster_status(ds, f.get()); + get_cluster_status(ds, f.get(), session); if (f) { f->flush(ds); diff --git a/src/mon/Monitor.h b/src/mon/Monitor.h index d8db8bf01043..b42ba927a9d9 100644 --- a/src/mon/Monitor.h +++ b/src/mon/Monitor.h @@ -753,7 +753,8 @@ protected: public: - void get_cluster_status(std::stringstream &ss, ceph::Formatter *f); + void get_cluster_status(std::stringstream &ss, ceph::Formatter *f, + MonSession *session); void reply_command(MonOpRequestRef op, int rc, const std::string &rs, version_t version); void reply_command(MonOpRequestRef op, int rc, const std::string &rs, ceph::buffer::list& rdata, version_t version); diff --git a/src/mon/Session.h b/src/mon/Session.h index 4ca4814d7005..0502dcc77a92 100644 --- a/src/mon/Session.h +++ b/src/mon/Session.h @@ -106,6 +106,14 @@ struct MonSession : public RefCountedObject { get_peer_socket_addr()); } + std::vector get_allowed_fs_names() const { + return caps.allowed_fs_names(); + } + + bool fs_name_capable(string_view fsname, __u8 mask) { + return caps.fs_name_capable(fsname, mask); + } + const entity_addr_t& get_peer_socket_addr() { return socket_addr; }