]> git.proxmox.com Git - ceph.git/blame - ceph/src/mon/MonCap.cc
update sources to v12.1.0
[ceph.git] / ceph / src / mon / MonCap.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2013 Inktank
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15#include <boost/config/warning_disable.hpp>
16#include <boost/spirit/include/qi_uint.hpp>
17#include <boost/spirit/include/qi.hpp>
18#include <boost/fusion/include/std_pair.hpp>
19#include <boost/spirit/include/phoenix.hpp>
20#include <boost/fusion/adapted/struct/adapt_struct.hpp>
21#include <boost/fusion/include/adapt_struct.hpp>
22
23#include "MonCap.h"
24#include "include/stringify.h"
25#include "common/debug.h"
26#include "common/Formatter.h"
27
28#include <algorithm>
29
30static inline bool is_not_alnum_space(char c)
31{
32 return !(isalpha(c) || isdigit(c) || (c == '-') || (c == '_'));
33}
34
35static string maybe_quote_string(const std::string& str)
36{
37 if (find_if(str.begin(), str.end(), is_not_alnum_space) == str.end())
38 return str;
39 return string("\"") + str + string("\"");
40}
41
42using std::ostream;
43using std::vector;
44
45#define dout_subsys ceph_subsys_mon
46
31f18b77 47ostream& operator<<(ostream& out, const mon_rwxa_t& p)
7c673cae
FG
48{
49 if (p == MON_CAP_ANY)
50 return out << "*";
51
52 if (p & MON_CAP_R)
53 out << "r";
54 if (p & MON_CAP_W)
55 out << "w";
56 if (p & MON_CAP_X)
57 out << "x";
58 return out;
59}
60
61ostream& operator<<(ostream& out, const StringConstraint& c)
62{
63 if (c.prefix.length())
64 return out << "prefix " << c.prefix;
65 else
66 return out << "value " << c.value;
67}
68
69ostream& operator<<(ostream& out, const MonCapGrant& m)
70{
71 out << "allow";
72 if (m.service.length()) {
73 out << " service " << maybe_quote_string(m.service);
74 }
75 if (m.command.length()) {
76 out << " command " << maybe_quote_string(m.command);
77 if (!m.command_args.empty()) {
78 out << " with";
79 for (map<string,StringConstraint>::const_iterator p = m.command_args.begin();
80 p != m.command_args.end();
81 ++p) {
82 if (p->second.value.length())
83 out << " " << maybe_quote_string(p->first) << "=" << maybe_quote_string(p->second.value);
84 else
85 out << " " << maybe_quote_string(p->first) << " prefix " << maybe_quote_string(p->second.prefix);
86 }
87 }
88 }
89 if (m.profile.length()) {
90 out << " profile " << maybe_quote_string(m.profile);
91 }
92 if (m.allow != 0)
93 out << " " << m.allow;
94 return out;
95}
96
97
98// <magic>
99// fusion lets us easily populate structs via the qi parser.
100
101typedef map<string,StringConstraint> kvmap;
102
103BOOST_FUSION_ADAPT_STRUCT(MonCapGrant,
104 (std::string, service)
105 (std::string, profile)
106 (std::string, command)
107 (kvmap, command_args)
108 (mon_rwxa_t, allow))
109
110BOOST_FUSION_ADAPT_STRUCT(StringConstraint,
111 (std::string, value)
112 (std::string, prefix))
113
114// </magic>
115
116void MonCapGrant::expand_profile(int daemon_type, const EntityName& name) const
117{
118 // only generate this list once
119 if (!profile_grants.empty())
120 return;
121
122 if (profile == "read-only") {
123 // grants READ-ONLY caps monitor-wide
124 // 'auth' requires MON_CAP_X even for RO, which we do not grant here.
125 profile_grants.push_back(mon_rwxa_t(MON_CAP_R));
126 return;
127 }
128
129 if (profile == "read-write") {
130 // grants READ-WRITE caps monitor-wide
131 // 'auth' requires MON_CAP_X for all operations, which we do not grant.
132 profile_grants.push_back(mon_rwxa_t(MON_CAP_R | MON_CAP_W));
133 return;
134 }
135
136 switch (daemon_type) {
137 case CEPH_ENTITY_TYPE_MON:
138 expand_profile_mon(name);
139 return;
140 case CEPH_ENTITY_TYPE_MGR:
141 expand_profile_mgr(name);
142 return;
143 }
144}
145
146void MonCapGrant::expand_profile_mgr(const EntityName& name) const
147{
148}
149
150void MonCapGrant::expand_profile_mon(const EntityName& name) const
151{
152 if (profile == "mon") {
153 profile_grants.push_back(MonCapGrant("mon", MON_CAP_ALL));
154 profile_grants.push_back(MonCapGrant("log", MON_CAP_ALL));
155 }
156 if (profile == "osd") {
157 profile_grants.push_back(MonCapGrant("osd", MON_CAP_ALL));
158 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
159 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R | MON_CAP_W));
160 profile_grants.push_back(MonCapGrant("log", MON_CAP_W));
161 }
162 if (profile == "mds") {
163 profile_grants.push_back(MonCapGrant("mds", MON_CAP_ALL));
164 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
165 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
166 // This command grant is checked explicitly in MRemoveSnaps handling
167 profile_grants.push_back(MonCapGrant("osd pool rmsnap"));
31f18b77 168 profile_grants.push_back(MonCapGrant("osd blacklist"));
7c673cae
FG
169 profile_grants.push_back(MonCapGrant("log", MON_CAP_W));
170 }
171 if (profile == "mgr") {
172 profile_grants.push_back(MonCapGrant("mgr", MON_CAP_ALL));
31f18b77
FG
173 profile_grants.push_back(MonCapGrant("log", MON_CAP_R | MON_CAP_W));
174 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R | MON_CAP_W));
175 profile_grants.push_back(MonCapGrant("mds", MON_CAP_R | MON_CAP_W));
7c673cae 176 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R | MON_CAP_W));
31f18b77
FG
177 profile_grants.push_back(MonCapGrant("auth", MON_CAP_R | MON_CAP_X));
178 profile_grants.push_back(MonCapGrant("config-key", MON_CAP_R | MON_CAP_W));
7c673cae
FG
179 string prefix = string("daemon-private/mgr/");
180 profile_grants.push_back(MonCapGrant("config-key get", "key",
181 StringConstraint("", prefix)));
182 profile_grants.push_back(MonCapGrant("config-key put", "key",
183 StringConstraint("", prefix)));
184 profile_grants.push_back(MonCapGrant("config-key exists", "key",
185 StringConstraint("", prefix)));
186 profile_grants.push_back(MonCapGrant("config-key delete", "key",
187 StringConstraint("", prefix)));
188 }
189 if (profile == "osd" || profile == "mds" || profile == "mon" ||
190 profile == "mgr") {
191 string prefix = string("daemon-private/") + stringify(name) + string("/");
192 profile_grants.push_back(MonCapGrant("config-key get", "key", StringConstraint("", prefix)));
193 profile_grants.push_back(MonCapGrant("config-key put", "key", StringConstraint("", prefix)));
194 profile_grants.push_back(MonCapGrant("config-key exists", "key", StringConstraint("", prefix)));
195 profile_grants.push_back(MonCapGrant("config-key delete", "key", StringConstraint("", prefix)));
196 }
197 if (profile == "bootstrap-osd") {
198 string prefix = "dm-crypt/osd";
199 profile_grants.push_back(MonCapGrant("config-key put", "key", StringConstraint("", prefix)));
200 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
201 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
202 profile_grants.push_back(MonCapGrant("mon getmap"));
203 profile_grants.push_back(MonCapGrant("osd create"));
204 profile_grants.push_back(MonCapGrant("auth get-or-create"));
205 profile_grants.back().command_args["entity"] = StringConstraint("", "client.");
206 prefix = "allow command \"config-key get\" with key=\"dm-crypt/osd/";
207 profile_grants.back().command_args["caps_mon"] = StringConstraint("", prefix);
208 profile_grants.push_back(MonCapGrant("auth add"));
209 profile_grants.back().command_args["entity"] = StringConstraint("", "osd.");
210 profile_grants.back().command_args["caps_mon"] = StringConstraint("allow profile osd", "");
211 profile_grants.back().command_args["caps_osd"] = StringConstraint("allow *", "");
212 }
213 if (profile == "bootstrap-mds") {
214 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
215 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
216 profile_grants.push_back(MonCapGrant("mon getmap"));
217 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
218 profile_grants.back().command_args["entity"] = StringConstraint("", "mds.");
219 profile_grants.back().command_args["caps_mon"] = StringConstraint("allow profile mds", "");
220 profile_grants.back().command_args["caps_osd"] = StringConstraint("allow rwx", "");
221 profile_grants.back().command_args["caps_mds"] = StringConstraint("allow", "");
222 }
223 if (profile == "bootstrap-mgr") {
224 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
225 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
226 profile_grants.push_back(MonCapGrant("mon getmap"));
227 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mgr keys
228 profile_grants.back().command_args["entity"] = StringConstraint("", "mgr.");
229 profile_grants.back().command_args["caps_mon"] = StringConstraint("allow profile mgr", "");
230 }
231 if (profile == "bootstrap-rgw") {
232 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
233 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
234 profile_grants.push_back(MonCapGrant("mon getmap"));
235 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
236 profile_grants.back().command_args["entity"] = StringConstraint("", "client.rgw.");
237 profile_grants.back().command_args["caps_mon"] = StringConstraint("allow rw", "");
238 profile_grants.back().command_args["caps_osd"] = StringConstraint("allow rwx", "");
239 }
240 if (profile == "fs-client") {
241 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
242 profile_grants.push_back(MonCapGrant("mds", MON_CAP_R));
243 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
244 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
245 }
246 if (profile == "simple-rados-client") {
247 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
248 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
249 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
250 }
251
252 if (profile == "role-definer") {
253 // grants ALL caps to the auth subsystem, read-only on the
254 // monitor subsystem and nothing else.
255 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
256 profile_grants.push_back(MonCapGrant("auth", MON_CAP_ALL));
257 }
258}
259
260mon_rwxa_t MonCapGrant::get_allowed(CephContext *cct,
261 int daemon_type,
262 EntityName name,
263 const std::string& s, const std::string& c,
264 const map<string,string>& c_args) const
265{
266 if (profile.length()) {
267 expand_profile(daemon_type, name);
268 mon_rwxa_t a;
269 for (list<MonCapGrant>::const_iterator p = profile_grants.begin();
270 p != profile_grants.end(); ++p)
271 a = a | p->get_allowed(cct, daemon_type, name, s, c, c_args);
272 return a;
273 }
274 if (service.length()) {
275 if (service != s)
276 return 0;
277 return allow;
278 }
279 if (command.length()) {
280 if (command != c)
281 return 0;
282 for (map<string,StringConstraint>::const_iterator p = command_args.begin(); p != command_args.end(); ++p) {
283 map<string,string>::const_iterator q = c_args.find(p->first);
284 // argument must be present if a constraint exists
285 if (q == c_args.end())
286 return 0;
287 if (p->second.value.length()) {
288 // match value
289 if (p->second.value != q->second)
290 return 0;
291 } else {
292 // match prefix
293 if (q->second.find(p->second.prefix) != 0)
294 return 0;
295 }
296 }
297 return MON_CAP_ALL;
298 }
299 return allow;
300}
301
302ostream& operator<<(ostream&out, const MonCap& m)
303{
304 for (vector<MonCapGrant>::const_iterator p = m.grants.begin(); p != m.grants.end(); ++p) {
305 if (p != m.grants.begin())
306 out << ", ";
307 out << *p;
308 }
309 return out;
310}
311
312bool MonCap::is_allow_all() const
313{
314 for (vector<MonCapGrant>::const_iterator p = grants.begin(); p != grants.end(); ++p)
315 if (p->is_allow_all())
316 return true;
317 return false;
318}
319
320void MonCap::set_allow_all()
321{
322 grants.clear();
323 grants.push_back(MonCapGrant(MON_CAP_ANY));
324 text = "allow *";
325}
326
327bool MonCap::is_capable(CephContext *cct,
328 int daemon_type,
329 EntityName name,
330 const string& service,
331 const string& command, const map<string,string>& command_args,
332 bool op_may_read, bool op_may_write, bool op_may_exec) const
333{
334 if (cct)
335 ldout(cct, 20) << "is_capable service=" << service << " command=" << command
336 << (op_may_read ? " read":"")
337 << (op_may_write ? " write":"")
338 << (op_may_exec ? " exec":"")
339 << " on cap " << *this
340 << dendl;
341 mon_rwxa_t allow = 0;
342 for (vector<MonCapGrant>::const_iterator p = grants.begin();
343 p != grants.end(); ++p) {
344 if (cct)
345 ldout(cct, 20) << " allow so far " << allow << ", doing grant " << *p << dendl;
346
347 if (p->is_allow_all()) {
348 if (cct)
349 ldout(cct, 20) << " allow all" << dendl;
350 return true;
351 }
352
353 // check enumerated caps
354 allow = allow | p->get_allowed(cct, daemon_type, name, service, command,
355 command_args);
356 if ((!op_may_read || (allow & MON_CAP_R)) &&
357 (!op_may_write || (allow & MON_CAP_W)) &&
358 (!op_may_exec || (allow & MON_CAP_X))) {
359 if (cct)
360 ldout(cct, 20) << " match" << dendl;
361 return true;
362 }
363 }
364 return false;
365}
366
367void MonCap::encode(bufferlist& bl) const
368{
369 ENCODE_START(4, 4, bl); // legacy MonCaps was 3, 3
370 ::encode(text, bl);
371 ENCODE_FINISH(bl);
372}
373
374void MonCap::decode(bufferlist::iterator& bl)
375{
376 string s;
377 DECODE_START(4, bl);
378 ::decode(s, bl);
379 DECODE_FINISH(bl);
380 parse(s, NULL);
381}
382
383void MonCap::dump(Formatter *f) const
384{
385 f->dump_string("text", text);
386}
387
388void MonCap::generate_test_instances(list<MonCap*>& ls)
389{
390 ls.push_back(new MonCap);
391 ls.push_back(new MonCap);
392 ls.back()->parse("allow *");
393 ls.push_back(new MonCap);
394 ls.back()->parse("allow rwx");
395 ls.push_back(new MonCap);
396 ls.back()->parse("allow service foo x");
397 ls.push_back(new MonCap);
398 ls.back()->parse("allow command bar x");
399 ls.push_back(new MonCap);
400 ls.back()->parse("allow service foo r, allow command bar x");
401 ls.push_back(new MonCap);
402 ls.back()->parse("allow command bar with k1=v1 x");
403 ls.push_back(new MonCap);
404 ls.back()->parse("allow command bar with k1=v1 k2=v2 x");
405}
406
407// grammar
408namespace qi = boost::spirit::qi;
409namespace ascii = boost::spirit::ascii;
410namespace phoenix = boost::phoenix;
411
412
413template <typename Iterator>
414struct MonCapParser : qi::grammar<Iterator, MonCap()>
415{
416 MonCapParser() : MonCapParser::base_type(moncap)
417 {
418 using qi::char_;
419 using qi::int_;
420 using qi::ulong_long;
421 using qi::lexeme;
422 using qi::alnum;
423 using qi::_val;
424 using qi::_1;
425 using qi::_2;
426 using qi::_3;
427 using qi::eps;
428 using qi::lit;
429
430 quoted_string %=
431 lexeme['"' >> +(char_ - '"') >> '"'] |
432 lexeme['\'' >> +(char_ - '\'') >> '\''];
433 unquoted_word %= +char_("a-zA-Z0-9_.-");
434 str %= quoted_string | unquoted_word;
435
436 spaces = +(lit(' ') | lit('\n') | lit('\t'));
437
438 // command := command[=]cmd [k1=v1 k2=v2 ...]
439 str_match = '=' >> str >> qi::attr(string());
440 str_prefix = spaces >> lit("prefix") >> spaces >> qi::attr(string()) >> str;
441 kv_pair = str >> (str_match | str_prefix);
442 kv_map %= kv_pair >> *(spaces >> kv_pair);
443 command_match = -spaces >> lit("allow") >> spaces >> lit("command") >> (lit('=') | spaces)
444 >> qi::attr(string()) >> qi::attr(string())
445 >> str
446 >> -(spaces >> lit("with") >> spaces >> kv_map)
447 >> qi::attr(0);
448
449 // service foo rwxa
450 service_match %= -spaces >> lit("allow") >> spaces >> lit("service") >> (lit('=') | spaces)
451 >> str >> qi::attr(string()) >> qi::attr(string())
452 >> qi::attr(map<string,StringConstraint>())
453 >> spaces >> rwxa;
454
455 // profile foo
456 profile_match %= -spaces >> lit("allow") >> spaces >> lit("profile") >> (lit('=') | spaces)
457 >> qi::attr(string())
458 >> str
459 >> qi::attr(string())
460 >> qi::attr(map<string,StringConstraint>())
461 >> qi::attr(0);
462
463 // rwxa
464 rwxa_match %= -spaces >> lit("allow") >> spaces
465 >> qi::attr(string()) >> qi::attr(string()) >> qi::attr(string())
466 >> qi::attr(map<string,StringConstraint>())
467 >> rwxa;
468
469 // rwxa := * | [r][w][x]
470 rwxa =
471 (lit("*")[_val = MON_CAP_ANY]) |
472 ( eps[_val = 0] >>
473 ( lit('r')[_val |= MON_CAP_R] ||
474 lit('w')[_val |= MON_CAP_W] ||
475 lit('x')[_val |= MON_CAP_X]
476 )
477 );
478
479 // grant := allow ...
480 grant = -spaces >> (rwxa_match | profile_match | service_match | command_match) >> -spaces;
481
482 // moncap := grant [grant ...]
483 grants %= (grant % (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
484 moncap = grants [_val = phoenix::construct<MonCap>(_1)];
485
486 }
487 qi::rule<Iterator> spaces;
488 qi::rule<Iterator, unsigned()> rwxa;
489 qi::rule<Iterator, string()> quoted_string;
490 qi::rule<Iterator, string()> unquoted_word;
491 qi::rule<Iterator, string()> str;
492
493 qi::rule<Iterator, StringConstraint()> str_match, str_prefix;
494 qi::rule<Iterator, pair<string, StringConstraint>()> kv_pair;
495 qi::rule<Iterator, map<string, StringConstraint>()> kv_map;
496
497 qi::rule<Iterator, MonCapGrant()> rwxa_match;
498 qi::rule<Iterator, MonCapGrant()> command_match;
499 qi::rule<Iterator, MonCapGrant()> service_match;
500 qi::rule<Iterator, MonCapGrant()> profile_match;
501 qi::rule<Iterator, MonCapGrant()> grant;
502 qi::rule<Iterator, std::vector<MonCapGrant>()> grants;
503 qi::rule<Iterator, MonCap()> moncap;
504};
505
506bool MonCap::parse(const string& str, ostream *err)
507{
508 string s = str;
509 string::iterator iter = s.begin();
510 string::iterator end = s.end();
511
512 MonCapParser<string::iterator> g;
513 bool r = qi::parse(iter, end, g, *this);
514 //MonCapGrant foo;
515 //bool r = qi::phrase_parse(iter, end, g, ascii::space, foo);
516 if (r && iter == end) {
517 text = str;
518 return true;
519 }
520
521 // Make sure no grants are kept after parsing failed!
522 grants.clear();
523
524 if (err) {
525 if (iter != end)
526 *err << "moncap parse failed, stopped at '" << std::string(iter, end)
527 << "' of '" << str << "'\n";
528 else
529 *err << "moncap parse failed, stopped at end of '" << str << "'\n";
530 }
531
532 return false;
533}
534