mgr 'allow {access-spec} [network {network/prefix}]'
- mgr 'profile {name} [network {network/prefix}]'
+ mgr 'profile {name} [{key1} {match-type} {value1} ...] [network {network/prefix}]'
Manager capabilities can also be specified for specific commands,
all commands exported by a built-in manager service, or all commands
mgr 'allow service {service-name} {access-spec} [network {network/prefix}]'
- mgr 'allow module {module-name} {access-spec} [network {network/prefix}]'
+ mgr 'allow module {module-name} [with {key1} {match-type} {value1} ...] {access-spec} [network {network/prefix}]'
The ``{access-spec}`` syntax is as follows: ::
out << " module " << maybe_quote_string(m.module);
} else if (!m.command.empty()) {
out << " command " << maybe_quote_string(m.command);
- if (!m.command_args.empty()) {
- out << " with";
- for (auto& [key, constraint] : m.command_args) {
- out << " " << maybe_quote_string(key) << constraint;
- }
- }
}
+ }
- if (m.allow != 0) {
- out << " " << m.allow;
+ if (!m.arguments.empty()) {
+ out << (!m.profile.empty() ? "" : " with");
+ for (auto& [key, constraint] : m.arguments) {
+ out << " " << maybe_quote_string(key) << constraint;
}
}
+ if (m.allow != 0) {
+ out << " " << m.allow;
+ }
+
if (m.network.size()) {
out << " network " << m.network;
}
(std::string, module)
(std::string, profile)
(std::string, command)
- (kvmap, command_args)
+ (kvmap, arguments)
(mgr_rwxa_t, allow)
(std::string, network))
}
}
+bool MgrCapGrant::validate_arguments(
+ const std::map<std::string, std::string>& args) const {
+ for (auto& [key, constraint] : arguments) {
+ auto q = args.find(key);
+
+ // argument must be present if a constraint exists
+ if (q == args.end()) {
+ return false;
+ }
+
+ switch (constraint.match_type) {
+ case MgrCapGrantConstraint::MATCH_TYPE_EQUAL:
+ if (constraint.value != q->second)
+ return false;
+ break;
+ case MgrCapGrantConstraint::MATCH_TYPE_PREFIX:
+ if (q->second.find(constraint.value) != 0)
+ return false;
+ break;
+ case MgrCapGrantConstraint::MATCH_TYPE_REGEX:
+ try {
+ std::regex pattern(constraint.value, std::regex::extended);
+ if (!std::regex_match(q->second, pattern)) {
+ return false;
+ }
+ } catch(const std::regex_error&) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
mgr_rwxa_t MgrCapGrant::get_allowed(
CephContext *cct, EntityName name, const std::string& s,
const std::string& m, const std::string& c,
- const std::map<std::string, std::string>& c_args) const {
+ const std::map<std::string, std::string>& args) const {
if (!profile.empty()) {
expand_profile();
mgr_rwxa_t a;
for (auto& grant : profile_grants) {
- a = a | grant.get_allowed(cct, name, s, m, c, c_args);
+ a = a | grant.get_allowed(cct, name, s, m, c, args);
}
return a;
}
if (module != m) {
return mgr_rwxa_t{};
}
+
+ // don't test module arguments when validating a specific command
+ if (c.empty() && !validate_arguments(args)) {
+ return mgr_rwxa_t{};
+ }
return allow;
}
if (command != c) {
return mgr_rwxa_t{};
}
-
- for (auto& [key, constraint] : command_args) {
- auto q = c_args.find(key);
-
- // argument must be present if a constraint exists
- if (q == c_args.end()) {
- return mgr_rwxa_t{};
- }
-
- switch (constraint.match_type) {
- case MgrCapGrantConstraint::MATCH_TYPE_EQUAL:
- if (constraint.value != q->second)
- return mgr_rwxa_t{};
- break;
- case MgrCapGrantConstraint::MATCH_TYPE_PREFIX:
- if (q->second.find(constraint.value) != 0)
- return mgr_rwxa_t{};
- break;
- case MgrCapGrantConstraint::MATCH_TYPE_REGEX:
- try {
- std::regex pattern(constraint.value, std::regex::extended);
- if (!std::regex_match(q->second, pattern)) {
- return mgr_rwxa_t{};
- }
- } catch(const std::regex_error&) {
- return mgr_rwxa_t{};
- }
- break;
- default:
- break;
- }
+ if (!validate_arguments(args)) {
+ return mgr_rwxa_t{};
}
return mgr_rwxa_t{MGR_CAP_ANY};
}
ls.back()->parse("allow command bar with k1=v1 x");
ls.push_back(new MgrCap);
ls.back()->parse("allow command bar with k1=v1 k2=v2 x");
+ ls.push_back(new MgrCap);
+ ls.back()->parse("allow module bar with k1=v1 k2=v2 x");
+ ls.push_back(new MgrCap);
+ ls.back()->parse("profile rbd pool=rbd");
}
// grammar
>> str
>> qi::attr(std::string())
>> qi::attr(std::string())
- >> qi::attr(map<std::string, MgrCapGrantConstraint>())
+ >> -(spaces >> lit("with") >> spaces >> kv_map)
>> spaces >> rwxa
>> -(spaces >> lit("network") >> spaces >> network_str);
>> qi::attr(std::string())
>> str
>> qi::attr(std::string())
- >> qi::attr(std::map<std::string, MgrCapGrantConstraint>())
+ >> -(spaces >> kv_map)
>> qi::attr(0)
>> -(spaces >> lit("network") >> spaces >> network_str);
* - 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')
+ * - a module allow ('allow module rbd_support rw, allow module rbd_support with pool=rbd rw')
* - this will match against a specific python add-on module and the r/w/x
* flags.
*
- * - a profile ('profile read-only')
+ * - a profile ('profile read-only, profile rbd pool=rbd')
* - this will match against specific MGR-enforced semantics of what
* this type of user should need to do. examples include 'read-write',
* 'read-only', 'crash'.
*
* - a command ('allow command foo', 'allow command bar with arg1=val1 arg2 prefix val2')
- * this includes the command name (the prefix string), and a set
- * of key/value pairs that constrain use of that command. if no pairs
- * are specified, any arguments are allowed; if a pair is specified, that
- * argument must be present and equal or match a prefix.
+ * this includes the command name (the prefix string)
+ *
+ * The command, module, and profile caps can also accept an optional
+ * key/value map. If not provided, all command arguments and module
+ * meta-arguments are allowed. If a key/value pair is specified, that
+ * argument must be present and must match the provided constraint.
*/
+ typedef std::map<std::string, MgrCapGrantConstraint> Arguments;
+
std::string service;
std::string module;
std::string profile;
std::string command;
- std::map<std::string, MgrCapGrantConstraint> command_args;
+ Arguments arguments;
// restrict by network
std::string network;
std::string&& module,
std::string&& profile,
std::string&& command,
- std::map<std::string, MgrCapGrantConstraint>&& command_args,
+ Arguments&& arguments,
mgr_rwxa_t 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) {
+ arguments(std::move(arguments)), allow(allow) {
}
+ bool validate_arguments(
+ const std::map<std::string, std::string>& arguments) const;
+
/**
* check if given request parameters match our constraints
*
* @param service service (if any)
* @param module module (if any)
* @param command command (if any)
- * @param command_args command args (if any)
+ * @param arguments profile/module/command args (if any)
* @return bits we allow
*/
mgr_rwxa_t get_allowed(
const std::string& service,
const std::string& module,
const std::string& command,
- const std::map<std::string, std::string>& command_args) const;
+ const std::map<std::string, std::string>& arguments) const;
bool is_allow_all() const {
return (allow == MGR_CAP_ANY &&
* @param service service name
* @param module module name
* @param command command id
- * @param command_args
+ * @param arguments
* @param op_may_read whether the operation may need to read
* @param op_may_write whether the operation may need to write
* @param op_may_exec whether the operation may exec
const std::string& service,
const std::string& module,
const std::string& command,
- const std::map<std::string, std::string>& command_args,
+ const std::map<std::string, std::string>& arguments,
bool op_may_read, bool op_may_write, bool op_may_exec,
const entity_addr_t& addr) const;
"allow module=foo x",
"allow module foo_foo r",
"allow module \" foo \" w",
+ "allow module foo with arg1=value1 x",
"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 command \"foo bar\" with arg=\"baz.xx\"",
"allow command \"foo bar\" with arg = \"baz.xx\"",
"profile osd",
+ "profile rbd pool=ABC namespace=NS",
"profile \"mds-bootstrap\", profile foo",
"allow * network 1.2.3.4/24",
"allow * network ::1/128",
"profile osd-bootstrap",
"profile mds-bootstrap, allow *",
"profile \"foo bar\", allow *",
+ "profile rbd namespace=NS pool=ABC",
"allow command abc",
"allow command \"a b c\"",
"allow command abc with arg=foo",
"allow service \" foo \" w, allow service bar r",
"allow module foo x",
"allow module \" foo_foo \" r",
+ "allow module foo with arg1=value1 x",
"allow command abc with arg=foo arg2=bar, allow service foo r",
0
};