]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/osd/OSDCap.cc
import ceph quincy 17.2.1
[ceph.git] / ceph / src / osd / OSDCap.cc
index 25da5e2b69a5dd76f6115a638fffa5bc8686e291..e7bf05827993a6369e02d5fb365668b646e6f513 100644 (file)
 #include <boost/spirit/include/qi.hpp>
 #include <boost/spirit/include/phoenix_operator.hpp>
 #include <boost/spirit/include/phoenix.hpp>
+#include <boost/algorithm/string/predicate.hpp>
 
 #include "OSDCap.h"
 #include "common/config.h"
 #include "common/debug.h"
+#include "include/ipaddr.h"
 
 using std::ostream;
+using std::string;
 using std::vector;
 
 ostream& operator<<(ostream& out, const osd_rwxa_t& p)
-{ 
+{
   if (p == OSD_CAP_ANY)
     return out << "*";
 
@@ -48,47 +51,136 @@ ostream& operator<<(ostream& out, const OSDCapSpec& s)
 {
   if (s.allow)
     return out << s.allow;
-  if (s.class_name.length())
-    return out << "class '" << s.class_name << "' '" << s.class_allow << "'";
+  if (s.class_name.length()) {
+    out << "class '" << s.class_name << "'";
+    if (!s.method_name.empty()) {
+      out << " '" << s.method_name << "'";
+    }
+  }
   return out;
 }
 
-ostream& operator<<(ostream& out, const OSDCapMatch& m)
+ostream& operator<<(ostream& out, const OSDCapPoolNamespace& pns)
 {
-  if (m.auid != -1LL) {
-    out << "auid " << m.auid << " ";
+  if (!pns.pool_name.empty()) {
+    out << "pool " << pns.pool_name << " ";
   }
-  if (m.object_prefix.length()) {
-    out << "object_prefix " << m.object_prefix << " ";
-  }
-  if (m.pool_name.length()) {
-    out << "pool " << m.pool_name << " ";
-  }
-  if (m.is_nspace) {
+  if (pns.nspace) {
     out << "namespace ";
-    if (m.nspace.length() == 0)
+    if (pns.nspace->empty()) {
       out << "\"\"";
-    else
-      out << m.nspace;
+    } else {
+      out << *pns.nspace;
+    }
     out << " ";
   }
   return out;
 }
 
-bool OSDCapMatch::is_match(const string& pn, const string& ns, int64_t pool_auid, const string& object) const
+ostream& operator<<(ostream &out, const OSDCapPoolTag &pt)
 {
-  if (auid >= 0) {
-    if (auid != pool_auid)
-      return false;
+  out << "app " << pt.application << " key " << pt.key << " val " << pt.value
+      << " ";
+  return out;
+}
+
+ostream& operator<<(ostream& out, const OSDCapMatch& m)
+{
+  if (!m.pool_namespace.pool_name.empty() || m.pool_namespace.nspace) {
+    out << m.pool_namespace;
+  }
+
+  if (!m.pool_tag.application.empty()) {
+    out << m.pool_tag;
   }
-  if (pool_name.length()) {
-    if (pool_name != pn)
+
+  if (m.object_prefix.length()) {
+    out << "object_prefix " << m.object_prefix << " ";
+  }
+  return out;
+}
+
+ostream& operator<<(ostream& out, const OSDCapProfile& m)
+{
+  out << "profile " << m.name;
+  out << m.pool_namespace;
+  return out;
+}
+
+bool OSDCapPoolNamespace::is_match(const std::string& pn,
+                                   const std::string& ns) const
+{
+  if (!pool_name.empty()) {
+    if (pool_name != pn) {
       return false;
+    }
   }
-  if (is_nspace) {
-    if (nspace != ns)
+  if (nspace) {
+    if (!nspace->empty() && nspace->back() == '*' &&
+       boost::starts_with(ns, nspace->substr(0, nspace->length() - 1))) {
+      return true;
+    }
+
+    if (*nspace != ns) {
       return false;
+    }
+  }
+  return true;
+}
+
+bool OSDCapPoolNamespace::is_match_all() const
+{
+  if (!pool_name.empty())
+    return false;
+  if (nspace)
+    return false;
+  return true;
+}
+
+bool OSDCapPoolTag::is_match(const app_map_t& app_map) const
+{
+  if (application.empty()) {
+    return true;
+  }
+  auto kv_map = app_map.find(application);
+  if (kv_map == app_map.end()) {
+    return false;
+  }
+  if (!key.compare("*") && !value.compare("*")) {
+    return true;
+  }
+  if (!key.compare("*")) {
+    for (auto it : kv_map->second) {
+      if (it.second == value) {
+       return true;
+      }
+    }
+    return false;
+  }
+  auto kv_val = kv_map->second.find(key);
+  if (kv_val == kv_map->second.end()) {
+    return false;
+  }
+  if (!value.compare("*")) {
+    return true;
+  }
+  return kv_val->second == value;
+}
+
+bool OSDCapPoolTag::is_match_all() const {
+  return application.empty();
+}
+
+bool OSDCapMatch::is_match(const string& pn, const string& ns,
+                          const OSDCapPoolTag::app_map_t& app_map,
+                          const string& object) const
+{
+  if (!pool_namespace.is_match(pn, ns)) {
+    return false;
+  } else if (!pool_tag.is_match(app_map)) {
+    return false;
   }
+
   if (object_prefix.length()) {
     if (object.find(object_prefix) != 0)
       return false;
@@ -98,77 +190,126 @@ bool OSDCapMatch::is_match(const string& pn, const string& ns, int64_t pool_auid
 
 bool OSDCapMatch::is_match_all() const
 {
-  if (auid >= 0)
-    return false;
-  if (pool_name.length())
+if (!pool_namespace.is_match_all()) {
     return false;
-  if (is_nspace)
+  } else if (!pool_tag.is_match_all()) {
     return false;
-  if (object_prefix.length())
+  }
+
+  if (object_prefix.length()) {
     return false;
+  }
   return true;
 }
 
 ostream& operator<<(ostream& out, const OSDCapGrant& g)
 {
-  return out << "grant(" << g.match << g.spec << ")";
+  out << "grant(";
+  if (g.profile.is_valid()) {
+    out << g.profile << " [";
+    for (auto it = g.profile_grants.cbegin();
+         it != g.profile_grants.cend(); ++it) {
+      if (it != g.profile_grants.cbegin()) {
+        out << ",";
+      }
+      out << *it;
+    }
+    out << "]";
+  } else {
+    out << g.match << g.spec;
+  }
+  if (g.network.size()) {
+    out << " network " << g.network;
+  }
+  out << ")";
+  return out;
 }
 
-
-bool OSDCap::allow_all() const
+void OSDCapGrant::set_network(const string& n)
 {
-  for (vector<OSDCapGrant>::const_iterator p = grants.begin(); p != grants.end(); ++p)
-    if (p->match.is_match_all() && p->spec.allow_all())
-      return true;
-  return false;
+  network = n;
+  network_valid = ::parse_network(n.c_str(), &network_parsed, &network_prefix);
 }
 
-void OSDCap::set_allow_all()
+bool OSDCapGrant::allow_all() const
 {
-  grants.clear();
-  grants.push_back(OSDCapGrant(OSDCapMatch(), OSDCapSpec(OSD_CAP_ANY)));
+  if (profile.is_valid()) {
+    return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
+                       [](const OSDCapGrant& grant) {
+        return grant.allow_all();
+      });
+  }
+
+  return (match.is_match_all() && spec.allow_all());
 }
 
-bool OSDCap::is_capable(const string& pool_name, const string& ns, int64_t pool_auid,
-                       const string& object, bool op_may_read, bool op_may_write,
-                       const std::vector<OpRequest::ClassInfo>& classes) const
+bool OSDCapGrant::is_capable(
+  const string& pool_name,
+  const string& ns,
+  const OSDCapPoolTag::app_map_t& application_metadata,
+  const string& object,
+  bool op_may_read,
+  bool op_may_write,
+  const std::vector<OpInfo::ClassInfo>& classes,
+  const entity_addr_t& addr,
+  std::vector<bool>* class_allowed) const
 {
-  const size_t num_classes = classes.size();
-  std::vector<bool> class_allowed(num_classes, false);
   osd_rwxa_t allow = 0;
-  for (vector<OSDCapGrant>::const_iterator p = grants.begin();
-       p != grants.end(); ++p) {
-    if (p->match.is_match(pool_name, ns, pool_auid, object)) {
-      allow = allow | p->spec.allow;
+
+  if (network.size() &&
+      (!network_valid ||
+       !network_contains(network_parsed,
+                        network_prefix,
+                        addr))) {
+    return false;
+  }
+
+  if (profile.is_valid()) {
+    return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
+                       [&](const OSDCapGrant& grant) {
+                          return grant.is_capable(pool_name, ns,
+                                                  application_metadata,
+                                                  object, op_may_read,
+                                                  op_may_write, classes, addr,
+                                                  class_allowed);
+                      });
+  } else {
+    if (match.is_match(pool_name, ns, application_metadata, object)) {
+      allow = allow | spec.allow;
       if ((op_may_read && !(allow & OSD_CAP_R)) ||
-          (op_may_write && !(allow & OSD_CAP_W)))
-        continue;
-      if (num_classes) {
+          (op_may_write && !(allow & OSD_CAP_W))) {
+        return false;
+      }
+      if (!classes.empty()) {
         // check 'allow *'
-        if (p->spec.allow_all())
+        if (spec.allow_all()) {
           return true;
+        }
+
         // compare this grant to each class in the operation
-        for (size_t i = 0; i < num_classes; i++) {
-          // check 'allow class foo'
-          if (classes[i].name == p->spec.class_name &&
-              !p->spec.class_name.empty()) {
-            class_allowed[i] = true;
+        for (size_t i = 0; i < classes.size(); ++i) {
+          // check 'allow class foo [method_name]'
+          if (!spec.class_name.empty() &&
+              classes[i].class_name == spec.class_name &&
+              (spec.method_name.empty() ||
+               classes[i].method_name == spec.method_name)) {
+            (*class_allowed)[i] = true;
             continue;
           }
-          // check 'allow x | class-{rw}': must be on whitelist
-          if (!classes[i].whitelisted)
+          // check 'allow x | class-{rw}': must be on allow list
+          if (!classes[i].allowed) {
             continue;
+          }
           if ((classes[i].read && !(allow & OSD_CAP_CLS_R)) ||
               (classes[i].write && !(allow & OSD_CAP_CLS_W))) {
             continue;
           }
-          class_allowed[i] = true;
+          (*class_allowed)[i] = true;
+        }
+        if (!std::all_of(class_allowed->cbegin(), class_allowed->cend(),
+              [](bool v) { return v; })) {
+          return false;
         }
-        if (std::all_of(class_allowed.cbegin(), class_allowed.cend(),
-              [](bool v) { return v; }))
-          return true;
-        else
-          continue;
       }
       return true;
     }
@@ -176,6 +317,83 @@ bool OSDCap::is_capable(const string& pool_name, const string& ns, int64_t pool_
   return false;
 }
 
+void OSDCapGrant::expand_profile()
+{
+  if (profile.name == "read-only") {
+    // grants READ-ONLY caps to the OSD
+    profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
+                                OSDCapSpec(osd_rwxa_t(OSD_CAP_R)));
+    return;
+  }
+  if (profile.name == "read-write") {
+    // grants READ-WRITE caps to the OSD
+    profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
+                                OSDCapSpec(osd_rwxa_t(OSD_CAP_R | OSD_CAP_W)));
+  }
+
+  if (profile.name == "rbd") {
+    // RBD read-write grant
+    profile_grants.emplace_back(OSDCapMatch(string(), "rbd_info"),
+                                OSDCapSpec(osd_rwxa_t(OSD_CAP_R)));
+    profile_grants.emplace_back(OSDCapMatch(string(), "rbd_children"),
+                                OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
+    profile_grants.emplace_back(OSDCapMatch(string(), "rbd_mirroring"),
+                                OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
+    profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace.pool_name),
+                                OSDCapSpec("rbd", "metadata_list"));
+    profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
+                                OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
+                                                      OSD_CAP_W |
+                                                      OSD_CAP_X)));
+  }
+  if (profile.name == "rbd-read-only") {
+    // RBD read-only grant
+    profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
+                                OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
+                                                      OSD_CAP_CLS_R)));
+    profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace,
+                                            "rbd_header."),
+                                OSDCapSpec("rbd", "child_attach"));
+    profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace,
+                                            "rbd_header."),
+                                OSDCapSpec("rbd", "child_detach"));
+  }
+}
+
+bool OSDCap::allow_all() const
+{
+  for (auto &grant : grants) {
+    if (grant.allow_all()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void OSDCap::set_allow_all()
+{
+  grants.clear();
+  grants.push_back(OSDCapGrant(OSDCapMatch(), OSDCapSpec(OSD_CAP_ANY)));
+}
+
+bool OSDCap::is_capable(const string& pool_name, const string& ns,
+                       const OSDCapPoolTag::app_map_t& application_metadata,
+                       const string& object,
+                        bool op_may_read, bool op_may_write,
+                       const std::vector<OpInfo::ClassInfo>& classes,
+                       const entity_addr_t& addr) const
+{
+  std::vector<bool> class_allowed(classes.size(), false);
+  for (auto &grant : grants) {
+    if (grant.is_capable(pool_name, ns, application_metadata,
+                        object, op_may_read, op_may_write, classes, addr,
+                        &class_allowed)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 
 // grammar
 namespace qi = boost::spirit::qi;
@@ -204,26 +422,37 @@ struct OSDCapParser : qi::grammar<Iterator, OSDCap()>
     equoted_string %=
       lexeme['"' >> *(char_ - '"') >> '"'] |
       lexeme['\'' >> *(char_ - '\'') >> '\''];
-    unquoted_word %= +char_("a-zA-Z0-9_.-");
+    unquoted_word %= +char_("a-zA-Z0-9_./-");
     str %= quoted_string | unquoted_word;
     estr %= equoted_string | unquoted_word;
+    network_str %= +char_("/.:a-fA-F0-9][");
 
     spaces = +ascii::space;
 
+    wildcard = (lit('*') | lit("all")) [_val = "*"];
 
-    // match := [pool[=]<poolname> [namespace[=]<namespace>] | auid <123>] [object_prefix <prefix>]
     pool_name %= -(spaces >> lit("pool") >> (lit('=') | spaces) >> str);
-    nspace %= (spaces >> lit("namespace") >> (lit('=') | spaces) >> estr);
-    auid %= (spaces >> lit("auid") >> spaces >> int_);
+    nspace %= (spaces >> lit("namespace")
+              >> (lit('=') | spaces)
+              >> estr >> -char_('*'));
+
+    // match := [pool[=]<poolname> [namespace[=]<namespace>]] [object_prefix <prefix>]
     object_prefix %= -(spaces >> lit("object_prefix") >> spaces >> str);
+    pooltag %= (spaces >> lit("tag")
+               >> spaces >> str // application
+               >> spaces >> (wildcard | str) // key
+               >> -spaces >> lit('=') >> -spaces >> (wildcard | str)); // value
 
-    match = ( (auid >> object_prefix)                 [_val = phoenix::construct<OSDCapMatch>(_1, _2)] |
-             (pool_name >> nspace >> object_prefix)   [_val = phoenix::construct<OSDCapMatch>(_1, _2, _3)] |
-             (pool_name >> object_prefix)             [_val = phoenix::construct<OSDCapMatch>(_1, _2)]);
+    match = (
+      pooltag                                 [_val = phoenix::construct<OSDCapMatch>(_1)] |
+      (nspace >> pooltag)                     [_val = phoenix::construct<OSDCapMatch>(_1, _2)] |
+      (pool_name >> nspace >> object_prefix)  [_val = phoenix::construct<OSDCapMatch>(_1, _2, _3)] |
+      (pool_name >> object_prefix)            [_val = phoenix::construct<OSDCapMatch>(_1, _2)]
+    );
 
     // rwxa := * | [r][w][x] [class-read] [class-write]
     rwxa =
-      (spaces >> lit("*")[_val = OSD_CAP_ANY]) |
+      (spaces >> wildcard[_val = OSD_CAP_ANY]) |
       ( eps[_val = 0] >>
        (
         spaces >>
@@ -233,32 +462,50 @@ struct OSDCapParser : qi::grammar<Iterator, OSDCap()>
        ( (spaces >> lit("class-read")[_val |= OSD_CAP_CLS_R]) ||
          (spaces >> lit("class-write")[_val |= OSD_CAP_CLS_W]) ));
 
-    // capspec := * | rwx | class <name> [classcap]
-    capspec =
-      rwxa                                            [_val = phoenix::construct<OSDCapSpec>(_1)] |
-      ( spaces >> lit("class") >> spaces >> ((str >> spaces >> str)   [_val = phoenix::construct<OSDCapSpec>(_1, _2)] |
-                        str                          [_val = phoenix::construct<OSDCapSpec>(_1, string())] ));
+    // capspec := * | rwx | class <name> [<method name>]
+    class_name %= (spaces >> lit("class") >> spaces >> str);
+    method_name %= -(spaces >> str);
+    capspec = (
+      (rwxa)                      [_val = phoenix::construct<OSDCapSpec>(_1)] |
+      (class_name >> method_name) [_val = phoenix::construct<OSDCapSpec>(_1, _2)]);
+
+    // profile := profile <name> [pool[=]<pool> [namespace[=]<namespace>]]
+    profile_name %= (lit("profile") >> (lit('=') | spaces) >> str);
+    profile = (
+      (profile_name >> pool_name >> nspace) [_val = phoenix::construct<OSDCapProfile>(_1, _2, _3)] |
+      (profile_name >> pool_name)           [_val = phoenix::construct<OSDCapProfile>(_1, _2)]);
 
     // grant := allow match capspec
-    grant = (*ascii::blank >> lit("allow") >>
-            ((capspec >> match)       [_val = phoenix::construct<OSDCapGrant>(_2, _1)] |
-             (match >> capspec)       [_val = phoenix::construct<OSDCapGrant>(_1, _2)]) >>
-            *ascii::blank);
+    grant = (*ascii::blank >>
+            ((lit("allow") >> capspec >> match >>
+              -(spaces >> lit("network") >> spaces >> network_str))
+              [_val = phoenix::construct<OSDCapGrant>(_2, _1, _3)] |
+             (lit("allow") >> match >> capspec >>
+              -(spaces >> lit("network") >> spaces >> network_str))
+              [_val = phoenix::construct<OSDCapGrant>(_1, _2, _3)] |
+              (profile >> -(spaces >> lit("network") >> spaces >> network_str))
+              [_val = phoenix::construct<OSDCapGrant>(_1, _2)]
+             ) >> *ascii::blank);
     // osdcap := grant [grant ...]
     grants %= (grant % (lit(';') | lit(',')));
-    osdcap = grants  [_val = phoenix::construct<OSDCap>(_1)]; 
+    osdcap = grants  [_val = phoenix::construct<OSDCap>(_1)];
   }
   qi::rule<Iterator> spaces;
   qi::rule<Iterator, unsigned()> rwxa;
   qi::rule<Iterator, string()> quoted_string, equoted_string;
   qi::rule<Iterator, string()> unquoted_word;
-  qi::rule<Iterator, string()> str, estr;
-  qi::rule<Iterator, int()> auid;
+  qi::rule<Iterator, string()> str, estr, network_str;
+  qi::rule<Iterator, string()> wildcard;
+  qi::rule<Iterator, string()> class_name;
+  qi::rule<Iterator, string()> method_name;
   qi::rule<Iterator, OSDCapSpec()> capspec;
   qi::rule<Iterator, string()> pool_name;
   qi::rule<Iterator, string()> nspace;
   qi::rule<Iterator, string()> object_prefix;
+  qi::rule<Iterator, OSDCapPoolTag()> pooltag;
   qi::rule<Iterator, OSDCapMatch()> match;
+  qi::rule<Iterator, string()> profile_name;
+  qi::rule<Iterator, OSDCapProfile()> profile;
   qi::rule<Iterator, OSDCapGrant()> grant;
   qi::rule<Iterator, std::vector<OSDCapGrant>()> grants;
   qi::rule<Iterator, OSDCap()> osdcap;
@@ -278,9 +525,8 @@ bool OSDCap::parse(const string& str, ostream *err)
   grants.clear();
 
   if (err)
-    *err << "osdcap parse failed, stopped at '" << std::string(iter, end)
-        << "' of '" << str << "'\n";
+    *err << "osd capability parse failed, stopped at '" << std::string(iter, end)
+        << "' of '" << str << "'";
 
   return false; 
 }
-