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"
20 #include "mon/OSDMonitor.h"
21 #include "mon/MDSMonitor.h"
22 #include "mon/ConfigMonitor.h"
24 #include "messages/MMonCommand.h"
25 #include "messages/MAuth.h"
26 #include "messages/MAuthReply.h"
27 #include "messages/MMonGlobalID.h"
28 #include "messages/MMonUsedPendingKeys.h"
29 #include "msg/Messenger.h"
31 #include "auth/AuthServiceHandler.h"
32 #include "auth/KeyRing.h"
33 #include "include/stringify.h"
34 #include "include/ceph_assert.h"
36 #include "mds/MDSAuthCaps.h"
37 #include "mgr/MgrCap.h"
38 #include "osd/OSDCap.h"
40 #define dout_subsys ceph_subsys_mon
42 #define dout_prefix _prefix(_dout, mon, get_last_committed())
43 using namespace TOPNSPC::common
;
53 using std::ostringstream
;
58 using std::stringstream
;
61 using std::unique_ptr
;
63 using ceph::bufferlist
;
66 using ceph::Formatter
;
67 using ceph::JSONFormatter
;
68 using ceph::make_message
;
69 using ceph::mono_clock
;
70 using ceph::mono_time
;
71 using ceph::timespan_str
;
72 static ostream
& _prefix(std::ostream
*_dout
, Monitor
&mon
, version_t v
) {
73 return *_dout
<< "mon." << mon
.name
<< "@" << mon
.rank
74 << "(" << mon
.get_state_name()
75 << ").auth v" << v
<< " ";
78 ostream
& operator<<(ostream
&out
, const AuthMonitor
&pm
)
83 bool AuthMonitor::check_rotate()
85 KeyServerData::Incremental rot_inc
;
86 rot_inc
.op
= KeyServerData::AUTH_INC_SET_ROTATING
;
87 if (mon
.key_server
.prepare_rotating_update(rot_inc
.rotating_bl
)) {
88 dout(10) << __func__
<< " updating rotating" << dendl
;
89 push_cephx_inc(rot_inc
);
95 void AuthMonitor::process_used_pending_keys(
96 const std::map
<EntityName
,CryptoKey
>& used_pending_keys
)
98 for (auto& [name
, used_key
] : used_pending_keys
) {
99 dout(10) << __func__
<< " used pending_key for " << name
<< dendl
;
100 KeyServerData::Incremental inc
;
101 inc
.op
= KeyServerData::AUTH_INC_ADD
;
104 mon
.key_server
.get_auth(name
, inc
.auth
);
105 for (auto& p
: pending_auth
) {
106 if (p
.inc_type
== AUTH_DATA
) {
107 KeyServerData::Incremental auth_inc
;
108 auto q
= p
.auth_data
.cbegin();
110 if (auth_inc
.op
== KeyServerData::AUTH_INC_ADD
&&
111 auth_inc
.name
== name
) {
112 dout(10) << __func__
<< " starting with pending uncommitted" << dendl
;
113 inc
.auth
= auth_inc
.auth
;
117 if (stringify(inc
.auth
.pending_key
) == stringify(used_key
)) {
118 dout(10) << __func__
<< " committing pending_key -> key for "
120 inc
.auth
.key
= inc
.auth
.pending_key
;
121 inc
.auth
.pending_key
.clear();
128 Tick function to update the map based on performance every N seconds
131 void AuthMonitor::tick()
133 if (!is_active()) return;
135 dout(10) << *this << dendl
;
137 // increase global_id?
138 bool propose
= false;
141 std::lock_guard
l(mon
.auth_lock
);
142 increase
= _should_increase_max_global_id();
145 if (mon
.is_leader()) {
146 increase_max_global_id();
149 dout(10) << __func__
<< "requesting more ids from leader" << dendl
;
150 MMonGlobalID
*req
= new MMonGlobalID();
151 req
->old_max_id
= max_global_id
;
152 mon
.send_mon_message(req
, mon
.get_leader());
156 if (mon
.monmap
->min_mon_release
>= ceph_release_t::quincy
) {
157 auto used_pending_keys
= mon
.key_server
.get_used_pending_keys();
158 if (!used_pending_keys
.empty()) {
159 dout(10) << __func__
<< " " << used_pending_keys
.size() << " used pending_keys"
161 if (mon
.is_leader()) {
162 process_used_pending_keys(used_pending_keys
);
165 MMonUsedPendingKeys
*req
= new MMonUsedPendingKeys();
166 req
->used_pending_keys
= used_pending_keys
;
167 mon
.send_mon_message(req
, mon
.get_leader());
172 if (!mon
.is_leader()) {
176 if (check_rotate()) {
185 void AuthMonitor::on_active()
187 dout(10) << "AuthMonitor::on_active()" << dendl
;
189 if (!mon
.is_leader())
192 mon
.key_server
.start_server();
193 mon
.key_server
.clear_used_pending_keys();
195 if (is_writeable()) {
196 bool propose
= false;
197 if (check_rotate()) {
202 std::lock_guard
l(mon
.auth_lock
);
203 increase
= _should_increase_max_global_id();
206 increase_max_global_id();
215 bufferlist
_encode_cap(const string
& cap
)
222 void AuthMonitor::get_initial_keyring(KeyRing
*keyring
)
224 dout(10) << __func__
<< dendl
;
225 ceph_assert(keyring
!= nullptr);
228 int ret
= mon
.store
->get("mkfs", "keyring", bl
);
229 if (ret
== -ENOENT
) {
232 // fail hard only if there's an error we're not expecting to see
233 ceph_assert(ret
== 0);
235 auto p
= bl
.cbegin();
239 void _generate_bootstrap_keys(
240 list
<pair
<EntityName
,EntityAuth
> >* auth_lst
)
242 ceph_assert(auth_lst
!= nullptr);
244 map
<string
,map
<string
,bufferlist
> > bootstrap
= {
246 { "mon", _encode_cap("allow *") },
247 { "osd", _encode_cap("allow *") },
248 { "mds", _encode_cap("allow *") },
249 { "mgr", _encode_cap("allow *") }
252 { "mon", _encode_cap("allow profile bootstrap-osd") }
255 { "mon", _encode_cap("allow profile bootstrap-rgw") }
258 { "mon", _encode_cap("allow profile bootstrap-mds") }
261 { "mon", _encode_cap("allow profile bootstrap-mgr") }
264 { "mon", _encode_cap("allow profile bootstrap-rbd") }
266 { "bootstrap-rbd-mirror", {
267 { "mon", _encode_cap("allow profile bootstrap-rbd-mirror") }
271 for (auto &p
: bootstrap
) {
273 name
.from_str("client." + p
.first
);
275 auth
.key
.create(g_ceph_context
, CEPH_CRYPTO_AES
);
276 auth
.caps
= p
.second
;
278 auth_lst
->push_back(make_pair(name
, auth
));
282 void AuthMonitor::create_initial_keys(KeyRing
*keyring
)
284 dout(10) << __func__
<< " with keyring" << dendl
;
285 ceph_assert(keyring
!= nullptr);
287 list
<pair
<EntityName
,EntityAuth
> > auth_lst
;
288 _generate_bootstrap_keys(&auth_lst
);
290 for (auto &p
: auth_lst
) {
291 if (keyring
->exists(p
.first
)) {
294 keyring
->add(p
.first
, p
.second
);
298 void AuthMonitor::create_initial()
300 dout(10) << "create_initial -- creating initial map" << dendl
;
302 // initialize rotating keys
303 mon
.key_server
.clear_secrets();
305 ceph_assert(pending_auth
.size() == 1);
307 if (mon
.is_keyring_required()) {
309 // attempt to obtain an existing mkfs-time keyring
310 get_initial_keyring(&keyring
);
311 // create missing keys in the keyring
312 create_initial_keys(&keyring
);
313 // import the resulting keyring
314 import_keyring(keyring
);
317 max_global_id
= MIN_GLOBAL_ID
;
320 inc
.inc_type
= GLOBAL_ID
;
321 inc
.max_global_id
= max_global_id
;
322 pending_auth
.push_back(inc
);
327 void AuthMonitor::update_from_paxos(bool *need_bootstrap
)
329 dout(10) << __func__
<< dendl
;
332 version_t version
= get_last_committed();
333 version_t keys_ver
= mon
.key_server
.get_ver();
334 if (version
== keys_ver
)
336 ceph_assert(version
> keys_ver
);
338 version_t latest_full
= get_version_latest_full();
340 dout(10) << __func__
<< " version " << version
<< " keys ver " << keys_ver
341 << " latest " << latest_full
<< dendl
;
343 if ((latest_full
> 0) && (latest_full
> keys_ver
)) {
344 bufferlist latest_bl
;
345 int err
= get_version_full(latest_full
, latest_bl
);
346 ceph_assert(err
== 0);
347 ceph_assert(latest_bl
.length() != 0);
348 dout(7) << __func__
<< " loading summary e " << latest_full
<< dendl
;
349 dout(7) << __func__
<< " latest length " << latest_bl
.length() << dendl
;
350 auto p
= latest_bl
.cbegin();
353 decode(max_global_id
, p
);
354 decode(mon
.key_server
, p
);
355 mon
.key_server
.set_ver(latest_full
);
356 keys_ver
= latest_full
;
359 dout(10) << __func__
<< " key server version " << mon
.key_server
.get_ver() << dendl
;
361 // walk through incrementals
362 while (version
> keys_ver
) {
364 int ret
= get_version(keys_ver
+1, bl
);
365 ceph_assert(ret
== 0);
366 ceph_assert(bl
.length());
368 // reset if we are moving to initial state. we will normally have
369 // keys in here temporarily for bootstrapping that we need to
372 mon
.key_server
.clear_secrets();
374 dout(20) << __func__
<< " walking through version " << (keys_ver
+1)
375 << " len " << bl
.length() << dendl
;
377 auto p
= bl
.cbegin();
383 switch (inc
.inc_type
) {
385 max_global_id
= inc
.max_global_id
;
390 KeyServerData::Incremental auth_inc
;
391 auto iter
= inc
.auth_data
.cbegin();
392 decode(auth_inc
, iter
);
393 mon
.key_server
.apply_data_incremental(auth_inc
);
400 mon
.key_server
.set_ver(keys_ver
);
402 if (keys_ver
== 1 && mon
.is_keyring_required()) {
403 auto t(std::make_shared
<MonitorDBStore::Transaction
>());
404 t
->erase("mkfs", "keyring");
405 mon
.store
->apply_transaction(t
);
410 std::lock_guard
l(mon
.auth_lock
);
411 if (last_allocated_id
== 0) {
412 last_allocated_id
= max_global_id
;
413 dout(10) << __func__
<< " last_allocated_id initialized to "
414 << max_global_id
<< dendl
;
418 dout(10) << __func__
<< " max_global_id=" << max_global_id
419 << " format_version " << format_version
422 mon
.key_server
.dump();
425 bool AuthMonitor::_should_increase_max_global_id()
427 ceph_assert(ceph_mutex_is_locked(mon
.auth_lock
));
428 auto num_prealloc
= g_conf()->mon_globalid_prealloc
;
429 if (max_global_id
< num_prealloc
||
430 (last_allocated_id
+ 1) >= max_global_id
- num_prealloc
/ 2) {
436 void AuthMonitor::increase_max_global_id()
438 ceph_assert(mon
.is_leader());
441 inc
.inc_type
= GLOBAL_ID
;
442 inc
.max_global_id
= max_global_id
+ g_conf()->mon_globalid_prealloc
;
443 dout(10) << "increasing max_global_id to " << inc
.max_global_id
<< dendl
;
444 pending_auth
.push_back(inc
);
447 bool AuthMonitor::should_propose(double& delay
)
449 return (!pending_auth
.empty());
452 void AuthMonitor::create_pending()
454 pending_auth
.clear();
455 dout(10) << "create_pending v " << (get_last_committed() + 1) << dendl
;
458 void AuthMonitor::encode_pending(MonitorDBStore::TransactionRef t
)
460 dout(10) << __func__
<< " v " << (get_last_committed() + 1) << dendl
;
466 vector
<Incremental
>::iterator p
;
467 for (p
= pending_auth
.begin(); p
!= pending_auth
.end(); ++p
)
468 p
->encode(bl
, mon
.get_quorum_con_features());
470 version_t version
= get_last_committed() + 1;
471 put_version(t
, version
, bl
);
472 put_last_committed(t
, version
);
475 health_check_map_t next
;
476 map
<string
,list
<string
>> bad_detail
; // entity -> details
477 for (auto i
= mon
.key_server
.secrets_begin();
478 i
!= mon
.key_server
.secrets_end();
480 for (auto& p
: i
->second
.caps
) {
482 if (!valid_caps(p
.first
, p
.second
, &ss
)) {
484 ss2
<< i
->first
<< " " << ss
.str();
485 bad_detail
[i
->first
.to_str()].push_back(ss2
.str());
489 for (auto& inc
: pending_auth
) {
490 if (inc
.inc_type
== AUTH_DATA
) {
491 KeyServerData::Incremental auth_inc
;
492 auto iter
= inc
.auth_data
.cbegin();
493 decode(auth_inc
, iter
);
494 if (auth_inc
.op
== KeyServerData::AUTH_INC_DEL
) {
495 bad_detail
.erase(auth_inc
.name
.to_str());
496 } else if (auth_inc
.op
== KeyServerData::AUTH_INC_ADD
) {
497 for (auto& p
: auth_inc
.auth
.caps
) {
499 if (!valid_caps(p
.first
, p
.second
, &ss
)) {
501 ss2
<< auth_inc
.name
<< " " << ss
.str();
502 bad_detail
[auth_inc
.name
.to_str()].push_back(ss2
.str());
508 if (bad_detail
.size()) {
510 ss
<< bad_detail
.size() << " auth entities have invalid capabilities";
511 health_check_t
*check
= &next
.add("AUTH_BAD_CAPS", HEALTH_ERR
, ss
.str(),
513 for (auto& i
: bad_detail
) {
514 for (auto& j
: i
.second
) {
515 check
->detail
.push_back(j
);
519 encode_health(next
, t
);
522 void AuthMonitor::encode_full(MonitorDBStore::TransactionRef t
)
524 version_t version
= mon
.key_server
.get_ver();
525 // do not stash full version 0 as it will never be removed nor read
529 dout(10) << __func__
<< " auth v " << version
<< dendl
;
530 ceph_assert(get_last_committed() == version
);
533 std::scoped_lock l
{mon
.key_server
.get_lock()};
534 dout(20) << __func__
<< " key server has "
535 << (mon
.key_server
.has_secrets() ? "" : "no ")
536 << "secrets!" << dendl
;
539 encode(max_global_id
, full_bl
);
540 encode(mon
.key_server
, full_bl
);
542 put_version_full(t
, version
, full_bl
);
543 put_version_latest_full(t
, version
);
546 version_t
AuthMonitor::get_trim_to() const
548 unsigned max
= g_conf()->paxos_max_join_drift
* 2;
549 version_t version
= get_last_committed();
550 if (mon
.is_leader() && (version
> max
))
551 return version
- max
;
555 bool AuthMonitor::preprocess_query(MonOpRequestRef op
)
557 auto m
= op
->get_req
<PaxosServiceMessage
>();
558 dout(10) << "preprocess_query " << *m
<< " from " << m
->get_orig_source_inst() << dendl
;
559 switch (m
->get_type()) {
560 case MSG_MON_COMMAND
:
562 return preprocess_command(op
);
563 } catch (const bad_cmd_get
& e
) {
565 mon
.reply_command(op
, -EINVAL
, e
.what(), bl
, get_last_committed());
570 return prep_auth(op
, false);
572 case MSG_MON_GLOBAL_ID
:
575 case MSG_MON_USED_PENDING_KEYS
:
584 bool AuthMonitor::prepare_update(MonOpRequestRef op
)
586 auto m
= op
->get_req
<PaxosServiceMessage
>();
587 dout(10) << "prepare_update " << *m
<< " from " << m
->get_orig_source_inst() << dendl
;
588 switch (m
->get_type()) {
589 case MSG_MON_COMMAND
:
591 return prepare_command(op
);
592 } catch (const bad_cmd_get
& e
) {
594 mon
.reply_command(op
, -EINVAL
, e
.what(), bl
, get_last_committed());
597 case MSG_MON_GLOBAL_ID
:
598 return prepare_global_id(op
);
599 case MSG_MON_USED_PENDING_KEYS
:
600 return prepare_used_pending_keys(op
);
602 return prep_auth(op
, true);
609 void AuthMonitor::_set_mon_num_rank(int num
, int rank
)
611 dout(10) << __func__
<< " num " << num
<< " rank " << rank
<< dendl
;
612 ceph_assert(ceph_mutex_is_locked(mon
.auth_lock
));
617 uint64_t AuthMonitor::_assign_global_id()
619 ceph_assert(ceph_mutex_is_locked(mon
.auth_lock
));
620 if (mon_num
< 1 || mon_rank
< 0) {
621 dout(10) << __func__
<< " inactive (num_mon " << mon_num
622 << " rank " << mon_rank
<< ")" << dendl
;
625 if (!last_allocated_id
) {
626 dout(10) << __func__
<< " last_allocated_id == 0" << dendl
;
630 uint64_t id
= last_allocated_id
+ 1;
631 int remainder
= id
% mon_num
;
633 remainder
= mon_num
- remainder
;
635 id
+= remainder
+ mon_rank
;
637 if (id
>= max_global_id
) {
638 dout(10) << __func__
<< " failed (max " << max_global_id
<< ")" << dendl
;
642 last_allocated_id
= id
;
643 dout(10) << __func__
<< " " << id
<< " (max " << max_global_id
<< ")"
648 uint64_t AuthMonitor::assign_global_id(bool should_increase_max
)
652 std::lock_guard
l(mon
.auth_lock
);
653 id
=_assign_global_id();
654 if (should_increase_max
) {
655 should_increase_max
= _should_increase_max_global_id();
658 if (mon
.is_leader() &&
659 should_increase_max
) {
660 increase_max_global_id();
665 bool AuthMonitor::prep_auth(MonOpRequestRef op
, bool paxos_writable
)
667 auto m
= op
->get_req
<MAuth
>();
668 dout(10) << "prep_auth() blob_size=" << m
->get_auth_payload().length() << dendl
;
670 MonSession
*s
= op
->get_session();
672 dout(10) << "no session, dropping" << dendl
;
678 bufferlist response_bl
;
679 auto indata
= m
->auth_payload
.cbegin();
680 __u32 proto
= m
->protocol
;
682 bool finished
= false;
683 EntityName entity_name
;
684 bool is_new_global_id
= false;
687 if (m
->protocol
== 0 && !s
->auth_handler
) {
688 set
<__u32
> supported
;
692 decode(struct_v
, indata
);
693 decode(supported
, indata
);
694 decode(entity_name
, indata
);
695 decode(s
->con
->peer_global_id
, indata
);
696 } catch (const ceph::buffer::error
&e
) {
697 dout(10) << "failed to decode initial auth message" << dendl
;
702 // do we require cephx signatures?
704 if (!m
->get_connection()->has_feature(CEPH_FEATURE_MSG_AUTH
)) {
705 if (entity_name
.get_type() == CEPH_ENTITY_TYPE_MON
||
706 entity_name
.get_type() == CEPH_ENTITY_TYPE_OSD
||
707 entity_name
.get_type() == CEPH_ENTITY_TYPE_MDS
||
708 entity_name
.get_type() == CEPH_ENTITY_TYPE_MGR
) {
709 if (g_conf()->cephx_cluster_require_signatures
||
710 g_conf()->cephx_require_signatures
) {
711 dout(1) << m
->get_source_inst()
712 << " supports cephx but not signatures and"
713 << " 'cephx [cluster] require signatures = true';"
714 << " disallowing cephx" << dendl
;
715 supported
.erase(CEPH_AUTH_CEPHX
);
718 if (g_conf()->cephx_service_require_signatures
||
719 g_conf()->cephx_require_signatures
) {
720 dout(1) << m
->get_source_inst()
721 << " supports cephx but not signatures and"
722 << " 'cephx [service] require signatures = true';"
723 << " disallowing cephx" << dendl
;
724 supported
.erase(CEPH_AUTH_CEPHX
);
727 } else if (!m
->get_connection()->has_feature(CEPH_FEATURE_CEPHX_V2
)) {
728 if (entity_name
.get_type() == CEPH_ENTITY_TYPE_MON
||
729 entity_name
.get_type() == CEPH_ENTITY_TYPE_OSD
||
730 entity_name
.get_type() == CEPH_ENTITY_TYPE_MDS
||
731 entity_name
.get_type() == CEPH_ENTITY_TYPE_MGR
) {
732 if (g_conf()->cephx_cluster_require_version
>= 2 ||
733 g_conf()->cephx_require_version
>= 2) {
734 dout(1) << m
->get_source_inst()
735 << " supports cephx but not v2 and"
736 << " 'cephx [cluster] require version >= 2';"
737 << " disallowing cephx" << dendl
;
738 supported
.erase(CEPH_AUTH_CEPHX
);
741 if (g_conf()->cephx_service_require_version
>= 2 ||
742 g_conf()->cephx_require_version
>= 2) {
743 dout(1) << m
->get_source_inst()
744 << " supports cephx but not v2 and"
745 << " 'cephx [service] require version >= 2';"
746 << " disallowing cephx" << dendl
;
747 supported
.erase(CEPH_AUTH_CEPHX
);
753 if (entity_name
.get_type() == CEPH_ENTITY_TYPE_MON
||
754 entity_name
.get_type() == CEPH_ENTITY_TYPE_OSD
||
755 entity_name
.get_type() == CEPH_ENTITY_TYPE_MDS
||
756 entity_name
.get_type() == CEPH_ENTITY_TYPE_MGR
)
757 type
= mon
.auth_cluster_required
.pick(supported
);
759 type
= mon
.auth_service_required
.pick(supported
);
761 s
->auth_handler
= get_auth_service_handler(type
, g_ceph_context
, &mon
.key_server
);
762 if (!s
->auth_handler
) {
763 dout(1) << "client did not provide supported auth type" << dendl
;
769 } else if (!s
->auth_handler
) {
770 dout(10) << "protocol specified but no s->auth_handler" << dendl
;
775 /* assign a new global_id? we assume this should only happen on the first
776 request. If a client tries to send it later, it'll screw up its auth
778 if (!s
->con
->peer_global_id
) {
779 s
->con
->peer_global_id
= assign_global_id(paxos_writable
);
780 if (!s
->con
->peer_global_id
) {
782 delete s
->auth_handler
;
783 s
->auth_handler
= NULL
;
785 if (mon
.is_leader() && paxos_writable
) {
786 dout(10) << "increasing global id, waitlisting message" << dendl
;
787 wait_for_active(op
, new C_RetryMessage(this, op
));
791 if (!mon
.is_leader()) {
792 dout(10) << "not the leader, requesting more ids from leader" << dendl
;
793 int leader
= mon
.get_leader();
794 MMonGlobalID
*req
= new MMonGlobalID();
795 req
->old_max_id
= max_global_id
;
796 mon
.send_mon_message(req
, leader
);
797 wait_for_finished_proposal(op
, new C_RetryMessage(this, op
));
801 ceph_assert(!paxos_writable
);
804 is_new_global_id
= true;
810 ret
= s
->auth_handler
->start_session(entity_name
,
811 s
->con
->peer_global_id
,
814 &s
->con
->peer_caps_info
);
817 ret
= s
->auth_handler
->handle_request(
819 0, // no connection_secret needed
821 &s
->con
->peer_caps_info
,
825 wait_for_active(op
, new C_RetryMessage(this,op
));
829 if (!s
->authenticated
&&
830 mon
.ms_handle_authentication(s
->con
.get()) > 0) {
835 } catch (const ceph::buffer::error
&err
) {
837 dout(0) << "caught error when trying to handle auth request, probably malformed request" << dendl
;
841 reply
= new MAuthReply(proto
, &response_bl
, ret
, s
->con
->peer_global_id
);
842 mon
.send_reply(op
, reply
);
844 // always send the latest monmap.
845 if (m
->monmap_epoch
< mon
.monmap
->get_epoch())
846 mon
.send_latest_monmap(m
->get_connection().get());
848 mon
.configmon()->check_sub(s
);
854 bool AuthMonitor::preprocess_command(MonOpRequestRef op
)
856 auto m
= op
->get_req
<MMonCommand
>();
862 if (!cmdmap_from_json(m
->cmd
, &cmdmap
, ss
)) {
863 // ss has reason for failure
864 string rs
= ss
.str();
865 mon
.reply_command(op
, -EINVAL
, rs
, rdata
, get_last_committed());
870 cmd_getval(cmdmap
, "prefix", prefix
);
871 if (prefix
== "auth add" ||
872 prefix
== "auth del" ||
873 prefix
== "auth rm" ||
874 prefix
== "auth get-or-create" ||
875 prefix
== "auth get-or-create-key" ||
876 prefix
== "auth get-or-create-pending" ||
877 prefix
== "auth clear-pending" ||
878 prefix
== "auth commit-pending" ||
879 prefix
== "fs authorize" ||
880 prefix
== "auth import" ||
881 prefix
== "auth caps") {
885 MonSession
*session
= op
->get_session();
887 mon
.reply_command(op
, -EACCES
, "access denied", rdata
, get_last_committed());
891 // entity might not be supplied, but if it is, it should be valid
893 cmd_getval(cmdmap
, "entity", entity_name
);
895 if (!entity_name
.empty() && !entity
.from_str(entity_name
)) {
896 ss
<< "invalid entity_auth " << entity_name
;
897 mon
.reply_command(op
, -EINVAL
, ss
.str(), get_last_committed());
901 string format
= cmd_getval_or
<string
>(cmdmap
, "format", "plain");
902 boost::scoped_ptr
<Formatter
> f(Formatter::create(format
));
904 if (prefix
== "auth export") {
906 export_keyring(keyring
);
907 if (!entity_name
.empty()) {
909 if (keyring
.get_auth(entity
, eauth
)) {
911 kr
.add(entity
, eauth
);
913 kr
.encode_formatted("auth", f
.get(), rdata
);
915 kr
.encode_plaintext(rdata
);
918 ss
<< "no key for " << eauth
;
923 keyring
.encode_formatted("auth", f
.get(), rdata
);
925 keyring
.encode_plaintext(rdata
);
928 } else if (prefix
== "auth get" && !entity_name
.empty()) {
930 EntityAuth entity_auth
;
931 if (!mon
.key_server
.get_auth(entity
, entity_auth
)) {
932 ss
<< "failed to find " << entity_name
<< " in keyring";
935 keyring
.add(entity
, entity_auth
);
937 keyring
.encode_formatted("auth", f
.get(), rdata
);
939 keyring
.encode_plaintext(rdata
);
942 } else if (prefix
== "auth print-key" ||
943 prefix
== "auth print_key" ||
944 prefix
== "auth get-key") {
946 if (!mon
.key_server
.get_auth(entity
, auth
)) {
947 ss
<< "don't have " << entity
;
952 auth
.key
.encode_formatted("auth", f
.get(), rdata
);
954 auth
.key
.encode_plaintext(rdata
);
957 } else if (prefix
== "auth list" ||
958 prefix
== "auth ls") {
960 mon
.key_server
.encode_formatted("auth", f
.get(), rdata
);
962 mon
.key_server
.encode_plaintext(rdata
);
967 ss
<< "invalid command";
974 getline(ss
, rs
, '\0');
975 mon
.reply_command(op
, r
, rs
, rdata
, get_last_committed());
979 void AuthMonitor::export_keyring(KeyRing
& keyring
)
981 mon
.key_server
.export_keyring(keyring
);
984 int AuthMonitor::import_keyring(KeyRing
& keyring
)
986 dout(10) << __func__
<< " " << keyring
.size() << " keys" << dendl
;
988 for (map
<EntityName
, EntityAuth
>::iterator p
= keyring
.get_keys().begin();
989 p
!= keyring
.get_keys().end();
991 if (p
->second
.caps
.empty()) {
992 dout(0) << "import: no caps supplied" << dendl
;
995 int err
= add_entity(p
->first
, p
->second
);
996 ceph_assert(err
== 0);
1001 int AuthMonitor::remove_entity(const EntityName
&entity
)
1003 dout(10) << __func__
<< " " << entity
<< dendl
;
1004 if (!mon
.key_server
.contains(entity
))
1007 KeyServerData::Incremental auth_inc
;
1008 auth_inc
.name
= entity
;
1009 auth_inc
.op
= KeyServerData::AUTH_INC_DEL
;
1010 push_cephx_inc(auth_inc
);
1015 bool AuthMonitor::entity_is_pending(EntityName
& entity
)
1017 // are we about to have it?
1018 for (auto& p
: pending_auth
) {
1019 if (p
.inc_type
== AUTH_DATA
) {
1020 KeyServerData::Incremental inc
;
1021 auto q
= p
.auth_data
.cbegin();
1023 if (inc
.op
== KeyServerData::AUTH_INC_ADD
&&
1024 inc
.name
== entity
) {
1032 int AuthMonitor::exists_and_matches_entity(
1033 const auth_entity_t
& entity
,
1037 return exists_and_matches_entity(entity
.name
, entity
.auth
,
1038 entity
.auth
.caps
, has_secret
, ss
);
1041 int AuthMonitor::exists_and_matches_entity(
1042 const EntityName
& name
,
1043 const EntityAuth
& auth
,
1044 const map
<string
,bufferlist
>& caps
,
1049 dout(20) << __func__
<< " entity " << name
<< " auth " << auth
1050 << " caps " << caps
<< " has_secret " << has_secret
<< dendl
;
1052 EntityAuth existing_auth
;
1053 // does entry already exist?
1054 if (mon
.key_server
.get_auth(name
, existing_auth
)) {
1057 if (existing_auth
.key
.get_secret().cmp(auth
.key
.get_secret())) {
1058 ss
<< "entity " << name
<< " exists but key does not match";
1064 if (caps
.size() != existing_auth
.caps
.size()) {
1065 ss
<< "entity " << name
<< " exists but caps do not match";
1068 for (auto& it
: caps
) {
1069 if (existing_auth
.caps
.count(it
.first
) == 0 ||
1070 !existing_auth
.caps
[it
.first
].contents_equal(it
.second
)) {
1071 ss
<< "entity " << name
<< " exists but cap "
1072 << it
.first
<< " does not match";
1077 // they match, no-op
1083 int AuthMonitor::add_entity(
1084 const EntityName
& name
,
1085 const EntityAuth
& auth
)
1089 KeyServerData::Incremental auth_inc
;
1090 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1091 auth_inc
.name
= name
;
1092 auth_inc
.auth
= auth
;
1094 dout(10) << " add auth entity " << auth_inc
.name
<< dendl
;
1095 dout(30) << " " << auth_inc
.auth
<< dendl
;
1096 push_cephx_inc(auth_inc
);
1100 int AuthMonitor::validate_osd_destroy(
1103 EntityName
& cephx_entity
,
1104 EntityName
& lockbox_entity
,
1107 ceph_assert(paxos
.is_plugged());
1109 dout(10) << __func__
<< " id " << id
<< " uuid " << uuid
<< dendl
;
1111 string cephx_str
= "osd." + stringify(id
);
1112 string lockbox_str
= "client.osd-lockbox." + stringify(uuid
);
1114 if (!cephx_entity
.from_str(cephx_str
)) {
1115 dout(10) << __func__
<< " invalid cephx entity '"
1116 << cephx_str
<< "'" << dendl
;
1117 ss
<< "invalid cephx key entity '" << cephx_str
<< "'";
1121 if (!lockbox_entity
.from_str(lockbox_str
)) {
1122 dout(10) << __func__
<< " invalid lockbox entity '"
1123 << lockbox_str
<< "'" << dendl
;
1124 ss
<< "invalid lockbox key entity '" << lockbox_str
<< "'";
1128 if (!mon
.key_server
.contains(cephx_entity
) &&
1129 !mon
.key_server
.contains(lockbox_entity
)) {
1136 int AuthMonitor::do_osd_destroy(
1137 const EntityName
& cephx_entity
,
1138 const EntityName
& lockbox_entity
)
1140 ceph_assert(paxos
.is_plugged());
1142 dout(10) << __func__
<< " cephx " << cephx_entity
1143 << " lockbox " << lockbox_entity
<< dendl
;
1145 bool removed
= false;
1147 int err
= remove_entity(cephx_entity
);
1148 if (err
== -ENOENT
) {
1149 dout(10) << __func__
<< " " << cephx_entity
<< " does not exist" << dendl
;
1154 err
= remove_entity(lockbox_entity
);
1155 if (err
== -ENOENT
) {
1156 dout(10) << __func__
<< " " << lockbox_entity
<< " does not exist" << dendl
;
1162 dout(10) << __func__
<< " entities do not exist -- no-op." << dendl
;
1166 // given we have paxos plugged, this will not result in a proposal
1167 // being triggered, but it will still be needed so that we get our
1168 // pending state encoded into the paxos' pending transaction.
1176 const map
<string
,bufferlist
>& caps
)
1181 auth
.key
.decode_base64(key
);
1182 } catch (ceph::buffer::error
& e
) {
1189 int AuthMonitor::validate_osd_new(
1192 const string
& cephx_secret
,
1193 const string
& lockbox_secret
,
1194 auth_entity_t
& cephx_entity
,
1195 auth_entity_t
& lockbox_entity
,
1199 dout(10) << __func__
<< " osd." << id
<< " uuid " << uuid
<< dendl
;
1201 map
<string
,bufferlist
> cephx_caps
= {
1202 { "osd", _encode_cap("allow *") },
1203 { "mon", _encode_cap("allow profile osd") },
1204 { "mgr", _encode_cap("allow profile osd") }
1206 map
<string
,bufferlist
> lockbox_caps
= {
1207 { "mon", _encode_cap("allow command \"config-key get\" "
1208 "with key=\"dm-crypt/osd/" +
1213 bool has_lockbox
= !lockbox_secret
.empty();
1215 string cephx_name
= "osd." + stringify(id
);
1216 string lockbox_name
= "client.osd-lockbox." + stringify(uuid
);
1218 if (!cephx_entity
.name
.from_str(cephx_name
)) {
1219 dout(10) << __func__
<< " invalid cephx entity '"
1220 << cephx_name
<< "'" << dendl
;
1221 ss
<< "invalid cephx key entity '" << cephx_name
<< "'";
1226 if (!lockbox_entity
.name
.from_str(lockbox_name
)) {
1227 dout(10) << __func__
<< " invalid cephx lockbox entity '"
1228 << lockbox_name
<< "'" << dendl
;
1229 ss
<< "invalid cephx lockbox entity '" << lockbox_name
<< "'";
1234 if (entity_is_pending(cephx_entity
.name
) ||
1235 (has_lockbox
&& entity_is_pending(lockbox_entity
.name
))) {
1236 // If we have pending entities for either the cephx secret or the
1237 // lockbox secret, then our safest bet is to retry the command at
1238 // a later time. These entities may be pending because an `osd new`
1239 // command has been run (which is unlikely, due to the nature of
1240 // the operation, which will force a paxos proposal), or (more likely)
1241 // because a competing client created those entities before we handled
1242 // the `osd new` command. Regardless, let's wait and see.
1246 if (!is_valid_cephx_key(cephx_secret
)) {
1247 ss
<< "invalid cephx secret.";
1251 if (has_lockbox
&& !is_valid_cephx_key(lockbox_secret
)) {
1252 ss
<< "invalid cephx lockbox secret.";
1256 int err
= _create_auth(cephx_entity
.auth
, cephx_secret
, cephx_caps
);
1257 ceph_assert(0 == err
);
1259 bool cephx_is_idempotent
= false, lockbox_is_idempotent
= false;
1260 err
= exists_and_matches_entity(cephx_entity
, true, ss
);
1262 if (err
!= -ENOENT
) {
1266 ceph_assert(0 == err
);
1267 cephx_is_idempotent
= true;
1271 err
= _create_auth(lockbox_entity
.auth
, lockbox_secret
, lockbox_caps
);
1272 ceph_assert(err
== 0);
1273 err
= exists_and_matches_entity(lockbox_entity
, true, ss
);
1274 if (err
!= -ENOENT
) {
1278 ceph_assert(0 == err
);
1279 lockbox_is_idempotent
= true;
1283 if (cephx_is_idempotent
&& (!has_lockbox
|| lockbox_is_idempotent
)) {
1290 int AuthMonitor::do_osd_new(
1291 const auth_entity_t
& cephx_entity
,
1292 const auth_entity_t
& lockbox_entity
,
1295 ceph_assert(paxos
.is_plugged());
1297 dout(10) << __func__
<< " cephx " << cephx_entity
.name
1300 *_dout
<< lockbox_entity
.name
;
1306 // we must have validated before reaching this point.
1307 // if keys exist, then this means they also match; otherwise we would
1308 // have failed before calling this function.
1309 bool cephx_exists
= mon
.key_server
.contains(cephx_entity
.name
);
1311 if (!cephx_exists
) {
1312 int err
= add_entity(cephx_entity
.name
, cephx_entity
.auth
);
1313 ceph_assert(0 == err
);
1317 !mon
.key_server
.contains(lockbox_entity
.name
)) {
1318 int err
= add_entity(lockbox_entity
.name
, lockbox_entity
.auth
);
1319 ceph_assert(0 == err
);
1322 // given we have paxos plugged, this will not result in a proposal
1323 // being triggered, but it will still be needed so that we get our
1324 // pending state encoded into the paxos' pending transaction.
1329 bool AuthMonitor::valid_caps(
1334 if (type
== "mon") {
1336 if (!moncap
.parse(caps
, out
)) {
1342 if (!g_conf().get_val
<bool>("mon_auth_validate_all_caps")) {
1346 if (type
== "mgr") {
1348 if (!mgrcap
.parse(caps
, out
)) {
1351 } else if (type
== "osd") {
1353 if (!ocap
.parse(caps
, out
)) {
1356 } else if (type
== "mds") {
1358 if (!mdscap
.parse(g_ceph_context
, caps
, out
)) {
1363 *out
<< "unknown cap type '" << type
<< "'";
1370 bool AuthMonitor::valid_caps(const vector
<string
>& caps
, ostream
*out
)
1372 for (vector
<string
>::const_iterator p
= caps
.begin();
1373 p
!= caps
.end(); p
+= 2) {
1374 if ((p
+1) == caps
.end()) {
1375 *out
<< "cap '" << *p
<< "' has no value";
1378 if (!valid_caps(*p
, *(p
+1), out
)) {
1385 bool AuthMonitor::prepare_command(MonOpRequestRef op
)
1387 auto m
= op
->get_req
<MMonCommand
>();
1388 stringstream ss
, ds
;
1394 if (!cmdmap_from_json(m
->cmd
, &cmdmap
, ss
)) {
1395 // ss has reason for failure
1396 string rs
= ss
.str();
1397 mon
.reply_command(op
, -EINVAL
, rs
, rdata
, get_last_committed());
1402 vector
<string
>caps_vec
;
1406 cmd_getval(cmdmap
, "prefix", prefix
);
1408 string format
= cmd_getval_or
<string
>(cmdmap
, "format", "plain");
1409 boost::scoped_ptr
<Formatter
> f(Formatter::create(format
));
1411 MonSession
*session
= op
->get_session();
1413 mon
.reply_command(op
, -EACCES
, "access denied", rdata
, get_last_committed());
1417 cmd_getval(cmdmap
, "caps", caps_vec
);
1418 // fs authorize command's can have odd number of caps arguments
1419 if ((prefix
!= "fs authorize") && (caps_vec
.size() % 2) != 0) {
1420 ss
<< "bad capabilities request; odd number of arguments";
1425 cmd_getval(cmdmap
, "entity", entity_name
);
1426 if (!entity_name
.empty() && !entity
.from_str(entity_name
)) {
1427 ss
<< "bad entity name";
1432 if (prefix
== "auth import") {
1433 bufferlist bl
= m
->get_data();
1434 if (bl
.length() == 0) {
1435 ss
<< "auth import: no data supplied";
1437 mon
.reply_command(op
, -EINVAL
, rs
, get_last_committed());
1440 auto iter
= bl
.cbegin();
1443 decode(keyring
, iter
);
1444 } catch (const ceph::buffer::error
&ex
) {
1445 ss
<< "error decoding keyring" << " " << ex
.what();
1449 err
= import_keyring(keyring
);
1451 ss
<< "auth import: no caps supplied";
1453 mon
.reply_command(op
, -EINVAL
, rs
, get_last_committed());
1457 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
1458 get_last_committed() + 1));
1460 } else if (prefix
== "auth add" && !entity_name
.empty()) {
1461 /* expected behavior:
1462 * - if command reproduces current state, return 0.
1463 * - if command adds brand new entity, handle it.
1464 * - if command adds new state to existing entity, return error.
1466 KeyServerData::Incremental auth_inc
;
1467 auth_inc
.name
= entity
;
1468 bufferlist bl
= m
->get_data();
1469 bool has_keyring
= (bl
.length() > 0);
1470 map
<string
,bufferlist
> new_caps
;
1472 KeyRing new_keyring
;
1474 auto iter
= bl
.cbegin();
1476 decode(new_keyring
, iter
);
1477 } catch (const ceph::buffer::error
&ex
) {
1478 ss
<< "error decoding keyring";
1484 if (!valid_caps(caps_vec
, &ss
)) {
1489 // are we about to have it?
1490 if (entity_is_pending(entity
)) {
1491 wait_for_finished_proposal(op
,
1492 new Monitor::C_Command(mon
, op
, 0, rs
, get_last_committed() + 1));
1496 // build new caps from provided arguments (if available)
1497 for (vector
<string
>::iterator it
= caps_vec
.begin();
1498 it
!= caps_vec
.end() && (it
+ 1) != caps_vec
.end();
1502 encode(*(it
+1), cap
);
1503 new_caps
[sys
] = cap
;
1506 // pull info out of provided keyring
1509 if (!new_keyring
.get_auth(auth_inc
.name
, new_inc
)) {
1510 ss
<< "key for " << auth_inc
.name
1511 << " not found in provided keyring";
1515 if (!new_caps
.empty() && !new_inc
.caps
.empty()) {
1516 ss
<< "caps cannot be specified both in keyring and in command";
1520 if (new_caps
.empty()) {
1521 new_caps
= new_inc
.caps
;
1525 err
= exists_and_matches_entity(auth_inc
.name
, new_inc
,
1526 new_caps
, has_keyring
, ss
);
1527 // if entity/key/caps do not exist in the keyring, just fall through
1528 // and add the entity; otherwise, make sure everything matches (in
1529 // which case it's a no-op), because if not we must fail.
1530 if (err
!= -ENOENT
) {
1535 ceph_assert(err
== 0);
1542 dout(10) << "AuthMonitor::prepare_command generating random key for "
1543 << auth_inc
.name
<< dendl
;
1544 new_inc
.key
.create(g_ceph_context
, CEPH_CRYPTO_AES
);
1546 new_inc
.caps
= new_caps
;
1548 err
= add_entity(auth_inc
.name
, new_inc
);
1549 ceph_assert(err
== 0);
1551 ss
<< "added key for " << auth_inc
.name
;
1553 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
1554 get_last_committed() + 1));
1556 } else if ((prefix
== "auth get-or-create-pending" ||
1557 prefix
== "auth clear-pending" ||
1558 prefix
== "auth commit-pending")) {
1559 if (mon
.monmap
->min_mon_release
< ceph_release_t::quincy
) {
1561 ss
<< "pending_keys are not available until after upgrading to quincy";
1565 EntityAuth entity_auth
;
1566 if (!mon
.key_server
.get_auth(entity
, entity_auth
)) {
1567 ss
<< "entity " << entity
<< " does not exist";
1572 // is there an uncommitted pending_key? (or any change for this entity)
1573 for (auto& p
: pending_auth
) {
1574 if (p
.inc_type
== AUTH_DATA
) {
1575 KeyServerData::Incremental auth_inc
;
1576 auto q
= p
.auth_data
.cbegin();
1577 decode(auth_inc
, q
);
1578 if (auth_inc
.op
== KeyServerData::AUTH_INC_ADD
&&
1579 auth_inc
.name
== entity
) {
1580 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
1581 get_last_committed() + 1));
1587 if (prefix
== "auth get-or-create-pending") {
1589 bool exists
= false;
1590 if (!entity_auth
.pending_key
.empty()) {
1591 kr
.add(entity
, entity_auth
.key
, entity_auth
.pending_key
);
1595 KeyServerData::Incremental auth_inc
;
1596 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1597 auth_inc
.name
= entity
;
1598 auth_inc
.auth
= entity_auth
;
1599 auth_inc
.auth
.pending_key
.create(g_ceph_context
, CEPH_CRYPTO_AES
);
1600 push_cephx_inc(auth_inc
);
1601 kr
.add(entity
, auth_inc
.auth
.key
, auth_inc
.auth
.pending_key
);
1602 push_cephx_inc(auth_inc
);
1605 kr
.encode_formatted("auth", f
.get(), rdata
);
1607 kr
.encode_plaintext(rdata
);
1612 } else if (prefix
== "auth clear-pending") {
1613 if (entity_auth
.pending_key
.empty()) {
1617 KeyServerData::Incremental auth_inc
;
1618 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1619 auth_inc
.name
= entity
;
1620 auth_inc
.auth
= entity_auth
;
1621 auth_inc
.auth
.pending_key
.clear();
1622 push_cephx_inc(auth_inc
);
1623 } else if (prefix
== "auth commit-pending") {
1624 if (entity_auth
.pending_key
.empty()) {
1626 ss
<< "no pending key";
1629 KeyServerData::Incremental auth_inc
;
1630 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1631 auth_inc
.name
= entity
;
1632 auth_inc
.auth
= entity_auth
;
1633 auth_inc
.auth
.key
= auth_inc
.auth
.pending_key
;
1634 auth_inc
.auth
.pending_key
.clear();
1635 push_cephx_inc(auth_inc
);
1637 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
, rdata
,
1638 get_last_committed() + 1));
1640 } else if ((prefix
== "auth get-or-create-key" ||
1641 prefix
== "auth get-or-create") &&
1642 !entity_name
.empty()) {
1643 // auth get-or-create <name> [mon osdcapa osd osdcapb ...]
1645 if (!valid_caps(caps_vec
, &ss
)) {
1650 // Parse the list of caps into a map
1651 std::map
<std::string
, bufferlist
> wanted_caps
;
1652 for (vector
<string
>::const_iterator it
= caps_vec
.begin();
1653 it
!= caps_vec
.end() && (it
+ 1) != caps_vec
.end();
1655 const std::string
&sys
= *it
;
1657 encode(*(it
+1), cap
);
1658 wanted_caps
[sys
] = cap
;
1662 EntityAuth entity_auth
;
1663 if (mon
.key_server
.get_auth(entity
, entity_auth
)) {
1664 for (const auto &sys_cap
: wanted_caps
) {
1665 if (entity_auth
.caps
.count(sys_cap
.first
) == 0 ||
1666 !entity_auth
.caps
[sys_cap
.first
].contents_equal(sys_cap
.second
)) {
1667 ss
<< "key for " << entity
<< " exists but cap " << sys_cap
.first
1668 << " does not match";
1674 if (prefix
== "auth get-or-create-key") {
1676 entity_auth
.key
.encode_formatted("auth", f
.get(), rdata
);
1678 ds
<< entity_auth
.key
;
1682 kr
.add(entity
, entity_auth
.key
, entity_auth
.pending_key
);
1684 kr
.set_caps(entity
, entity_auth
.caps
);
1685 kr
.encode_formatted("auth", f
.get(), rdata
);
1687 kr
.encode_plaintext(rdata
);
1694 // ...or are we about to?
1695 for (vector
<Incremental
>::iterator p
= pending_auth
.begin();
1696 p
!= pending_auth
.end();
1698 if (p
->inc_type
== AUTH_DATA
) {
1699 KeyServerData::Incremental auth_inc
;
1700 auto q
= p
->auth_data
.cbegin();
1701 decode(auth_inc
, q
);
1702 if (auth_inc
.op
== KeyServerData::AUTH_INC_ADD
&&
1703 auth_inc
.name
== entity
) {
1704 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
1705 get_last_committed() + 1));
1712 KeyServerData::Incremental auth_inc
;
1713 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1714 auth_inc
.name
= entity
;
1715 auth_inc
.auth
.key
.create(g_ceph_context
, CEPH_CRYPTO_AES
);
1716 auth_inc
.auth
.caps
= wanted_caps
;
1718 push_cephx_inc(auth_inc
);
1720 if (prefix
== "auth get-or-create-key") {
1722 auth_inc
.auth
.key
.encode_formatted("auth", f
.get(), rdata
);
1724 ds
<< auth_inc
.auth
.key
;
1728 kr
.add(entity
, auth_inc
.auth
.key
);
1730 kr
.set_caps(entity
, wanted_caps
);
1731 kr
.encode_formatted("auth", f
.get(), rdata
);
1733 kr
.encode_plaintext(rdata
);
1739 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
, rdata
,
1740 get_last_committed() + 1));
1742 } else if (prefix
== "fs authorize") {
1744 cmd_getval(cmdmap
, "filesystem", filesystem
);
1745 string mon_cap_string
= "allow r";
1746 string mds_cap_string
, osd_cap_string
;
1747 string osd_cap_wanted
= "r";
1749 std::shared_ptr
<const Filesystem
> fs
;
1750 if (filesystem
!= "*" && filesystem
!= "all") {
1751 fs
= mon
.mdsmon()->get_fsmap().get_filesystem(filesystem
);
1752 if (fs
== nullptr) {
1753 ss
<< "filesystem " << filesystem
<< " does not exist.";
1757 mon_cap_string
+= " fsname=" + std::string(fs
->mds_map
.get_fs_name());
1761 for (auto it
= caps_vec
.begin();
1762 it
!= caps_vec
.end() && (it
+ 1) != caps_vec
.end();
1764 const string
&path
= *it
;
1765 const string
&cap
= *(it
+1);
1766 bool root_squash
= false;
1767 if ((it
+ 2) != caps_vec
.end() && *(it
+2) == "root_squash") {
1772 if (cap
!= "r" && cap
.compare(0, 2, "rw")) {
1773 ss
<< "Permission flags must start with 'r' or 'rw'.";
1777 if (cap
.compare(0, 2, "rw") == 0)
1778 osd_cap_wanted
= "rw";
1781 for (size_t i
= 2; i
< cap
.size(); ++i
) {
1784 ss
<< "Permission flags (except 'rw') must be specified in alphabetical order.";
1794 ss
<< "Unknown permission flag '" << c
<< "'.";
1800 mds_cap_string
+= mds_cap_string
.empty() ? "" : ", ";
1801 mds_cap_string
+= "allow " + cap
;
1803 if (filesystem
!= "*" && filesystem
!= "all" && fs
!= nullptr) {
1804 mds_cap_string
+= " fsname=" + std::string(fs
->mds_map
.get_fs_name());
1808 mds_cap_string
+= " path=" + path
;
1812 mds_cap_string
+= " root_squash";
1816 osd_cap_string
+= osd_cap_string
.empty() ? "" : ", ";
1817 osd_cap_string
+= "allow " + osd_cap_wanted
1818 + " tag " + pg_pool_t::APPLICATION_NAME_CEPHFS
1819 + " data=" + filesystem
;
1821 std::map
<string
, bufferlist
> wanted_caps
= {
1822 { "mon", _encode_cap(mon_cap_string
) },
1823 { "osd", _encode_cap(osd_cap_string
) },
1824 { "mds", _encode_cap(mds_cap_string
) }
1827 if (!valid_caps("mon", mon_cap_string
, &ss
) ||
1828 !valid_caps("osd", osd_cap_string
, &ss
) ||
1829 !valid_caps("mds", mds_cap_string
, &ss
)) {
1834 EntityAuth entity_auth
;
1835 if (mon
.key_server
.get_auth(entity
, entity_auth
)) {
1836 for (const auto &sys_cap
: wanted_caps
) {
1837 if (entity_auth
.caps
.count(sys_cap
.first
) == 0 ||
1838 !entity_auth
.caps
[sys_cap
.first
].contents_equal(sys_cap
.second
)) {
1839 ss
<< entity
<< " already has fs capabilities that differ from "
1840 << "those supplied. To generate a new auth key for " << entity
1841 << ", first remove " << entity
<< " from configuration files, "
1842 << "execute 'ceph auth rm " << entity
<< "', then execute this "
1843 << "command again.";
1850 kr
.add(entity
, entity_auth
.key
);
1852 kr
.set_caps(entity
, entity_auth
.caps
);
1853 kr
.encode_formatted("auth", f
.get(), rdata
);
1855 kr
.encode_plaintext(rdata
);
1861 KeyServerData::Incremental auth_inc
;
1862 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1863 auth_inc
.name
= entity
;
1864 auth_inc
.auth
.key
.create(g_ceph_context
, CEPH_CRYPTO_AES
);
1865 auth_inc
.auth
.caps
= wanted_caps
;
1867 push_cephx_inc(auth_inc
);
1869 kr
.add(entity
, auth_inc
.auth
.key
);
1871 kr
.set_caps(entity
, wanted_caps
);
1872 kr
.encode_formatted("auth", f
.get(), rdata
);
1874 kr
.encode_plaintext(rdata
);
1879 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
, rdata
,
1880 get_last_committed() + 1));
1882 } else if (prefix
== "auth caps" && !entity_name
.empty()) {
1883 KeyServerData::Incremental auth_inc
;
1884 auth_inc
.name
= entity
;
1885 if (!mon
.key_server
.get_auth(auth_inc
.name
, auth_inc
.auth
)) {
1886 ss
<< "couldn't find entry " << auth_inc
.name
;
1891 if (!valid_caps(caps_vec
, &ss
)) {
1896 map
<string
,bufferlist
> newcaps
;
1897 for (vector
<string
>::iterator it
= caps_vec
.begin();
1898 it
!= caps_vec
.end(); it
+= 2)
1899 encode(*(it
+1), newcaps
[*it
]);
1901 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
1902 auth_inc
.auth
.caps
= newcaps
;
1903 push_cephx_inc(auth_inc
);
1905 ss
<< "updated caps for " << auth_inc
.name
;
1907 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
1908 get_last_committed() + 1));
1910 } else if ((prefix
== "auth del" || prefix
== "auth rm") &&
1911 !entity_name
.empty()) {
1912 KeyServerData::Incremental auth_inc
;
1913 auth_inc
.name
= entity
;
1914 if (!mon
.key_server
.contains(auth_inc
.name
)) {
1918 auth_inc
.op
= KeyServerData::AUTH_INC_DEL
;
1919 push_cephx_inc(auth_inc
);
1921 wait_for_finished_proposal(op
, new Monitor::C_Command(mon
, op
, 0, rs
,
1922 get_last_committed() + 1));
1927 getline(ss
, rs
, '\0');
1928 mon
.reply_command(op
, err
, rs
, rdata
, get_last_committed());
1932 bool AuthMonitor::prepare_global_id(MonOpRequestRef op
)
1934 dout(10) << "AuthMonitor::prepare_global_id" << dendl
;
1935 increase_max_global_id();
1939 bool AuthMonitor::prepare_used_pending_keys(MonOpRequestRef op
)
1941 dout(10) << __func__
<< " " << op
<< dendl
;
1942 auto m
= op
->get_req
<MMonUsedPendingKeys
>();
1943 process_used_pending_keys(m
->used_pending_keys
);
1947 bool AuthMonitor::_upgrade_format_to_dumpling()
1949 dout(1) << __func__
<< " upgrading from format 0 to 1" << dendl
;
1950 ceph_assert(format_version
== 0);
1952 bool changed
= false;
1953 map
<EntityName
, EntityAuth
>::iterator p
;
1954 for (p
= mon
.key_server
.secrets_begin();
1955 p
!= mon
.key_server
.secrets_end();
1957 // grab mon caps, if any
1959 if (p
->second
.caps
.count("mon") == 0)
1962 auto it
= p
->second
.caps
["mon"].cbegin();
1963 decode(mon_caps
, it
);
1965 catch (const ceph::buffer::error
&) {
1966 dout(10) << __func__
<< " unable to parse mon cap for "
1967 << p
->first
<< dendl
;
1971 string n
= p
->first
.to_str();
1974 // set daemon profiles
1975 if ((p
->first
.is_osd() || p
->first
.is_mds()) &&
1976 mon_caps
== "allow rwx") {
1977 new_caps
= string("allow profile ") + std::string(p
->first
.get_type_name());
1980 // update bootstrap keys
1981 if (n
== "client.bootstrap-osd") {
1982 new_caps
= "allow profile bootstrap-osd";
1984 if (n
== "client.bootstrap-mds") {
1985 new_caps
= "allow profile bootstrap-mds";
1988 if (new_caps
.length() > 0) {
1989 dout(5) << __func__
<< " updating " << p
->first
<< " mon cap from "
1990 << mon_caps
<< " to " << new_caps
<< dendl
;
1993 encode(new_caps
, bl
);
1995 KeyServerData::Incremental auth_inc
;
1996 auth_inc
.name
= p
->first
;
1997 auth_inc
.auth
= p
->second
;
1998 auth_inc
.auth
.caps
["mon"] = bl
;
1999 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
2000 push_cephx_inc(auth_inc
);
2007 bool AuthMonitor::_upgrade_format_to_luminous()
2009 dout(1) << __func__
<< " upgrading from format 1 to 2" << dendl
;
2010 ceph_assert(format_version
== 1);
2012 bool changed
= false;
2013 map
<EntityName
, EntityAuth
>::iterator p
;
2014 for (p
= mon
.key_server
.secrets_begin();
2015 p
!= mon
.key_server
.secrets_end();
2017 string n
= p
->first
.to_str();
2020 if (n
== "client.admin") {
2021 // admin gets it all
2023 } else if (n
.find("osd.") == 0 ||
2024 n
.find("mds.") == 0 ||
2025 n
.find("mon.") == 0) {
2026 // daemons follow their profile
2027 string type
= n
.substr(0, 3);
2028 newcap
= "allow profile " + type
;
2029 } else if (p
->second
.caps
.count("mon")) {
2030 // if there are any mon caps, give them 'r' mgr caps
2034 if (newcap
.length() > 0) {
2035 dout(5) << " giving " << n
<< " mgr '" << newcap
<< "'" << dendl
;
2039 EntityAuth auth
= p
->second
;
2040 auth
.caps
["mgr"] = bl
;
2042 add_entity(p
->first
, auth
);
2046 if (n
.find("mgr.") == 0 &&
2047 p
->second
.caps
.count("mon")) {
2048 // the kraken ceph-mgr@.service set the mon cap to 'allow *'.
2049 auto blp
= p
->second
.caps
["mon"].cbegin();
2051 decode(oldcaps
, blp
);
2052 if (oldcaps
== "allow *") {
2053 dout(5) << " fixing " << n
<< " mon cap to 'allow profile mgr'"
2056 encode("allow profile mgr", bl
);
2058 EntityAuth auth
= p
->second
;
2059 auth
.caps
["mon"] = bl
;
2060 add_entity(p
->first
, p
->second
);
2066 // add bootstrap key if it does not already exist
2067 // (might have already been get-or-create'd by
2068 // ceph-create-keys)
2069 EntityName bootstrap_mgr_name
;
2070 int r
= bootstrap_mgr_name
.from_str("client.bootstrap-mgr");
2072 if (!mon
.key_server
.contains(bootstrap_mgr_name
)) {
2074 EntityName name
= bootstrap_mgr_name
;
2076 encode("allow profile bootstrap-mgr", auth
.caps
["mon"]);
2077 auth
.key
.create(g_ceph_context
, CEPH_CRYPTO_AES
);
2078 add_entity(name
, auth
);
2084 bool AuthMonitor::_upgrade_format_to_mimic()
2086 dout(1) << __func__
<< " upgrading from format 2 to 3" << dendl
;
2087 ceph_assert(format_version
== 2);
2089 list
<pair
<EntityName
,EntityAuth
> > auth_lst
;
2090 _generate_bootstrap_keys(&auth_lst
);
2092 bool changed
= false;
2093 for (auto &p
: auth_lst
) {
2094 if (mon
.key_server
.contains(p
.first
)) {
2097 int err
= add_entity(p
.first
, p
.second
);
2098 ceph_assert(err
== 0);
2105 void AuthMonitor::upgrade_format()
2107 constexpr unsigned int FORMAT_NONE
= 0;
2108 constexpr unsigned int FORMAT_DUMPLING
= 1;
2109 constexpr unsigned int FORMAT_LUMINOUS
= 2;
2110 constexpr unsigned int FORMAT_MIMIC
= 3;
2112 // when upgrading from the current format to a new format, ensure that
2113 // the new format doesn't break the older format. I.e., if a given format N
2114 // changes or adds something, ensure that when upgrading from N-1 to N+1, we
2115 // still observe the changes for format N if those have not been superseded
2118 unsigned int current
= FORMAT_MIMIC
;
2119 if (!mon
.get_quorum_mon_features().contains_all(
2120 ceph::features::mon::FEATURE_LUMINOUS
)) {
2121 // pre-luminous quorum
2122 current
= FORMAT_DUMPLING
;
2123 } else if (!mon
.get_quorum_mon_features().contains_all(
2124 ceph::features::mon::FEATURE_MIMIC
)) {
2126 current
= FORMAT_LUMINOUS
;
2128 if (format_version
>= current
) {
2129 dout(20) << __func__
<< " format " << format_version
2130 << " is current" << dendl
;
2134 // perform a rolling upgrade of the new format, if necessary.
2135 // i.e., if we are moving from format NONE to MIMIC, we will first upgrade
2136 // to DUMPLING, then to LUMINOUS, and finally to MIMIC, in several different
2139 bool changed
= false;
2140 if (format_version
== FORMAT_NONE
) {
2141 changed
= _upgrade_format_to_dumpling();
2143 } else if (format_version
== FORMAT_DUMPLING
) {
2144 changed
= _upgrade_format_to_luminous();
2145 } else if (format_version
== FORMAT_LUMINOUS
) {
2146 changed
= _upgrade_format_to_mimic();
2151 dout(10) << __func__
<< " proposing update from format " << format_version
2152 << " -> " << current
<< dendl
;
2153 format_version
= current
;
2158 void AuthMonitor::dump_info(Formatter
*f
)
2160 /*** WARNING: do not include any privileged information here! ***/
2161 f
->open_object_section("auth");
2162 f
->dump_unsigned("first_committed", get_first_committed());
2163 f
->dump_unsigned("last_committed", get_last_committed());
2164 f
->dump_unsigned("num_secrets", mon
.key_server
.get_num_secrets());