From 0bb08cf3079bbf36bc4bf52e7ea5cebf701dc919 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Thu, 14 Jun 2018 17:21:59 +0800 Subject: [PATCH] mds: add MAY_SNAPSHOT in MDSAuthCaps For controlling whether a client is allowed to create or delete snapshots Fixes: http://tracker.ceph.com/issues/24284 Signed-off-by: "Yan, Zheng" --- PendingReleaseNotes | 7 ++++ doc/cephfs/client-auth.rst | 19 ++++++++++ src/mds/MDSAuthCaps.cc | 42 +++++++++++++++-------- src/mds/MDSAuthCaps.h | 61 ++++++++++++++++++++++----------- src/mds/Server.cc | 6 ++-- src/mon/AuthMonitor.cc | 26 ++++++++++++-- src/test/mds/TestMDSAuthCaps.cc | 2 +- 7 files changed, 122 insertions(+), 41 deletions(-) diff --git a/PendingReleaseNotes b/PendingReleaseNotes index 2761c198e7f4f..e06be58747216 100644 --- a/PendingReleaseNotes +++ b/PendingReleaseNotes @@ -18,3 +18,10 @@ * The JSON output of the ``osd find`` command has replaced the ``ip`` field with an ``addrs`` section to reflect that OSDs may bind to multiple addresses. + +* CephFS clients without the 's' flag in their authentication capability + string will no longer be able to create/delete snapshots. To allow + ``client.foo`` to create/delete snapshots in the ``bar`` directory of + filesystem ``cephfs_a``, use command: + + - ``ceph auth caps client.foo mon 'allow r' osd 'allow rw tag cephfs data=cephfs_a' mds 'allow rw, allow rws path=/bar'`` diff --git a/doc/cephfs/client-auth.rst b/doc/cephfs/client-auth.rst index 78dd81fe970d4..851da5246e466 100644 --- a/doc/cephfs/client-auth.rst +++ b/doc/cephfs/client-auth.rst @@ -110,4 +110,23 @@ on the filesystem cephfs_a, but client.1 cannot. caps: [osd] allow rw tag cephfs data=cephfs_a +Snapshot restriction (the 's' flag) +=========================================== + +To create or delete snapshots, clients require the 's' flag in addition to 'rw'. +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 filesystem ``cephfs_a``. + +:: + + client.0 + key: AQAz7EVWygILFRAAdIcuJ12opU/JKyfFmxhuaw== + caps: [mds] allow rw, allow rws path=/bar + caps: [mon] allow r + caps: [osd] allow rw tag cephfs data=cephfs_a + + .. _User Management - Add a User to a Keyring: ../../rados/operations/user-management/#add-a-user-to-a-keyring diff --git a/src/mds/MDSAuthCaps.cc b/src/mds/MDSAuthCaps.cc index aeb86d8851d90..13ab77ee1f91b 100644 --- a/src/mds/MDSAuthCaps.cc +++ b/src/mds/MDSAuthCaps.cc @@ -69,17 +69,21 @@ struct MDSCapParser : qi::grammar (path >> uid >> gidlist)[_val = phoenix::construct(_1, _2, _3)] | (path)[_val = phoenix::construct(_1)]); - // capspec = * | r[w] + // capspec = * | r[w][p][s] capspec = spaces >> ( - lit("*")[_val = MDSCapSpec(true, true, true, true)] + lit("*")[_val = MDSCapSpec(MDSCapSpec::ALL)] | - lit("all")[_val = MDSCapSpec(true, true, true, true)] + lit("all")[_val = MDSCapSpec(MDSCapSpec::ALL)] | - (lit("rwp"))[_val = MDSCapSpec(true, true, false, true)] + (lit("rwps"))[_val = MDSCapSpec(MDSCapSpec::RWPS)] | - (lit("rw"))[_val = MDSCapSpec(true, true, false, false)] + (lit("rwp"))[_val = MDSCapSpec(MDSCapSpec::RWP)] | - (lit("r"))[_val = MDSCapSpec(true, false, false, false)] + (lit("rws"))[_val = MDSCapSpec(MDSCapSpec::RWS)] + | + (lit("rw"))[_val = MDSCapSpec(MDSCapSpec::RW)] + | + (lit("r"))[_val = MDSCapSpec(MDSCapSpec::READ)] ); grant = lit("allow") >> (capspec >> match)[_val = phoenix::construct(_1, _2)]; @@ -221,7 +225,13 @@ bool MDSAuthCaps::is_capable(std::string_view inode_path, // Spec is non-allowing if caller asked for set pool but spec forbids it if (mask & MAY_SET_VXATTR) { - if (!i->spec.allows_set_vxattr()) { + if (!i->spec.allow_set_vxattr()) { + continue; + } + } + + if (mask & MAY_SNAPSHOT) { + if (!i->spec.allow_snapshot()) { continue; } } @@ -276,9 +286,7 @@ bool MDSAuthCaps::is_capable(std::string_view inode_path, void MDSAuthCaps::set_allow_all() { grants.clear(); - grants.push_back(MDSCapGrant( - MDSCapSpec(true, true, true, true), - MDSCapMatch())); + grants.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::ALL), MDSCapMatch())); } bool MDSAuthCaps::parse(CephContext *c, std::string_view str, ostream *err) @@ -286,7 +294,7 @@ bool MDSAuthCaps::parse(CephContext *c, std::string_view str, ostream *err) // Special case for legacy caps if (str == "allow") { grants.clear(); - grants.push_back(MDSCapGrant(MDSCapSpec(true, true, false, true), MDSCapMatch())); + grants.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::RWPS), MDSCapMatch())); return true; } @@ -353,15 +361,21 @@ ostream &operator<<(ostream &out, const MDSCapMatch &match) ostream &operator<<(ostream &out, const MDSCapSpec &spec) { - if (spec.any) { + if (spec.allow_all()) { out << "*"; } else { - if (spec.read) { + if (spec.allow_read()) { out << "r"; } - if (spec.write) { + if (spec.allow_write()) { out << "w"; } + if (spec.allow_set_vxattr()) { + out << "p"; + } + if (spec.allow_snapshot()) { + out << "s"; + } } return out; diff --git a/src/mds/MDSAuthCaps.h b/src/mds/MDSAuthCaps.h index b867da4f0f9d8..d7c3a66ac8610 100644 --- a/src/mds/MDSAuthCaps.h +++ b/src/mds/MDSAuthCaps.h @@ -26,44 +26,65 @@ // unix-style capabilities enum { - MAY_READ = 1, - MAY_WRITE = 2, - MAY_EXECUTE = 4, - MAY_CHOWN = 16, - MAY_CHGRP = 32, - MAY_SET_VXATTR = 64, + MAY_READ = (1 << 0), + MAY_WRITE = (1 << 1), + MAY_EXECUTE = (1 << 2), + MAY_CHOWN = (1 << 4), + MAY_CHGRP = (1 << 5), + MAY_SET_VXATTR = (1 << 6), + MAY_SNAPSHOT = (1 << 7), }; class CephContext; // what we can do struct MDSCapSpec { - bool read, write, any; - - // True if the capability permits setting vxattrs (layout, quota, etc) - bool set_vxattr; - - MDSCapSpec() : read(false), write(false), any(false), set_vxattr(false) {} - MDSCapSpec(bool r, bool w, bool a, bool lop) - : read(r), write(w), any(a), set_vxattr(lop) {} + static const unsigned ALL = (1 << 0); + static const unsigned READ = (1 << 1); + static const unsigned WRITE = (1 << 2); + // if the capability permits setting vxattrs (layout, quota, etc) + static const unsigned SET_VXATTR = (1 << 3); + // if the capability permits mksnap/rmsnap + static const unsigned SNAPSHOT = (1 << 4); + + static const unsigned RW = (READ|WRITE); + static const unsigned RWP = (READ|WRITE|SET_VXATTR); + static const unsigned RWS = (READ|WRITE|SNAPSHOT); + static const unsigned RWPS = (READ|WRITE|SET_VXATTR|SNAPSHOT); + + MDSCapSpec(unsigned _caps=0) : caps(_caps) { + if (caps & ALL) + caps |= RWPS; + } bool allow_all() const { - return any; + return (caps & ALL); + } + bool allow_read() const { + return (caps & READ); + } + bool allow_write() const { + return (caps & WRITE); } bool allows(bool r, bool w) const { - if (any) + if (allow_all()) return true; - if (r && !read) + if (r && !allow_read()) return false; - if (w && !write) + if (w && !allow_write()) return false; return true; } - bool allows_set_vxattr() const { - return set_vxattr; + bool allow_snapshot() const { + return (caps & SNAPSHOT); + } + bool allow_set_vxattr() const { + return (caps & SET_VXATTR); } +private: + unsigned caps; }; // conditions before we are allowed to do it diff --git a/src/mds/Server.cc b/src/mds/Server.cc index 9a0aebeef2b26..770a465199d07 100644 --- a/src/mds/Server.cc +++ b/src/mds/Server.cc @@ -9116,7 +9116,7 @@ void Server::handle_client_mksnap(MDRequestRef& mdr) if (!mds->locker->acquire_locks(mdr, rdlocks, wrlocks, xlocks)) return; - if (!check_access(mdr, diri, MAY_WRITE)) + if (!check_access(mdr, diri, MAY_WRITE|MAY_SNAPSHOT)) return; // make sure name is unique @@ -9273,7 +9273,7 @@ void Server::handle_client_rmsnap(MDRequestRef& mdr) if (!mds->locker->acquire_locks(mdr, rdlocks, wrlocks, xlocks)) return; - if (!check_access(mdr, diri, MAY_WRITE)) + if (!check_access(mdr, diri, MAY_WRITE|MAY_SNAPSHOT)) return; // prepare @@ -9420,7 +9420,7 @@ void Server::handle_client_renamesnap(MDRequestRef& mdr) if (!mds->locker->acquire_locks(mdr, rdlocks, wrlocks, xlocks)) return; - if (!check_access(mdr, diri, MAY_WRITE)) + if (!check_access(mdr, diri, MAY_WRITE|MAY_SNAPSHOT)) return; // prepare diff --git a/src/mon/AuthMonitor.cc b/src/mon/AuthMonitor.cc index 9880681ee2330..5cf37eb9caf6c 100644 --- a/src/mon/AuthMonitor.cc +++ b/src/mon/AuthMonitor.cc @@ -1413,13 +1413,33 @@ bool AuthMonitor::prepare_command(MonOpRequestRef op) it += 2) { const string &path = *it; const string &cap = *(it+1); - if (cap != "r" && cap != "rw" && cap != "rwp") { - ss << "Only 'r', 'rw', and 'rwp' permissions are allowed for filesystems."; + + if (cap != "r" && cap.compare(0, 2, "rw")) { + ss << "Permission flags must start with 'r' or 'rw'."; err = -EINVAL; goto done; } - if (cap.find('w') != string::npos) { + if (cap.compare(0, 2, "rw") == 0) osd_cap_wanted = "rw"; + + char last='\0'; + for (size_t i = 2; i < cap.size(); ++i) { + char c = cap.at(i); + if (last >= c) { + ss << "Permission flags (except 'rw') must be specified in alphabetical order."; + err = -EINVAL; + goto done; + } + switch (c) { + case 'p': + break; + case 's': + break; + default: + ss << "Unknown permission flag '" << c << "'."; + err = -EINVAL; + goto done; + } } mds_cap_string += mds_cap_string.empty() ? "" : ", "; diff --git a/src/test/mds/TestMDSAuthCaps.cc b/src/test/mds/TestMDSAuthCaps.cc index fabb785789505..59e9fb87603cb 100644 --- a/src/test/mds/TestMDSAuthCaps.cc +++ b/src/test/mds/TestMDSAuthCaps.cc @@ -235,7 +235,7 @@ TEST(MDSAuthCaps, OutputParsed) { }; CapsTest test_values[] = { {"allow", - "MDSAuthCaps[allow rw]"}, + "MDSAuthCaps[allow rwps]"}, {"allow *", "MDSAuthCaps[allow *]"}, {"allow r", -- 2.39.5