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``.
+
+MDS communication restriction
+=============================
+
+By default, user applications may communicate with any MDS, whether or not
+they are allowed to modify data on an associated file system (see
+`Path restriction` above). Client's communication can be restricted to MDS
+daemons associated with particular file system(s) by adding MDS caps for that
+particular file system. Consider the following example where 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 ]
+
+Client ``someuser`` is authorized only for one FS::
+
+ $ ceph fs authorize cephfs client.someuser / rw
+ [client.someuser]
+ key = AQBPSARfg8hCJRAAEegIxjlm7VkHuiuntm6wsA==
+ $ ceph auth get client.someuser > ceph.client.someuser.keyring
+ exported keyring for client.someuser
+ $ cat ceph.client.someuser.keyring
+ [client.someuser]
+ key = AQBPSARfg8hCJRAAEegIxjlm7VkHuiuntm6wsA==
+ caps mds = "allow rw fsname=cephfs"
+ caps mon = "allow r"
+ caps osd = "allow rw tag cephfs data=cephfs"
+
+Mounting ``cephfs1`` with ``someuser`` works::
+
+ $ sudo ceph-fuse /mnt/cephfs1 -n client.someuser -k ceph.client.someuser.keyring --client-fs=cephfs
+ ceph-fuse[96634]: starting ceph client
+ ceph-fuse[96634]: starting fuse
+ $ mount | grep ceph-fuse
+ ceph-fuse on /mnt/cephfs1 type fuse.ceph-fuse (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other)
+
+But mounting ``cephfs2`` does not::
+
+ $ sudo ceph-fuse /mnt/cephfs2 -n client.someuser -k ceph.client.someuser.keyring --client-fs=cephfs2
+ ceph-fuse[96599]: starting ceph client
+ ceph-fuse[96599]: ceph mount failed with (1) Operation not permitted
#include "common/debug.h"
#include "MDSAuthCaps.h"
+#include "mdstypes.h"
#include "include/ipaddr.h"
#define dout_subsys ceph_subsys_mds
lexeme[lit("'") >> *(char_ - '\'') >> '\''];
unquoted_path %= +char_("a-zA-Z0-9_./-");
network_str %= +char_("/.:a-fA-F0-9][");
+ fs_name_str %= +char_("a-zA-Z0-9-_.");
// match := [path=<path>] [uid=<uid> [gids=<gid>[,<gid>...]]
path %= (spaces >> lit("path") >> lit('=') >> (quoted_path | unquoted_path));
uid %= (spaces >> lit("uid") >> lit('=') >> uint_);
uintlist %= (uint_ % lit(','));
gidlist %= -(spaces >> lit("gids") >> lit('=') >> uintlist);
+ fs_name %= -(spaces >> lit("fsname") >> lit('=') >> fs_name_str);
match = -(
(uid >> gidlist)[_val = phoenix::construct<MDSCapMatch>(_1, _2)] |
(path >> uid >> gidlist)[_val = phoenix::construct<MDSCapMatch>(_1, _2, _3)] |
- (path)[_val = phoenix::construct<MDSCapMatch>(_1)]);
+ (path)[_val = phoenix::construct<MDSCapMatch>(_1)] |
+ (fs_name)[_val = phoenix::construct<MDSCapMatch>(std::string(),
+ _1)]);
// capspec = * | r[w][p][s]
capspec = spaces >> (
}
qi::rule<Iterator> spaces;
qi::rule<Iterator, string()> quoted_path, unquoted_path, network_str;
+ qi::rule<Iterator, string()> fs_name_str, fs_name, path;
qi::rule<Iterator, MDSCapSpec()> capspec;
- qi::rule<Iterator, string()> path;
qi::rule<Iterator, uint32_t()> uid;
qi::rule<Iterator, std::vector<uint32_t>() > uintlist;
qi::rule<Iterator, std::vector<uint32_t>() > gidlist;
#include "include/types.h"
#include "common/debug.h"
+#include "mdstypes.h"
+
// unix-style capabilities
enum {
MAY_READ = (1 << 0),
struct MDSCapMatch {
static const int64_t MDS_AUTH_UID_ANY = -1;
- MDSCapMatch() : uid(MDS_AUTH_UID_ANY) {}
- MDSCapMatch(int64_t uid_, std::vector<gid_t>& gids_) : uid(uid_), gids(gids_) {}
+ MDSCapMatch() : uid(MDS_AUTH_UID_ANY), fs_name(std::string()) {}
+
+ MDSCapMatch(int64_t uid_, std::vector<gid_t>& gids_) :
+ uid(uid_), gids(gids_), fs_name(std::string()) {}
+
explicit MDSCapMatch(const std::string &path_)
- : uid(MDS_AUTH_UID_ANY), path(path_) {
+ : uid(MDS_AUTH_UID_ANY), path(path_), fs_name(std::string()) {
+ normalize_path();
+ }
+
+ explicit MDSCapMatch(std::string path, std::string fs_name) :
+ uid(MDS_AUTH_UID_ANY), path(std::move(path)), fs_name(std::move(fs_name))
+ {
normalize_path();
}
+
MDSCapMatch(const std::string& path_, int64_t uid_, std::vector<gid_t>& gids_)
- : uid(uid_), gids(gids_), path(path_) {
+ : uid(uid_), gids(gids_), path(path_), fs_name(std::string()) {
normalize_path();
}
int64_t uid; // Require UID to be equal to this, if !=MDS_AUTH_UID_ANY
std::vector<gid_t> gids; // Use these GIDs
std::string path; // Require path to be child of this (may be "" or "/" for any)
+ std::string fs_name;
};
struct MDSCapGrant {
const entity_addr_t& addr) const;
bool path_capable(std::string_view inode_path) const;
+ bool fs_name_capable(std::string_view fs_name, unsigned mask) const {
+ if (allow_all()) {
+ return true;
+ }
+
+ for (const MDSCapGrant &g : grants) {
+ if (g.match.fs_name == fs_name || g.match.fs_name.empty() ||
+ g.match.fs_name == "*") {
+ if (mask & MAY_READ && g.spec.allow_read()) {
+ return true;
+ }
+
+ if (mask & MAY_WRITE && g.spec.allow_write()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
friend std::ostream &operator<<(std::ostream &out, const MDSAuthCaps &cap);
private:
CephContext *cct = nullptr;
return;
}
+ std::string_view fs_name = mds->get_fs_name();
+ if (!fs_name.empty() && !session->fs_name_capable(fs_name, MAY_READ)) {
+ dout(0) << " dropping message not allowed for this fs_name: " << *m << dendl;
+ return;
+ }
+
if (mds->get_state() < MDSMap::STATE_CLIENTREPLAY) {
mds->wait_for_replay(new C_MDS_RetryMessage(mds, m));
return;
return;
}
+ std::string_view fs_name = mds->get_fs_name();
+ if (!fs_name.empty() && !session->fs_name_capable(fs_name, MAY_READ)) {
+ dout(0) << " dropping message not allowed for this fs_name: " << *m << dendl;
+ auto reply = make_message<MClientSession>(CEPH_SESSION_REJECT);
+ reply->metadata["error_string"] = "client doesn't have caps for FS \"" +
+ std::string(fs_name) + "\"";
+ mds->send_message(std::move(reply), m->get_connection());
+ return;
+ }
+
if (m->get_op() == CEPH_SESSION_REQUEST_RENEWCAPS) {
// always handle renewcaps (state >= MDSMap::STATE_RECONNECT)
} else if (m->get_op() == CEPH_SESSION_REQUEST_CLOSE) {
int check_access(CInode *in, unsigned mask, int caller_uid, int caller_gid,
const std::vector<uint64_t> *gid_list, int new_uid, int new_gid);
+ bool fs_name_capable(std::string_view fs_name, unsigned mask) const {
+ return auth_caps.fs_name_capable(fs_name, mask);
+ }
+
void set_connection(ConnectionRef con) {
connection = std::move(con);
auto& c = connection;