From: Douglas Fuller Date: Thu, 7 Mar 2019 15:17:23 +0000 (-0500) Subject: cephfs: add auth caps based on fs names X-Git-Tag: v16.1.0~1118^2~12 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=91d5715be6996629fb912a7dd1e52b754dd6e7fd;p=ceph.git cephfs: add auth caps based on fs names Add new auth caps to restrict access to clients based on fsnames. To specify this, for example: mds 'allow rw fsname=cephfs1' This will restrict client access to fs name "cephfs1" only. Messages to active MDS assigned to any other FSMap will be dropped. Standby MDS not associated with an FSMap will accept messages from clients. To allow multiple file systems, create MDS cap as follows - mds 'allow rw fsname=cephfs1, allow rw fsname=cephfs2' Fixes: http://tracker.ceph.com/issues/15070 Signed-off-by: Douglas Fuller Signed-off-by: Rishabh Dave --- diff --git a/doc/cephfs/client-auth.rst b/doc/cephfs/client-auth.rst index 33ab7a7a53b7..dc2e9d331847 100644 --- a/doc/cephfs/client-auth.rst +++ b/doc/cephfs/client-auth.rst @@ -184,3 +184,45 @@ And the client can only see the FS that it has authorization for:: 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 diff --git a/src/mds/MDSAuthCaps.cc b/src/mds/MDSAuthCaps.cc index 3f0c04da9a9e..40db4c1ec1ad 100644 --- a/src/mds/MDSAuthCaps.cc +++ b/src/mds/MDSAuthCaps.cc @@ -23,6 +23,7 @@ #include "common/debug.h" #include "MDSAuthCaps.h" +#include "mdstypes.h" #include "include/ipaddr.h" #define dout_subsys ceph_subsys_mds @@ -61,16 +62,20 @@ struct MDSCapParser : qi::grammar 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=] [uid= [gids=[,...]] 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(_1, _2)] | (path >> uid >> gidlist)[_val = phoenix::construct(_1, _2, _3)] | - (path)[_val = phoenix::construct(_1)]); + (path)[_val = phoenix::construct(_1)] | + (fs_name)[_val = phoenix::construct(std::string(), + _1)]); // capspec = * | r[w][p][s] capspec = spaces >> ( @@ -97,8 +102,8 @@ struct MDSCapParser : qi::grammar } qi::rule spaces; qi::rule quoted_path, unquoted_path, network_str; + qi::rule fs_name_str, fs_name, path; qi::rule capspec; - qi::rule path; qi::rule uid; qi::rule() > uintlist; qi::rule() > gidlist; diff --git a/src/mds/MDSAuthCaps.h b/src/mds/MDSAuthCaps.h index 940d474a8468..06d6c7190f4b 100644 --- a/src/mds/MDSAuthCaps.h +++ b/src/mds/MDSAuthCaps.h @@ -23,6 +23,8 @@ #include "include/types.h" #include "common/debug.h" +#include "mdstypes.h" + // unix-style capabilities enum { MAY_READ = (1 << 0), @@ -89,14 +91,24 @@ private: struct MDSCapMatch { static const int64_t MDS_AUTH_UID_ANY = -1; - MDSCapMatch() : uid(MDS_AUTH_UID_ANY) {} - MDSCapMatch(int64_t uid_, std::vector& gids_) : uid(uid_), gids(gids_) {} + MDSCapMatch() : uid(MDS_AUTH_UID_ANY), fs_name(std::string()) {} + + MDSCapMatch(int64_t uid_, std::vector& 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& gids_) - : uid(uid_), gids(gids_), path(path_) { + : uid(uid_), gids(gids_), path(path_), fs_name(std::string()) { normalize_path(); } @@ -124,6 +136,7 @@ struct MDSCapMatch { int64_t uid; // Require UID to be equal to this, if !=MDS_AUTH_UID_ANY std::vector 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 { @@ -173,6 +186,27 @@ public: 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; diff --git a/src/mds/Server.cc b/src/mds/Server.cc index 515f96bbeba6..6b534930b19d 100644 --- a/src/mds/Server.cc +++ b/src/mds/Server.cc @@ -494,6 +494,12 @@ void Server::handle_client_reclaim(const cref_t &m) 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; @@ -522,6 +528,16 @@ void Server::handle_client_session(const cref_t &m) 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(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) { diff --git a/src/mds/SessionMap.h b/src/mds/SessionMap.h index 01e13266ea86..ffa7973a2415 100644 --- a/src/mds/SessionMap.h +++ b/src/mds/SessionMap.h @@ -385,6 +385,10 @@ public: int check_access(CInode *in, unsigned mask, int caller_uid, int caller_gid, const std::vector *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;