1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
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.
17 #include "mon/AuthMonitor.h"
18 #include "mon/Monitor.h"
19 #include "mon/MonitorDBStore.h"
21 #include "messages/MMonCommand.h"
22 #include "messages/MAuth.h"
23 #include "messages/MAuthReply.h"
24 #include "messages/MMonGlobalID.h"
25 #include "msg/Messenger.h"
27 #include "auth/AuthServiceHandler.h"
28 #include "auth/KeyRing.h"
29 #include "include/assert.h"
31 #define dout_subsys ceph_subsys_mon
33 #define dout_prefix _prefix(_dout, mon, get_last_committed())
34 static ostream
& _prefix(std::ostream
*_dout
, Monitor
*mon
, version_t v
) {
35 return *_dout
<< "mon." << mon
->name
<< "@" << mon
->rank
36 << "(" << mon
->get_state_name()
37 << ").auth v" << v
<< " ";
40 ostream
& operator<<(ostream
&out
, const AuthMonitor
&pm
)
45 bool AuthMonitor::check_rotate()
47 KeyServerData::Incremental rot_inc
;
48 rot_inc
.op
= KeyServerData::AUTH_INC_SET_ROTATING
;
49 if (!mon
->key_server
.updated_rotating(rot_inc
.rotating_bl
, last_rotating_ver
))
51 dout(10) << __func__
<< " updated rotating" << dendl
;
52 push_cephx_inc(rot_inc
);
57 Tick function to update the map based on performance every N seconds
60 void AuthMonitor::tick()
62 if (!is_active()) return;
64 dout(10) << *this << dendl
;
66 if (!mon
->is_leader()) return;
72 void AuthMonitor::on_active()
74 dout(10) << "AuthMonitor::on_active()" << dendl
;
76 if (!mon
->is_leader())
78 mon
->key_server
.start_server();
81 void AuthMonitor::create_initial()
83 dout(10) << "create_initial -- creating initial map" << dendl
;
85 // initialize rotating keys
86 last_rotating_ver
= 0;
88 assert(pending_auth
.size() == 1);
90 if (mon
->is_keyring_required()) {
93 int ret
= mon
->store
->get("mkfs", "keyring", bl
);
94 // fail hard only if there's an error we're not expecting to see
95 assert((ret
== 0) || (ret
== -ENOENT
));
97 // try importing only if there's a key
100 bufferlist::iterator p
= bl
.begin();
102 ::decode(keyring
, p
);
103 import_keyring(keyring
);
107 max_global_id
= MIN_GLOBAL_ID
;
110 inc
.inc_type
= GLOBAL_ID
;
111 inc
.max_global_id
= max_global_id
;
112 pending_auth
.push_back(inc
);
117 void AuthMonitor::update_from_paxos(bool *need_bootstrap
)
119 dout(10) << __func__
<< dendl
;
120 version_t version
= get_last_committed();
121 version_t keys_ver
= mon
->key_server
.get_ver();
122 if (version
== keys_ver
)
124 assert(version
> keys_ver
);
126 version_t latest_full
= get_version_latest_full();
128 dout(10) << __func__
<< " version " << version
<< " keys ver " << keys_ver
129 << " latest " << latest_full
<< dendl
;
131 if ((latest_full
> 0) && (latest_full
> keys_ver
)) {
132 bufferlist latest_bl
;
133 int err
= get_version_full(latest_full
, latest_bl
);
135 assert(latest_bl
.length() != 0);
136 dout(7) << __func__
<< " loading summary e " << latest_full
<< dendl
;
137 dout(7) << __func__
<< " latest length " << latest_bl
.length() << dendl
;
138 bufferlist::iterator p
= latest_bl
.begin();
140 ::decode(struct_v
, p
);
141 ::decode(max_global_id
, p
);
142 ::decode(mon
->key_server
, p
);
143 mon
->key_server
.set_ver(latest_full
);
144 keys_ver
= latest_full
;
147 dout(10) << __func__
<< " key server version " << mon
->key_server
.get_ver() << dendl
;
149 // walk through incrementals
150 while (version
> keys_ver
) {
152 int ret
= get_version(keys_ver
+1, bl
);
156 // reset if we are moving to initial state. we will normally have
157 // keys in here temporarily for bootstrapping that we need to
160 mon
->key_server
.clear_secrets();
162 dout(20) << __func__
<< " walking through version " << (keys_ver
+1)
163 << " len " << bl
.length() << dendl
;
165 bufferlist::iterator p
= bl
.begin();
171 switch (inc
.inc_type
) {
173 max_global_id
= inc
.max_global_id
;
178 KeyServerData::Incremental auth_inc
;
179 bufferlist::iterator iter
= inc
.auth_data
.begin();
180 ::decode(auth_inc
, iter
);
181 mon
->key_server
.apply_data_incremental(auth_inc
);
188 mon
->key_server
.set_ver(keys_ver
);
190 if (keys_ver
== 1 && mon
->is_keyring_required()) {
191 auto t(std::make_shared
<MonitorDBStore::Transaction
>());
192 t
->erase("mkfs", "keyring");
193 mon
->store
->apply_transaction(t
);
197 if (last_allocated_id
== 0)
198 last_allocated_id
= max_global_id
;
200 dout(10) << "update_from_paxos() last_allocated_id=" << last_allocated_id
201 << " max_global_id=" << max_global_id
202 << " format_version " << format_version
206 void AuthMonitor::increase_max_global_id()
208 assert(mon
->is_leader());
210 max_global_id
+= g_conf
->mon_globalid_prealloc
;
211 dout(10) << "increasing max_global_id to " << max_global_id
<< dendl
;
213 inc
.inc_type
= GLOBAL_ID
;
214 inc
.max_global_id
= max_global_id
;
215 pending_auth
.push_back(inc
);
218 bool AuthMonitor::should_propose(double& delay
)
220 return (!pending_auth
.empty());
223 void AuthMonitor::create_pending()
225 pending_auth
.clear();
226 dout(10) << "create_pending v " << (get_last_committed() + 1) << dendl
;
229 void AuthMonitor::encode_pending(MonitorDBStore::TransactionRef t
)
231 dout(10) << __func__
<< " v " << (get_last_committed() + 1) << dendl
;
237 vector
<Incremental
>::iterator p
;
238 for (p
= pending_auth
.begin(); p
!= pending_auth
.end(); ++p
)
239 p
->encode(bl
, mon
->get_quorum_con_features());
241 version_t version
= get_last_committed() + 1;
242 put_version(t
, version
, bl
);
243 put_last_committed(t
, version
);
246 void AuthMonitor::encode_full(MonitorDBStore::TransactionRef t
)
248 version_t version
= mon
->key_server
.get_ver();
249 // do not stash full version 0 as it will never be removed nor read
253 dout(10) << __func__
<< " auth v " << version
<< dendl
;
254 assert(get_last_committed() == version
);
257 Mutex::Locker
l(mon
->key_server
.get_lock());
258 dout(20) << __func__
<< " key server has "
259 << (mon
->key_server
.has_secrets() ? "" : "no ")
260 << "secrets!" << dendl
;
262 ::encode(v
, full_bl
);
263 ::encode(max_global_id
, full_bl
);
264 ::encode(mon
->key_server
, full_bl
);
266 put_version_full(t
, version
, full_bl
);
267 put_version_latest_full(t
, version
);
270 version_t
AuthMonitor::get_trim_to()
272 unsigned max
= g_conf
->paxos_max_join_drift
* 2;
273 version_t version
= get_last_committed();
274 if (mon
->is_leader() && (version
> max
))
275 return version
- max
;
279 bool AuthMonitor::preprocess_query(MonOpRequestRef op
)
281 PaxosServiceMessage
*m
= static_cast<PaxosServiceMessage
*>(op
->get_req());
282 dout(10) << "preprocess_query " << *m
<< " from " << m
->get_orig_source_inst() << dendl
;
283 switch (m
->get_type()) {
284 case MSG_MON_COMMAND
:
285 return preprocess_command(op
);
288 return prep_auth(op
, false);
290 case MSG_MON_GLOBAL_ID
:
299 bool AuthMonitor::prepare_update(MonOpRequestRef op
)
301 PaxosServiceMessage
*m
= static_cast<PaxosServiceMessage
*>(op
->get_req());
302 dout(10) << "prepare_update " << *m
<< " from " << m
->get_orig_source_inst() << dendl
;
303 switch (m
->get_type()) {
304 case MSG_MON_COMMAND
:
305 return prepare_command(op
);
306 case MSG_MON_GLOBAL_ID
:
307 return prepare_global_id(op
);
309 return prep_auth(op
, true);
316 uint64_t AuthMonitor::assign_global_id(MonOpRequestRef op
, bool should_increase_max
)
318 MAuth
*m
= static_cast<MAuth
*>(op
->get_req());
319 int total_mon
= mon
->monmap
->size();
320 dout(10) << "AuthMonitor::assign_global_id m=" << *m
<< " mon=" << mon
->rank
<< "/" << total_mon
321 << " last_allocated=" << last_allocated_id
<< " max_global_id=" << max_global_id
<< dendl
;
323 uint64_t next_global_id
= last_allocated_id
+ 1;
324 int remainder
= next_global_id
% total_mon
;
326 remainder
= total_mon
- remainder
;
327 next_global_id
+= remainder
+ mon
->rank
;
328 dout(10) << "next_global_id should be " << next_global_id
<< dendl
;
330 // if we can't bump the max, bail out now on an out-of-bounds gid
331 if (next_global_id
> max_global_id
&&
332 (!mon
->is_leader() || !should_increase_max
)) {
336 // can we return a gid?
337 bool return_next
= (next_global_id
<= max_global_id
);
340 while (mon
->is_leader() &&
341 (max_global_id
< g_conf
->mon_globalid_prealloc
||
342 next_global_id
>= max_global_id
- g_conf
->mon_globalid_prealloc
/ 2)) {
343 increase_max_global_id();
347 last_allocated_id
= next_global_id
;
348 return next_global_id
;
355 bool AuthMonitor::prep_auth(MonOpRequestRef op
, bool paxos_writable
)
357 MAuth
*m
= static_cast<MAuth
*>(op
->get_req());
358 dout(10) << "prep_auth() blob_size=" << m
->get_auth_payload().length() << dendl
;
360 MonSession
*s
= op
->get_session();
362 dout(10) << "no session, dropping" << dendl
;
367 AuthCapsInfo caps_info
;
369 bufferlist response_bl
;
370 bufferlist::iterator indata
= m
->auth_payload
.begin();
371 __u32 proto
= m
->protocol
;
373 EntityName entity_name
;
376 if (m
->protocol
== 0 && !s
->auth_handler
) {
377 set
<__u32
> supported
;
381 ::decode(struct_v
, indata
);
382 ::decode(supported
, indata
);
383 ::decode(entity_name
, indata
);
384 ::decode(s
->global_id
, indata
);
385 } catch (const buffer::error
&e
) {
386 dout(10) << "failed to decode initial auth message" << dendl
;
391 // do we require cephx signatures?
393 if (!m
->get_connection()->has_feature(CEPH_FEATURE_MSG_AUTH
)) {
394 if (entity_name
.get_type() == CEPH_ENTITY_TYPE_MON
||
395 entity_name
.get_type() == CEPH_ENTITY_TYPE_OSD
||
396 entity_name
.get_type() == CEPH_ENTITY_TYPE_MDS
||
397 entity_name
.get_type() == CEPH_ENTITY_TYPE_MGR
) {
398 if (g_conf
->cephx_cluster_require_signatures
||
399 g_conf
->cephx_require_signatures
) {
400 dout(1) << m
->get_source_inst()
401 << " supports cephx but not signatures and"
402 << " 'cephx [cluster] require signatures = true';"
403 << " disallowing cephx" << dendl
;
404 supported
.erase(CEPH_AUTH_CEPHX
);
407 if (g_conf
->cephx_service_require_signatures
||
408 g_conf
->cephx_require_signatures
) {
409 dout(1) << m
->get_source_inst()
410 << " supports cephx but not signatures and"
411 << " 'cephx [service] require signatures = true';"
412 << " disallowing cephx" << dendl
;
413 supported
.erase(CEPH_AUTH_CEPHX
);
419 if (entity_name
.get_type() == CEPH_ENTITY_TYPE_MON
||
420 entity_name
.get_type() == CEPH_ENTITY_TYPE_OSD
||
421 entity_name
.get_type() == CEPH_ENTITY_TYPE_MDS
||
422 entity_name
.get_type() == CEPH_ENTITY_TYPE_MGR
)
423 type
= mon
->auth_cluster_required
.pick(supported
);
425 type
= mon
->auth_service_required
.pick(supported
);
427 s
->auth_handler
= get_auth_service_handler(type
, g_ceph_context
, &mon
->key_server
);
428 if (!s
->auth_handler
) {
429 dout(1) << "client did not provide supported auth type" << dendl
;
434 } else if (!s
->auth_handler
) {
435 dout(10) << "protocol specified but no s->auth_handler" << dendl
;
440 /* assign a new global_id? we assume this should only happen on the first
441 request. If a client tries to send it later, it'll screw up its auth
444 s
->global_id
= assign_global_id(op
, paxos_writable
);
447 delete s
->auth_handler
;
448 s
->auth_handler
= NULL
;
450 if (mon
->is_leader() && paxos_writable
) {
451 dout(10) << "increasing global id, waitlisting message" << dendl
;
452 wait_for_active(op
, new C_RetryMessage(this, op
));
456 if (!mon
->is_leader()) {
457 dout(10) << "not the leader, requesting more ids from leader" << dendl
;
458 int leader
= mon
->get_leader();
459 MMonGlobalID
*req
= new MMonGlobalID();
460 req
->old_max_id
= max_global_id
;
461 mon
->messenger
->send_message(req
, mon
->monmap
->get_inst(leader
));
462 wait_for_finished_proposal(op
, new C_RetryMessage(this, op
));
466 assert(!paxos_writable
);
476 // always send the latest monmap.
477 if (m
->monmap_epoch
< mon
->monmap
->get_epoch())
478 mon
->send_latest_monmap(m
->get_connection().get());
480 proto
= s
->auth_handler
->start_session(entity_name
, indata
, response_bl
, caps_info
);
482 if (caps_info
.allow_all
)
483 s
->caps
.set_allow_all();
486 ret
= s
->auth_handler
->handle_request(indata
, response_bl
, s
->global_id
, caps_info
, &auid
);
489 wait_for_active(op
, new C_RetryMessage(this,op
));
492 if (caps_info
.caps
.length()) {
493 bufferlist::iterator p
= caps_info
.caps
.begin();
497 } catch (const buffer::error
&err
) {
498 derr
<< "corrupt cap data for " << entity_name
<< " in auth db" << dendl
;
501 s
->caps
.parse(str
, NULL
);
504 } catch (const buffer::error
&err
) {
506 dout(0) << "caught error when trying to handle auth request, probably malformed request" << dendl
;
510 reply
= new MAuthReply(proto
, &response_bl
, ret
, s
->global_id
);
511 mon
->send_reply(op
, reply
);
516 bool AuthMonitor::preprocess_command(MonOpRequestRef op
)
518 MMonCommand
*m
= static_cast<MMonCommand
*>(op
->get_req());
523 map
<string
, cmd_vartype
> cmdmap
;
524 if (!cmdmap_from_json(m
->cmd
, &cmdmap
, ss
)) {
525 // ss has reason for failure
526 string rs
= ss
.str();
527 mon
->reply_command(op
, -EINVAL
, rs
, rdata
, get_last_committed());
532 cmd_getval(g_ceph_context
, cmdmap
, "prefix", prefix
);
533 if (prefix
== "auth add" ||
534 prefix
== "auth del" ||
535 prefix
== "auth rm" ||
536 prefix
== "auth get-or-create" ||
537 prefix
== "auth get-or-create-key" ||
538 prefix
== "auth import" ||
539 prefix
== "auth caps") {
543 MonSession
*session
= m
->get_session();
545 mon
->reply_command(op
, -EACCES
, "access denied", rdata
, get_last_committed());
549 // entity might not be supplied, but if it is, it should be valid
551 cmd_getval(g_ceph_context
, cmdmap
, "entity", entity_name
);
553 if (!entity_name
.empty() && !entity
.from_str(entity_name
)) {
554 ss
<< "invalid entity_auth " << entity_name
;
555 mon
->reply_command(op
, -EINVAL
, ss
.str(), get_last_committed());
560 cmd_getval(g_ceph_context
, cmdmap
, "format", format
, string("plain"));
561 boost::scoped_ptr
<Formatter
> f(Formatter::create(format
));
563 if (prefix
== "auth export") {
565 export_keyring(keyring
);
566 if (!entity_name
.empty()) {
568 if (keyring
.get_auth(entity
, eauth
)) {
570 kr
.add(entity
, eauth
);
572 kr
.encode_formatted("auth", f
.get(), rdata
);
574 kr
.encode_plaintext(rdata
);
575 ss
<< "export " << eauth
;
578 ss
<< "no key for " << eauth
;
583 keyring
.encode_formatted("auth", f
.get(), rdata
);
585 keyring
.encode_plaintext(rdata
);
587 ss
<< "exported master keyring";
590 } else if (prefix
== "auth get" && !entity_name
.empty()) {
592 EntityAuth entity_auth
;
593 if(!mon
->key_server
.get_auth(entity
, entity_auth
)) {
594 ss
<< "failed to find " << entity_name
<< " in keyring";
597 keyring
.add(entity
, entity_auth
);
599 keyring
.encode_formatted("auth", f
.get(), rdata
);
601 keyring
.encode_plaintext(rdata
);
602 ss
<< "exported keyring for " << entity_name
;
605 } else if (prefix
== "auth print-key" ||
606 prefix
== "auth print_key" ||
607 prefix
== "auth get-key") {
609 if (!mon
->key_server
.get_auth(entity
, auth
)) {
610 ss
<< "don't have " << entity
;
615 auth
.key
.encode_formatted("auth", f
.get(), rdata
);
617 auth
.key
.encode_plaintext(rdata
);
620 } else if (prefix
== "auth list") {
622 mon
->key_server
.encode_formatted("auth", f
.get(), rdata
);
624 mon
->key_server
.encode_plaintext(rdata
);
625 if (rdata
.length() > 0)
626 ss
<< "installed auth entries:" << std::endl
;
628 ss
<< "no installed auth entries!" << std::endl
;
633 ss
<< "invalid command";
640 getline(ss
, rs
, '\0');
641 mon
->reply_command(op
, r
, rs
, rdata
, get_last_committed());
645 void AuthMonitor::export_keyring(KeyRing
& keyring
)
647 mon
->key_server
.export_keyring(keyring
);
650 int AuthMonitor::import_keyring(KeyRing
& keyring
)
652 for (map
<EntityName
, EntityAuth
>::iterator p
= keyring
.get_keys().begin();
653 p
!= keyring
.get_keys().end();
655 if (p
->second
.caps
.empty()) {
656 dout(0) << "import: no caps supplied" << dendl
;
659 KeyServerData::Incremental auth_inc
;
660 auth_inc
.name
= p
->first
;
661 auth_inc
.auth
= p
->second
;
662 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
663 dout(10) << " importing " << auth_inc
.name
<< dendl
;
664 dout(30) << " " << auth_inc
.auth
<< dendl
;
665 push_cephx_inc(auth_inc
);
670 bool AuthMonitor::prepare_command(MonOpRequestRef op
)
672 MMonCommand
*m
= static_cast<MMonCommand
*>(op
->get_req());
678 map
<string
, cmd_vartype
> cmdmap
;
679 if (!cmdmap_from_json(m
->cmd
, &cmdmap
, ss
)) {
680 // ss has reason for failure
681 string rs
= ss
.str();
682 mon
->reply_command(op
, -EINVAL
, rs
, rdata
, get_last_committed());
687 vector
<string
>caps_vec
;
691 cmd_getval(g_ceph_context
, cmdmap
, "prefix", prefix
);
694 cmd_getval(g_ceph_context
, cmdmap
, "format", format
, string("plain"));
695 boost::scoped_ptr
<Formatter
> f(Formatter::create(format
));
697 MonSession
*session
= m
->get_session();
699 mon
->reply_command(op
, -EACCES
, "access denied", rdata
, get_last_committed());
703 cmd_getval(g_ceph_context
, cmdmap
, "caps", caps_vec
);
704 if ((caps_vec
.size() % 2) != 0) {
705 ss
<< "bad capabilities request; odd number of arguments";
710 cmd_getval(g_ceph_context
, cmdmap
, "entity", entity_name
);
711 if (!entity_name
.empty() && !entity
.from_str(entity_name
)) {
712 ss
<< "bad entity name";
717 if (prefix
== "auth import") {
718 bufferlist bl
= m
->get_data();
719 if (bl
.length() == 0) {
720 ss
<< "auth import: no data supplied";
722 mon
->reply_command(op
, -EINVAL
, rs
, get_last_committed());
725 bufferlist::iterator iter
= bl
.begin();
728 ::decode(keyring
, iter
);
729 } catch (const buffer::error
&ex
) {
730 ss
<< "error decoding keyring" << " " << ex
.what();
734 err
= import_keyring(keyring
);
736 ss
<< "auth import: no caps supplied";
738 mon
->reply_command(op
, -EINVAL
, rs
, get_last_committed());
741 ss
<< "imported keyring";
744 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
745 get_last_committed() + 1));
747 } else if (prefix
== "auth add" && !entity_name
.empty()) {
748 /* expected behavior:
749 * - if command reproduces current state, return 0.
750 * - if command adds brand new entity, handle it.
751 * - if command adds new state to existing entity, return error.
753 KeyServerData::Incremental auth_inc
;
754 auth_inc
.name
= entity
;
755 bufferlist bl
= m
->get_data();
756 bool has_keyring
= (bl
.length() > 0);
757 map
<string
,bufferlist
> new_caps
;
761 bufferlist::iterator iter
= bl
.begin();
763 ::decode(new_keyring
, iter
);
764 } catch (const buffer::error
&ex
) {
765 ss
<< "error decoding keyring";
771 // are we about to have it?
772 for (vector
<Incremental
>::iterator p
= pending_auth
.begin();
773 p
!= pending_auth
.end();
775 if (p
->inc_type
== AUTH_DATA
) {
776 KeyServerData::Incremental inc
;
777 bufferlist::iterator q
= p
->auth_data
.begin();
779 if (inc
.op
== KeyServerData::AUTH_INC_ADD
&&
780 inc
.name
== entity
) {
781 wait_for_finished_proposal(op
,
782 new Monitor::C_Command(mon
, op
, 0, rs
, get_last_committed() + 1));
788 // build new caps from provided arguments (if available)
789 for (vector
<string
>::iterator it
= caps_vec
.begin();
790 it
!= caps_vec
.end() && (it
+ 1) != caps_vec
.end();
794 ::encode(*(it
+1), cap
);
798 // pull info out of provided keyring
801 if (!new_keyring
.get_auth(auth_inc
.name
, new_inc
)) {
802 ss
<< "key for " << auth_inc
.name
803 << " not found in provided keyring";
807 if (!new_caps
.empty() && !new_inc
.caps
.empty()) {
808 ss
<< "caps cannot be specified both in keyring and in command";
812 if (new_caps
.empty()) {
813 new_caps
= new_inc
.caps
;
817 // does entry already exist?
818 if (mon
->key_server
.get_auth(auth_inc
.name
, auth_inc
.auth
)) {
821 if (auth_inc
.auth
.key
.get_secret().cmp(new_inc
.key
.get_secret())) {
822 ss
<< "entity " << auth_inc
.name
<< " exists but key does not match";
829 if (new_caps
.size() != auth_inc
.auth
.caps
.size()) {
830 ss
<< "entity " << auth_inc
.name
<< " exists but caps do not match";
834 for (map
<string
,bufferlist
>::iterator it
= new_caps
.begin();
835 it
!= new_caps
.end(); ++it
) {
836 if (auth_inc
.auth
.caps
.count(it
->first
) == 0 ||
837 !auth_inc
.auth
.caps
[it
->first
].contents_equal(it
->second
)) {
838 ss
<< "entity " << auth_inc
.name
<< " exists but cap "
839 << it
->first
<< " does not match";
851 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
852 auth_inc
.auth
.caps
= new_caps
;
854 auth_inc
.auth
.key
= new_inc
.key
;
856 dout(10) << "AuthMonitor::prepare_command generating random key for "
857 << auth_inc
.name
<< dendl
;
858 auth_inc
.auth
.key
.create(g_ceph_context
, CEPH_CRYPTO_AES
);
861 dout(10) << " importing " << auth_inc
.name
<< dendl
;
862 dout(30) << " " << auth_inc
.auth
<< dendl
;
863 push_cephx_inc(auth_inc
);
865 ss
<< "added key for " << auth_inc
.name
;
867 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
868 get_last_committed() + 1));
870 } else if ((prefix
== "auth get-or-create-key" ||
871 prefix
== "auth get-or-create") &&
872 !entity_name
.empty()) {
873 // auth get-or-create <name> [mon osdcapa osd osdcapb ...]
875 if (!valid_caps(caps_vec
, &ss
)) {
880 // Parse the list of caps into a map
881 std::map
<std::string
, bufferlist
> wanted_caps
;
882 for (vector
<string
>::const_iterator it
= caps_vec
.begin();
883 it
!= caps_vec
.end() && (it
+ 1) != caps_vec
.end();
885 const std::string
&sys
= *it
;
887 ::encode(*(it
+1), cap
);
888 wanted_caps
[sys
] = cap
;
892 EntityAuth entity_auth
;
893 if (mon
->key_server
.get_auth(entity
, entity_auth
)) {
894 for (const auto &sys_cap
: wanted_caps
) {
895 if (entity_auth
.caps
.count(sys_cap
.first
) == 0 ||
896 !entity_auth
.caps
[sys_cap
.first
].contents_equal(sys_cap
.second
)) {
897 ss
<< "key for " << entity
<< " exists but cap " << sys_cap
.first
898 << " does not match";
904 if (prefix
== "auth get-or-create-key") {
906 entity_auth
.key
.encode_formatted("auth", f
.get(), rdata
);
908 ds
<< entity_auth
.key
;
912 kr
.add(entity
, entity_auth
.key
);
914 kr
.set_caps(entity
, entity_auth
.caps
);
915 kr
.encode_formatted("auth", f
.get(), rdata
);
917 kr
.encode_plaintext(rdata
);
924 // ...or are we about to?
925 for (vector
<Incremental
>::iterator p
= pending_auth
.begin();
926 p
!= pending_auth
.end();
928 if (p
->inc_type
== AUTH_DATA
) {
929 KeyServerData::Incremental auth_inc
;
930 bufferlist::iterator q
= p
->auth_data
.begin();
931 ::decode(auth_inc
, q
);
932 if (auth_inc
.op
== KeyServerData::AUTH_INC_ADD
&&
933 auth_inc
.name
== entity
) {
934 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
935 get_last_committed() + 1));
942 KeyServerData::Incremental auth_inc
;
943 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
944 auth_inc
.name
= entity
;
945 auth_inc
.auth
.key
.create(g_ceph_context
, CEPH_CRYPTO_AES
);
946 auth_inc
.auth
.caps
= wanted_caps
;
948 push_cephx_inc(auth_inc
);
950 if (prefix
== "auth get-or-create-key") {
952 auth_inc
.auth
.key
.encode_formatted("auth", f
.get(), rdata
);
954 ds
<< auth_inc
.auth
.key
;
958 kr
.add(entity
, auth_inc
.auth
.key
);
960 kr
.set_caps(entity
, wanted_caps
);
961 kr
.encode_formatted("auth", f
.get(), rdata
);
963 kr
.encode_plaintext(rdata
);
969 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
, rdata
,
970 get_last_committed() + 1));
972 } else if (prefix
== "auth caps" && !entity_name
.empty()) {
973 KeyServerData::Incremental auth_inc
;
974 auth_inc
.name
= entity
;
975 if (!mon
->key_server
.get_auth(auth_inc
.name
, auth_inc
.auth
)) {
976 ss
<< "couldn't find entry " << auth_inc
.name
;
981 if (!valid_caps(caps_vec
, &ss
)) {
986 map
<string
,bufferlist
> newcaps
;
987 for (vector
<string
>::iterator it
= caps_vec
.begin();
988 it
!= caps_vec
.end(); it
+= 2)
989 ::encode(*(it
+1), newcaps
[*it
]);
991 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
992 auth_inc
.auth
.caps
= newcaps
;
993 push_cephx_inc(auth_inc
);
995 ss
<< "updated caps for " << auth_inc
.name
;
997 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
998 get_last_committed() + 1));
1000 } else if ((prefix
== "auth del" || prefix
== "auth rm") &&
1001 !entity_name
.empty()) {
1002 KeyServerData::Incremental auth_inc
;
1003 auth_inc
.name
= entity
;
1004 if (!mon
->key_server
.contains(auth_inc
.name
)) {
1005 ss
<< "entity " << entity
<< " does not exist";
1009 auth_inc
.op
= KeyServerData::AUTH_INC_DEL
;
1010 push_cephx_inc(auth_inc
);
1014 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
1015 get_last_committed() + 1));
1021 getline(ss
, rs
, '\0');
1022 mon
->reply_command(op
, err
, rs
, rdata
, get_last_committed());
1026 bool AuthMonitor::prepare_global_id(MonOpRequestRef op
)
1028 dout(10) << "AuthMonitor::prepare_global_id" << dendl
;
1029 increase_max_global_id();
1034 void AuthMonitor::upgrade_format()
1036 unsigned int current
= 2;
1037 if (!mon
->get_quorum_mon_features().contains_all(
1038 ceph::features::mon::FEATURE_LUMINOUS
)) {
1041 if (format_version
>= current
) {
1042 dout(20) << __func__
<< " format " << format_version
<< " is current" << dendl
;
1046 bool changed
= false;
1047 if (format_version
== 0) {
1048 dout(1) << __func__
<< " upgrading from format 0 to 1" << dendl
;
1049 map
<EntityName
, EntityAuth
>::iterator p
;
1050 for (p
= mon
->key_server
.secrets_begin();
1051 p
!= mon
->key_server
.secrets_end();
1053 // grab mon caps, if any
1055 if (p
->second
.caps
.count("mon") == 0)
1058 bufferlist::iterator it
= p
->second
.caps
["mon"].begin();
1059 ::decode(mon_caps
, it
);
1061 catch (buffer::error
) {
1062 dout(10) << __func__
<< " unable to parse mon cap for "
1063 << p
->first
<< dendl
;
1067 string n
= p
->first
.to_str();
1070 // set daemon profiles
1071 if ((p
->first
.is_osd() || p
->first
.is_mds()) &&
1072 mon_caps
== "allow rwx") {
1073 new_caps
= string("allow profile ") + string(p
->first
.get_type_name());
1076 // update bootstrap keys
1077 if (n
== "client.bootstrap-osd") {
1078 new_caps
= "allow profile bootstrap-osd";
1080 if (n
== "client.bootstrap-mds") {
1081 new_caps
= "allow profile bootstrap-mds";
1084 if (new_caps
.length() > 0) {
1085 dout(5) << __func__
<< " updating " << p
->first
<< " mon cap from "
1086 << mon_caps
<< " to " << new_caps
<< dendl
;
1089 ::encode(new_caps
, bl
);
1091 KeyServerData::Incremental auth_inc
;
1092 auth_inc
.name
= p
->first
;
1093 auth_inc
.auth
= p
->second
;
1094 auth_inc
.auth
.caps
["mon"] = bl
;
1095 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1096 push_cephx_inc(auth_inc
);
1102 if (format_version
== 1) {
1103 dout(1) << __func__
<< " upgrading from format 1 to 2" << dendl
;
1104 map
<EntityName
, EntityAuth
>::iterator p
;
1105 for (p
= mon
->key_server
.secrets_begin();
1106 p
!= mon
->key_server
.secrets_end();
1108 string n
= p
->first
.to_str();
1111 if (n
== "client.admin") {
1112 // admin gets it all
1114 } else if (n
.find("osd.") == 0 ||
1115 n
.find("mds.") == 0 ||
1116 n
.find("mon.") == 0) {
1117 // daemons follow their profile
1118 string type
= n
.substr(0, 3);
1119 newcap
= "allow profile " + type
;
1120 } else if (p
->second
.caps
.count("mon")) {
1121 // if there are any mon caps, give them 'r' mgr caps
1125 if (newcap
.length() > 0) {
1126 dout(5) << " giving " << n
<< " mgr '" << newcap
<< "'" << dendl
;
1128 ::encode(newcap
, bl
);
1130 KeyServerData::Incremental auth_inc
;
1131 auth_inc
.name
= p
->first
;
1132 auth_inc
.auth
= p
->second
;
1133 auth_inc
.auth
.caps
["mgr"] = bl
;
1134 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1135 push_cephx_inc(auth_inc
);
1138 if (n
.find("mgr.") == 0 &&
1139 p
->second
.caps
.count("mon")) {
1140 // the kraken ceph-mgr@.service set the mon cap to 'allow *'.
1141 auto blp
= p
->second
.caps
["mon"].begin();
1143 ::decode(oldcaps
, blp
);
1144 if (oldcaps
== "allow *") {
1145 dout(5) << " fixing " << n
<< " mon cap to 'allow profile mgr'"
1148 ::encode("allow profile mgr", bl
);
1149 KeyServerData::Incremental auth_inc
;
1150 auth_inc
.name
= p
->first
;
1151 auth_inc
.auth
= p
->second
;
1152 auth_inc
.auth
.caps
["mon"] = bl
;
1153 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1154 push_cephx_inc(auth_inc
);
1159 // add bootstrap key
1161 KeyServerData::Incremental auth_inc
;
1162 bool r
= auth_inc
.name
.from_str("client.bootstrap-mgr");
1164 ::encode("allow profile bootstrap-mgr", auth_inc
.auth
.caps
["mon"]);
1165 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1166 push_cephx_inc(auth_inc
);
1173 dout(10) << __func__
<< " proposing update from format " << format_version
1174 << " -> " << current
<< dendl
;
1175 format_version
= current
;
1180 void AuthMonitor::dump_info(Formatter
*f
)
1182 /*** WARNING: do not include any privileged information here! ***/
1183 f
->open_object_section("auth");
1184 f
->dump_unsigned("first_committed", get_first_committed());
1185 f
->dump_unsigned("last_committed", get_last_committed());
1186 f
->dump_unsigned("num_secrets", mon
->key_server
.get_num_secrets());