]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mon/MDSMonitor: add mon auth caps for CephFS names
authorDouglas Fuller <dfuller@redhat.com>
Fri, 1 Mar 2019 16:41:45 +0000 (11:41 -0500)
committerRishabh Dave <ridave@redhat.com>
Thu, 10 Sep 2020 11:40:51 +0000 (17:10 +0530)
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 <dfuller@redhat.com>
Signed-off-by: Rishabh Dave <ridave@redhat.com>
13 files changed:
PendingReleaseNotes
doc/cephfs/client-auth.rst
src/mds/FSMap.cc
src/mds/FSMap.h
src/mon/FSCommands.cc
src/mon/FSCommands.h
src/mon/MDSMonitor.cc
src/mon/MDSMonitor.h
src/mon/MonCap.cc
src/mon/MonCap.h
src/mon/Monitor.cc
src/mon/Monitor.h
src/mon/Session.h

index 55e9bfa6b80e7aae7187be23e007799c0e2a03a8..858c720ef744a04708ce35d3c256a2e5d16d8356 100644 (file)
 
   - ``ceph osd dump``
   - ``ceph <tell|daemon> osd.<N> 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.
index 7807b64d990e5f89a756b3ec9cd8d1e9aea2a094..33ab7a7a53b774e17b37b1554391b311e364756a 100644 (file)
@@ -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``.
index 5612b9f994fdfb89217f2a2d974f8236ae265178..797325d6e874ed3f00e0a944d98315b91dcfd352 100644 (file)
@@ -1014,6 +1014,28 @@ std::vector<mds_gid_t> 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<string> &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,
index 9b426f02584a4182d88304698ec45a85ac66b5cb..84e98de69843200a35cef590e596569bf5637f43 100644 (file)
@@ -244,6 +244,29 @@ public:
 
   const CompatSet &get_compat() const {return compat;}
 
+  void filter(const std::vector<string>& 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<string>& 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<string> &filter) const;
+
   int parse_role(
       std::string_view role_str,
       mds_role_t *role,
index 28a2fd015598223a8503309f4909e9c6ed55111e..f92a76cabecfaa7d00ae11eed68b0ba18e68b01d 100644 (file)
@@ -64,7 +64,7 @@ class FlagSetHandler : public FileSystemCommandHandler
 
   int handle(
       Monitor *mon,
-      FSMap &fsmap,
+      FSMapfsmap,
       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,
+      FSMapfsmap,
       MonOpRequestRef op,
       const cmdmap_t& cmdmap,
       std::stringstream &ss) override
@@ -323,7 +319,7 @@ public:
 
   int handle(
       Monitor *mon,
-      FSMap &fsmap,
+      FSMapfsmap,
       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,
+      FSMapfsmap,
       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,
+      FSMapfsmap,
       MonOpRequestRef op,
       const cmdmap_t& cmdmap,
       std::stringstream &ss) override
@@ -844,7 +831,7 @@ class RemoveFilesystemHandler : public FileSystemCommandHandler
 
   int handle(
       Monitor *mon,
-      FSMap &fsmap,
+      FSMapfsmap,
       MonOpRequestRef op,
       const cmdmap_t& cmdmap,
       std::stringstream &ss) override
@@ -918,7 +905,7 @@ class ResetFilesystemHandler : public FileSystemCommandHandler
 
   int handle(
       Monitor *mon,
-      FSMap &fsmap,
+      FSMapfsmap,
       MonOpRequestRef op,
       const cmdmap_t& cmdmap,
       std::stringstream &ss) override
@@ -963,7 +950,7 @@ class RemoveDataPoolHandler : public FileSystemCommandHandler
 
   int handle(
       Monitor *mon,
-      FSMap &fsmap,
+      FSMapfsmap,
       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<Filesystem> 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,
+      FSMapfsmap,
       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;
+}
index 66e0286a465fdfa132bcdde28371abc113ff0453..726ff281a1bc583284ed2b3a0e5f7ab851db1ef4 100644 (file)
@@ -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<std::shared_ptr<FileSystemCommandHandler> > load(Paxos *paxos);
index 2a8e2742e8632dc5f1c8a4296584c97e0955a717..4010f1ee39febe3801e8db4af40f69e3464b3f55 100644 (file)
@@ -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);
index 02dec34d30f88775cab1df5f017e91c47177927d..45be5106672d29fbc5082c432137ce5ca409557b 100644 (file)
@@ -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);
index b550ad7dd3ec1995258c93eafa174af6debb7b99..f130f95bcc4c3bb849b34412cd7bc106e3fa7976 100644 (file)
@@ -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<Iterator, MonCap()>
     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<Iterator, MonCap()>
                          >> qi::attr(string()) >> qi::attr(string()) >> qi::attr(string())
                          >> qi::attr(map<string,StringConstraint>())
                          >> 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<Iterator, MonCap()>
   qi::rule<Iterator, string()> quoted_string;
   qi::rule<Iterator, string()> unquoted_word;
   qi::rule<Iterator, string()> str, network_str;
+  qi::rule<Iterator, string()> fs_name_str;
 
   qi::rule<Iterator, StringConstraint()> str_match, str_prefix, str_regex;
   qi::rule<Iterator, pair<string, StringConstraint>()> kv_pair;
index edc1bda51a1f96b0dd2732b053119c5544ac232a..d38ffc54be7780d98aafcb9615474faeb18b1da9 100644 (file)
@@ -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<std::string, StringConstraint> 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<MonCap*>& ls);
+
+  std::vector<string> allowed_fs_names() const {
+    std::vector<string> 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)
 
index 51d5c12c41e58a4c2f0d5e5eb159e27cf1f6547c..5df77ebc2bae30764c2a13154f70a2b866b93241 100644 (file)
@@ -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);
index d8db8bf01043b24b5a8d08a4ab94c304bfda9834..b42ba927a9d9ca77d3f575b7489969d76c3be4d0 100644 (file)
@@ -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);
index 4ca4814d70050e83039904d3eba97653d5c761dd..0502dcc77a9231e16eccbc6f629bc8b9b3b969c6 100644 (file)
@@ -106,6 +106,14 @@ struct MonSession : public RefCountedObject {
       get_peer_socket_addr());
   }
 
+  std::vector<string> 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;
   }