// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #ifndef CEPH_RGW_KEYSTONE_H #define CEPH_RGW_KEYSTONE_H #include #include #include #include "rgw_common.h" #include "rgw_http_client.h" #include "common/Cond.h" #include "global/global_init.h" #include int rgw_open_cms_envelope(CephContext *cct, const std::string& src, std::string& dst); /* out */ int rgw_decode_b64_cms(CephContext *cct, const string& signed_b64, bufferlist& bl); bool rgw_is_pki_token(const string& token); void rgw_get_token_id(const string& token, string& token_id); static inline std::string rgw_get_token_id(const string& token) { std::string token_id; rgw_get_token_id(token, token_id); return token_id; } bool rgw_decode_pki_token(CephContext *cct, const string& token, bufferlist& bl); namespace rgw { namespace keystone { enum class ApiVersion { VER_2, VER_3 }; class Config { protected: Config() = default; virtual ~Config() = default; public: virtual std::string get_endpoint_url() const noexcept = 0; virtual ApiVersion get_api_version() const noexcept = 0; virtual boost::string_ref get_admin_token() const noexcept = 0; virtual boost::string_ref get_admin_user() const noexcept = 0; virtual boost::string_ref get_admin_password() const noexcept = 0; virtual boost::string_ref get_admin_tenant() const noexcept = 0; virtual boost::string_ref get_admin_project() const noexcept = 0; virtual boost::string_ref get_admin_domain() const noexcept = 0; }; class CephCtxConfig : public Config { protected: CephCtxConfig() = default; virtual ~CephCtxConfig() = default; public: static CephCtxConfig& get_instance() { static CephCtxConfig instance; return instance; } std::string get_endpoint_url() const noexcept override; ApiVersion get_api_version() const noexcept override; boost::string_ref get_admin_token() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_token; } boost::string_ref get_admin_user() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_user; } boost::string_ref get_admin_password() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_password; } boost::string_ref get_admin_tenant() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_tenant; } boost::string_ref get_admin_project() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_project; } boost::string_ref get_admin_domain() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_domain; } }; class TokenEnvelope; class TokenCache; class Service { public: class RGWKeystoneHTTPTransceiver : public RGWHTTPTransceiver { public: RGWKeystoneHTTPTransceiver(CephContext * const cct, bufferlist * const token_body_bl) : RGWHTTPTransceiver(cct, token_body_bl, cct->_conf->rgw_keystone_verify_ssl, { "X-Subject-Token" }) { } const header_value_t& get_subject_token() const { try { return get_header_value("X-Subject-Token"); } catch (std::out_of_range&) { static header_value_t empty_val; return empty_val; } } }; typedef RGWKeystoneHTTPTransceiver RGWValidateKeystoneToken; typedef RGWKeystoneHTTPTransceiver RGWGetKeystoneAdminToken; typedef RGWKeystoneHTTPTransceiver RGWGetRevokedTokens; static int get_admin_token(CephContext* const cct, TokenCache& token_cache, const Config& config, std::string& token); static int issue_admin_token_request(CephContext* const cct, const Config& config, TokenEnvelope& token); static int get_keystone_barbican_token(CephContext * const cct, std::string& token); }; class TokenEnvelope { public: class Domain { public: string id; string name; void decode_json(JSONObj *obj); }; class Project { public: Domain domain; string id; string name; void decode_json(JSONObj *obj); }; class Token { public: Token() : expires(0) { } string id; time_t expires; Project tenant_v2; void decode_json(JSONObj *obj); }; class Role { public: string id; string name; void decode_json(JSONObj *obj); }; class User { public: string id; string name; Domain domain; list roles_v2; void decode_json(JSONObj *obj); }; Token token; Project project; User user; list roles; void decode_v3(JSONObj* obj); void decode_v2(JSONObj* obj); public: /* We really need the default ctor because of the internals of TokenCache. */ TokenEnvelope() = default; time_t get_expires() const { return token.expires; } const std::string& get_domain_id() const {return project.domain.id;}; const std::string& get_domain_name() const {return project.domain.name;}; const std::string& get_project_id() const {return project.id;}; const std::string& get_project_name() const {return project.name;}; const std::string& get_user_id() const {return user.id;}; const std::string& get_user_name() const {return user.name;}; bool has_role(const string& r) const; bool expired() const { const uint64_t now = ceph_clock_now().sec(); return now >= static_cast(get_expires()); } int parse(CephContext* cct, const std::string& token_str, ceph::buffer::list& bl /* in */, ApiVersion version); }; class TokenCache { struct token_entry { TokenEnvelope token; list::iterator lru_iter; }; std::atomic down_flag = { false }; class RevokeThread : public Thread { friend class TokenCache; typedef RGWPostHTTPData RGWGetRevokedTokens; CephContext* const cct; TokenCache* const cache; const rgw::keystone::Config& config; Mutex lock; Cond cond; RevokeThread(CephContext* const cct, TokenCache* const cache, const rgw::keystone::Config& config) : cct(cct), cache(cache), config(config), lock("rgw::keystone::TokenCache::RevokeThread") { } void *entry() override; void stop(); int check_revoked(); } revocator; const boost::intrusive_ptr cct; std::string admin_token_id; std::string barbican_token_id; std::map tokens; std::list tokens_lru; Mutex lock; const size_t max; TokenCache(const rgw::keystone::Config& config) : revocator(g_ceph_context, this, config), cct(g_ceph_context), lock("rgw::keystone::TokenCache"), max(cct->_conf->rgw_keystone_token_cache_size) { /* revocation logic needs to be smarter, but meanwhile, * make it optional. * see http://tracker.ceph.com/issues/9493 * http://tracker.ceph.com/issues/19499 */ if (cct->_conf->rgw_keystone_revocation_interval > 0 && cct->_conf->rgw_keystone_token_cache_size ) { /* The thread name has been kept for backward compliance. */ revocator.create("rgw_swift_k_rev"); } } ~TokenCache() { down_flag = true; // Only stop and join if revocator thread is started. if (revocator.is_started()) { revocator.stop(); revocator.join(); } } public: TokenCache(const TokenCache&) = delete; void operator=(const TokenCache&) = delete; template static TokenCache& get_instance() { static_assert(std::is_base_of::value, "ConfigT must be a subclass of rgw::keystone::Config"); /* In C++11 this is thread safe. */ static TokenCache instance(ConfigT::get_instance()); return instance; } bool find(const std::string& token_id, TokenEnvelope& token); boost::optional find(const std::string& token_id) { TokenEnvelope token_envlp; if (find(token_id, token_envlp)) { return token_envlp; } return boost::none; } bool find_admin(TokenEnvelope& token); bool find_barbican(TokenEnvelope& token); void add(const std::string& token_id, const TokenEnvelope& token); void add_admin(const TokenEnvelope& token); void add_barbican(const TokenEnvelope& token); void invalidate(const std::string& token_id); bool going_down() const; private: void add_locked(const std::string& token_id, const TokenEnvelope& token); bool find_locked(const std::string& token_id, TokenEnvelope& token); }; class AdminTokenRequest { public: virtual ~AdminTokenRequest() = default; virtual void dump(Formatter* f) const = 0; }; class AdminTokenRequestVer2 : public AdminTokenRequest { const Config& conf; public: AdminTokenRequestVer2(const Config& conf) : conf(conf) { } void dump(Formatter *f) const override; }; class AdminTokenRequestVer3 : public AdminTokenRequest { const Config& conf; public: AdminTokenRequestVer3(const Config& conf) : conf(conf) { } void dump(Formatter *f) const override; }; class BarbicanTokenRequestVer2 : public AdminTokenRequest { CephContext *cct; public: BarbicanTokenRequestVer2(CephContext * const _cct) : cct(_cct) { } void dump(Formatter *f) const; }; class BarbicanTokenRequestVer3 : public AdminTokenRequest { CephContext *cct; public: BarbicanTokenRequestVer3(CephContext * const _cct) : cct(_cct) { } void dump(Formatter *f) const; }; }; /* namespace keystone */ }; /* namespace rgw */ #endif