the use of this capability is restricted to clients connecting from
this network.
+- **Manager Caps:** Manager (``ceph-mgr``) capabilities include
+ ``r``, ``w``, ``x`` access settings or ``profile {name}``. For example: ::
+
+ mgr 'allow {access-spec} [network {network/prefix}]'
+
+ mgr 'profile {name} [network {network/prefix}]'
+
+ Manager capabilities can also be specified for specific commands,
+ all commands exported by a built-in manager service, or all commands
+ exported by a specific add-on module. For example: ::
+
+ mgr 'allow command "{command-prefix}" [with {key1} {match-type} {value1} ...] [network {network/prefix}]'
+
+ mgr 'allow service {service-name} {access-spec} [network {network/prefix}]'
+
+ mgr 'allow module {module-name} {access-spec} [network {network/prefix}]'
+
+ The ``{access-spec}`` syntax is as follows: ::
+
+ * | all | [r][w][x]
+
+ The ``{service-name}`` is one of the following: ::
+
+ mgr | osd | pg | py
+
+ The ``{match-type}`` is one of the following: ::
+
+ = | prefix | regex
+
- **Metadata Server Caps:** For administrators, use ``allow *``. For all
other users, such as CephFS clients, consult :doc:`/cephfs/client-auth`
bool capable = s->caps.is_capable(
g_ceph_context,
s->entity_name,
- module, prefix, param_str_map,
+ module, "", prefix, param_str_map,
cmd_r, cmd_w, cmd_x,
s->get_peer_addr());
out << "allow";
if (!m.service.empty()) {
out << " service " << maybe_quote_string(m.service);
+ } else if (!m.module.empty()) {
+ out << " module " << maybe_quote_string(m.module);
} else if (!m.command.empty()) {
out << " command " << maybe_quote_string(m.command);
if (!m.command_args.empty()) {
BOOST_FUSION_ADAPT_STRUCT(MgrCapGrant,
(std::string, service)
+ (std::string, module)
(std::string, profile)
(std::string, command)
(kvmap, command_args)
if (profile == "read-only") {
// grants READ-ONLY caps MGR-wide
- profile_grants.push_back({{}, {}, {}, {}, mgr_rwxa_t{MGR_CAP_R}});
+ profile_grants.push_back({{}, {}, {}, {}, {}, mgr_rwxa_t{MGR_CAP_R}});
return;
}
if (profile == "read-write") {
// grants READ-WRITE caps MGR-wide
- profile_grants.push_back({{}, {}, {}, {},
+ profile_grants.push_back({{}, {}, {}, {}, {},
mgr_rwxa_t{MGR_CAP_R | MGR_CAP_W}});
return;
}
if (profile == "crash") {
- profile_grants.push_back({{}, {}, "crash post", {}, {}});
+ profile_grants.push_back({{}, {}, {}, "crash post", {}, {}});
return;
}
}
mgr_rwxa_t MgrCapGrant::get_allowed(
CephContext *cct, EntityName name, const std::string& s,
- const std::string& c,
+ const std::string& m, const std::string& c,
const std::map<std::string, std::string>& c_args) const {
if (!profile.empty()) {
expand_profile();
mgr_rwxa_t a;
for (auto& grant : profile_grants) {
- a = a | grant.get_allowed(cct, name, s, c, c_args);
+ a = a | grant.get_allowed(cct, name, s, m, c, c_args);
}
return a;
}
return allow;
}
+ if (!module.empty()) {
+ if (module != m) {
+ return mgr_rwxa_t{};
+ }
+ return allow;
+ }
+
if (!command.empty()) {
if (command != c) {
return mgr_rwxa_t{};
void MgrCap::set_allow_all() {
grants.clear();
- grants.push_back({{}, {}, {}, {}, mgr_rwxa_t{MGR_CAP_ANY}});
+ grants.push_back({{}, {}, {}, {}, {}, mgr_rwxa_t{MGR_CAP_ANY}});
text = "allow *";
}
CephContext *cct,
EntityName name,
const std::string& service,
+ const std::string& module,
const std::string& command,
const std::map<std::string, std::string>& command_args,
bool op_may_read, bool op_may_write, bool op_may_exec,
const entity_addr_t& addr) const {
if (cct) {
ldout(cct, 20) << "is_capable service=" << service << " "
+ << "module=" << module << " "
<< "command=" << command
<< (op_may_read ? " read":"")
<< (op_may_write ? " write":"")
}
// check enumerated caps
- allow = allow | grant.get_allowed(cct, name, service, command,
+ allow = allow | grant.get_allowed(cct, name, service, module, command,
command_args);
if ((!op_may_read || (allow & MGR_CAP_R)) &&
(!op_may_write || (allow & MGR_CAP_W)) &&
// command := command[=]cmd [k1=v1 k2=v2 ...]
command_match = -spaces >> lit("allow") >> spaces >> lit("command") >> (lit('=') | spaces)
- >> qi::attr(std::string()) >> qi::attr(std::string())
+ >> qi::attr(std::string())
+ >> qi::attr(std::string())
+ >> qi::attr(std::string())
>> str
>> -(spaces >> lit("with") >> spaces >> kv_map)
>> qi::attr(0)
// service foo rwxa
service_match %= -spaces >> lit("allow") >> spaces >> lit("service") >> (lit('=') | spaces)
- >> str >> qi::attr(std::string()) >> qi::attr(std::string())
+ >> str
+ >> qi::attr(std::string())
+ >> qi::attr(std::string())
+ >> qi::attr(std::string())
>> qi::attr(map<std::string, MgrCapGrantConstraint>())
>> spaces >> rwxa
>> -(spaces >> lit("network") >> spaces >> network_str);
+ // module foo rwxa
+ module_match %= -spaces >> lit("allow") >> spaces >> lit("module") >> (lit('=') | spaces)
+ >> qi::attr(std::string())
+ >> str
+ >> qi::attr(std::string())
+ >> qi::attr(std::string())
+ >> qi::attr(map<std::string, MgrCapGrantConstraint>())
+ >> spaces >> rwxa
+ >> -(spaces >> lit("network") >> spaces >> network_str);
+
// profile foo
profile_match %= -spaces >> -(lit("allow") >> spaces)
>> lit("profile") >> (lit('=') | spaces)
>> qi::attr(std::string())
+ >> qi::attr(std::string())
>> str
>> qi::attr(std::string())
>> qi::attr(std::map<std::string, MgrCapGrantConstraint>())
// rwxa
rwxa_match %= -spaces >> lit("allow") >> spaces
- >> qi::attr(std::string()) >> qi::attr(std::string()) >> qi::attr(std::string())
+ >> qi::attr(std::string())
+ >> qi::attr(std::string())
+ >> qi::attr(std::string())
+ >> qi::attr(std::string())
>> qi::attr(std::map<std::string,MgrCapGrantConstraint>())
>> rwxa
>> -(spaces >> lit("network") >> spaces >> network_str);
);
// grant := allow ...
- grant = -spaces >> (rwxa_match | profile_match | service_match | command_match) >> -spaces;
+ grant = -spaces >> (rwxa_match | profile_match | service_match |
+ module_match | command_match) >> -spaces;
// mgrcap := grant [grant ...]
grants %= (grant % (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
qi::rule<Iterator, MgrCapGrant()> rwxa_match;
qi::rule<Iterator, MgrCapGrant()> command_match;
qi::rule<Iterator, MgrCapGrant()> service_match;
+ qi::rule<Iterator, MgrCapGrant()> module_match;
qi::rule<Iterator, MgrCapGrant()> profile_match;
qi::rule<Iterator, MgrCapGrant()> grant;
qi::rule<Iterator, std::vector<MgrCapGrant>()> grants;
* - a service allow ('allow service mds rw')
* - this will match against a specific service and the r/w/x flags.
*
+ * - a module allow ('allow module rbd_support rw')
+ * - this will match against a specific python add-on module and the r/w/x
+ * flags.
+ *
* - a profile ('profile read-only')
* - this will match against specific MGR-enforced semantics of what
* this type of user should need to do. examples include 'read-write',
* argument must be present and equal or match a prefix.
*/
std::string service;
+ std::string module;
std::string profile;
std::string command;
std::map<std::string, MgrCapGrantConstraint> command_args;
MgrCapGrant() : allow(0) {}
MgrCapGrant(std::string&& service,
+ std::string&& module,
std::string&& profile,
std::string&& command,
std::map<std::string, MgrCapGrantConstraint>&& command_args,
mgr_rwxa_t allow)
- : service(std::move(service)), profile(std::move(profile)),
- command(std::move(command)), command_args(std::move(command_args)),
- allow(allow) {
+ : service(std::move(service)), module(std::move(module)),
+ profile(std::move(profile)), command(std::move(command)),
+ command_args(std::move(command_args)), allow(allow) {
}
/**
* @param cct context
* @param name entity name
* @param service service (if any)
+ * @param module module (if any)
* @param command command (if any)
* @param command_args command args (if any)
* @return bits we allow
CephContext *cct,
EntityName name,
const std::string& service,
+ const std::string& module,
const std::string& command,
const std::map<std::string, std::string>& command_args) const;
bool is_allow_all() const {
return (allow == MGR_CAP_ANY &&
service.empty() &&
+ module.empty() &&
profile.empty() &&
command.empty());
}
* what the capability has specified.
*
* @param service service name
+ * @param module module name
* @param command command id
* @param command_args
* @param op_may_read whether the operation may need to read
bool is_capable(CephContext *cct,
EntityName name,
const std::string& service,
+ const std::string& module,
const std::string& command,
const std::map<std::string, std::string>& command_args,
bool op_may_read, bool op_may_write, bool op_may_exec,
"allow service foo_foo r, allow service bar r",
"allow service foo-foo r, allow service bar r",
"allow service \" foo \" w, allow service bar r",
+ "allow module foo x",
+ "allow module=foo x",
+ "allow module foo_foo r",
+ "allow module \" foo \" w",
"allow command abc with arg=foo arg2=bar, allow service foo r",
"allow command abc.def with arg=foo arg2=bar, allow service foo r",
"allow command \"foo bar\" with arg=\"baz\"",
"allow service foo_foo r, allow service bar r",
"allow service foo-foo r, allow service bar r",
"allow service \" foo \" w, allow service bar r",
+ "allow module foo x",
+ "allow module \" foo_foo \" r",
"allow command abc with arg=foo arg2=bar, allow service foo r",
0
};
ASSERT_TRUE(cap.parse("allow *", nullptr));
ASSERT_TRUE(cap.is_allow_all());
- ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "asdf", {}, true, true, true,
- {}));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
+ true, {}));
MgrCap cap2;
ASSERT_FALSE(cap2.is_allow_all());
b.parse("192.168.2.3");
c.parse("192.167.2.3");
- ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "asdf", {}, true, true, true,
- a));
- ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "asdf", {}, true, true, true,
- b));
- ASSERT_FALSE(cap.is_capable(nullptr, {}, "foo", "asdf", {}, true, true, true,
- c));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
+ true, a));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
+ true, b));
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
+ true, c));
}
TEST(MgrCap, CommandRegEx) {
EntityName name;
name.from_str("osd.123");
- ASSERT_TRUE(cap.is_capable(nullptr, name, "", "abc", {{"arg", "12345abcde"}},
- true, true, true, {}));
- ASSERT_FALSE(cap.is_capable(nullptr, name, "", "abc", {{"arg", "~!@#$"}},
+ ASSERT_TRUE(cap.is_capable(nullptr, name, "", "", "abc",
+ {{"arg", "12345abcde"}}, true, true, true, {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "", "", "abc", {{"arg", "~!@#$"}},
true, true, true, {}));
ASSERT_TRUE(cap.parse("allow command abc with arg regex \"[*\"", nullptr));
- ASSERT_FALSE(cap.is_capable(nullptr, name, "", "abc", {{"arg", ""}}, true,
+ ASSERT_FALSE(cap.is_capable(nullptr, name, "", "", "abc", {{"arg", ""}}, true,
true, true, {}));
}
+
+TEST(MgrCap, Module) {
+ MgrCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+ ASSERT_TRUE(cap.parse("allow module abc r, allow module bcd w", nullptr));
+
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, true, false,
+ {}));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false, false,
+ {}));
+ ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "bcd", "", {}, true, true, false,
+ {}));
+ ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "bcd", "", {}, false, true, false,
+ {}));
+}