* 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'``
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
(path >> uid >> gidlist)[_val = phoenix::construct<MDSCapMatch>(_1, _2, _3)] |
(path)[_val = phoenix::construct<MDSCapMatch>(_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<MDSCapGrant>(_1, _2)];
// 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;
}
}
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)
// 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;
}
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;
// 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
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
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
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
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() ? "" : ", ";
};
CapsTest test_values[] = {
{"allow",
- "MDSAuthCaps[allow rw]"},
+ "MDSAuthCaps[allow rwps]"},
{"allow *",
"MDSAuthCaps[allow *]"},
{"allow r",