From 7ef48d521adf04487d4d7d5380e8a4c4ed5a3a25 Mon Sep 17 00:00:00 2001 From: Radoslaw Zarzynski Date: Mon, 11 Apr 2016 20:43:19 +0200 Subject: [PATCH] rgw: implement RGWKeystoneAuthEngine. Signed-off-by: Radoslaw Zarzynski --- src/common/config_opts.h | 1 + src/rgw/rgw_auth.cc | 197 ++++++++++++++++++++++++++++++++++++++- src/rgw/rgw_keystone.h | 7 ++ src/rgw/rgw_swift.cc | 23 ----- 4 files changed, 204 insertions(+), 24 deletions(-) diff --git a/src/common/config_opts.h b/src/common/config_opts.h index ae0e20772a5ed..8fbaa6b320689 100644 --- a/src/common/config_opts.h +++ b/src/common/config_opts.h @@ -1286,6 +1286,7 @@ OPTION(rgw_keystone_admin_project, OPT_STR, "") // keystone admin user project OPTION(rgw_keystone_admin_domain, OPT_STR, "") // keystone admin user domain OPTION(rgw_keystone_api_version, OPT_INT, 2) // Version of Keystone API to use (2 or 3) OPTION(rgw_keystone_accepted_roles, OPT_STR, "Member, admin") // roles required to serve requests +OPTION(rgw_keystone_accepted_admin_roles, OPT_STR, "") // list of roles allowing an user to gain admin privileges OPTION(rgw_keystone_token_cache_size, OPT_INT, 10000) // max number of entries in keystone token cache OPTION(rgw_keystone_revocation_interval, OPT_INT, 15 * 60) // seconds between tokens revocation check OPTION(rgw_keystone_verify_ssl, OPT_BOOL, true) // should we try to verify keystone's ssl diff --git a/src/rgw/rgw_auth.cc b/src/rgw/rgw_auth.cc index 7a2e13f7b0e55..793e998186fbe 100644 --- a/src/rgw/rgw_auth.cc +++ b/src/rgw/rgw_auth.cc @@ -6,7 +6,8 @@ #include "rgw_user.h" #include "rgw_http_client.h" #include "rgw_keystone.h" -#include "rgw_swift.h" + +#include "include/str_list.h" #define dout_subsys ceph_subsys_rgw @@ -239,3 +240,197 @@ RGWAuthApplier::aplptr_t RGWAnonymousAuthEngine::authenticate() const return apl_factory->create_apl_local(cct, user_info, RGWLocalAuthApplier::NO_SUBUSER); } + + +/* Keystone */ +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" }) { + } + + std::string get_subject_token() const { + try { + return get_header_value("X-Subject-Token"); + } catch (std::out_of_range&) { + return header_value_t(); + } + } +}; + +typedef RGWKeystoneHTTPTransceiver RGWValidateKeystoneToken; +typedef RGWKeystoneHTTPTransceiver RGWGetKeystoneAdminToken; +typedef RGWKeystoneHTTPTransceiver RGWGetRevokedTokens; + + +bool RGWKeystoneAuthEngine::is_applicable() const noexcept +{ + if (false == RGWTokenBasedAuthEngine::is_applicable()) { + return false; + } + + return false == cct->_conf->rgw_keystone_url.empty(); +} + +KeystoneToken RGWKeystoneAuthEngine::decode_pki_token(const std::string token) const +{ + bufferlist token_body_bl; + int ret = rgw_decode_b64_cms(cct, token, token_body_bl); + if (ret < 0) { + ldout(cct, 20) << "cannot decode pki token" << dendl; + throw ret; + } else { + ldout(cct, 20) << "successfully decoded pki token" << dendl; + } + + KeystoneToken token_body; + ret = token_body.parse(cct, token, token_body_bl); + if (ret < 0) { + throw ret; + } + + return token_body; +} + +KeystoneToken RGWKeystoneAuthEngine::get_from_keystone(const std::string token) const +{ + bufferlist token_body_bl; + RGWValidateKeystoneToken validate(cct, &token_body_bl); + + std::string url; + if (KeystoneService::get_keystone_url(cct, url) < 0) { + throw -EINVAL; + } + + const auto keystone_version = KeystoneService::get_api_version(); + if (keystone_version == KeystoneApiVersion::VER_2) { + url.append("v2.0/tokens/" + token); + } else if (keystone_version == KeystoneApiVersion::VER_3) { + url.append("v3/auth/tokens"); + validate.append_header("X-Subject-Token", token); + } + + std::string admin_token; + if (KeystoneService::get_keystone_admin_token(cct, admin_token) < 0) { + throw -EINVAL; + } + + validate.append_header("X-Auth-Token", admin_token); + validate.set_send_length(0); + + int ret = validate.process(url.c_str()); + if (ret < 0) { + throw ret; + } + token_body_bl.append((char)0); // NULL terminate for debug output + + ldout(cct, 20) << "received response: " << token_body_bl.c_str() << dendl; + + KeystoneToken token_body; + ret = token_body.parse(cct, token, token_body_bl); + if (ret < 0) { + throw ret; + } + + return token_body; +} + +RGWRemoteAuthApplier::AuthInfo +RGWKeystoneAuthEngine::get_creds_info(const KeystoneToken& token, + const std::vector& admin_roles + ) const noexcept +{ + /* Check whether the user has an admin status. */ + bool is_admin = false; + for (const auto admin_role : admin_roles) { + if (token.has_role(admin_role)) { + is_admin = true; + break; + } + } + + return { + /* Suggested account name for the authenticated user. */ + rgw_user(token.get_project_id()), + /* User's display name (aka real name). */ + token.get_project_name(), + /* Keystone doesn't support RGW's subuser concept, so we cannot cut down + * the access rights through the perm_mask. At least at this layer. */ + RGW_PERM_FULL_CONTROL, + is_admin, + }; +} + +RGWAuthApplier::aplptr_t RGWKeystoneAuthEngine::authenticate() const +{ + KeystoneToken t; + + /* This will be initialized on the first call to this method. In C++11 it's + * also thread-safe. */ + static struct RolesCacher { + RolesCacher(CephContext * const cct) { + get_str_vec(cct->_conf->rgw_keystone_accepted_roles, plain); + get_str_vec(cct->_conf->rgw_keystone_accepted_admin_roles, admin); + + /* Let's suppose that having an admin role implies also a regular one. */ + plain.insert(std::end(plain), std::begin(admin), std::end(admin)); + } + + std::vector plain; + std::vector admin; + } roles(cct); + + /* Token ID is a concept that makes dealing with PKI tokens more effective. + * Instead of storing several kilobytes, a short hash can be burried. */ + const auto token_id = rgw_get_token_id(token); + ldout(cct, 20) << "token_id=" << token_id << dendl; + + /* Check cache first. */ + if (RGWKeystoneTokenCache::get_instance().find(token_id, t)) { + ldout(cct, 20) << "cached token.project.id=" << t.get_project_id() + << dendl; + return apl_factory->create_apl_remote(cct, + get_creds_info(t, roles.admin)); + } + + /* Retrieve token. */ + if (rgw_is_pki_token(token)) { + try { + t = decode_pki_token(token); + } catch (...) { + /* Last resort. */ + t = get_from_keystone(token); + } + } else { + /* Can't decode, just go to the Keystone server for validation. */ + t = get_from_keystone(token); + } + + /* Verify expiration. */ + if (t.expired()) { + ldout(cct, 0) << "got expired token: " << t.get_project_name() + << ":" << t.get_user_name() + << " expired: " << t.get_expires() << dendl; + return nullptr; + } + + /* Check for necessary roles. */ + for (const auto role : roles.plain) { + if (t.has_role(role) == true) { + ldout(cct, 0) << "validated token: " << t.get_project_name() + << ":" << t.get_user_name() + << " expires: " << t.get_expires() << dendl; + RGWKeystoneTokenCache::get_instance().add(token_id, t); + return apl_factory->create_apl_remote(cct, + get_creds_info(t, roles.admin)); + } + } + + ldout(cct, 0) << "user does not hold a matching role; required roles: " + << g_conf->rgw_keystone_accepted_roles << dendl; + + return nullptr; +} diff --git a/src/rgw/rgw_keystone.h b/src/rgw/rgw_keystone.h index 63501ffa6d959..2449dae7a265b 100644 --- a/src/rgw/rgw_keystone.h +++ b/src/rgw/rgw_keystone.h @@ -16,6 +16,13 @@ int rgw_decode_b64_cms(CephContext *cct, 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); diff --git a/src/rgw/rgw_swift.cc b/src/rgw/rgw_swift.cc index 7525d32c4271b..4a835bf452ed7 100644 --- a/src/rgw/rgw_swift.cc +++ b/src/rgw/rgw_swift.cc @@ -106,29 +106,6 @@ int RGWSwift::validate_token(const char *token, struct rgw_swift_auth_info *info } -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" }) { - } - - std::string get_subject_token() const { - try { - return get_header_value("X-Subject-Token"); - } catch (std::out_of_range&) { - return header_value_t(); - } - } -}; - -typedef RGWKeystoneHTTPTransceiver RGWValidateKeystoneToken; -typedef RGWKeystoneHTTPTransceiver RGWGetKeystoneAdminToken; -typedef RGWKeystoneHTTPTransceiver RGWGetRevokedTokens; - - int RGWSwift::check_revoked() { string url; -- 2.39.5