From: Tobias Urdin Date: Tue, 15 Mar 2022 10:51:34 +0000 (+0000) Subject: rgw/auth: Add service token logic for Keystone X-Git-Tag: v18.1.0~1023^2~20 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=954dffe888a716ff218358600a835951d8d8c231;p=ceph.git rgw/auth: Add service token logic for Keystone Signed-off-by: Tobias Urdin --- diff --git a/src/rgw/rgw_auth_keystone.cc b/src/rgw/rgw_auth_keystone.cc index 10d8d8273886..0b3dcbe03463 100644 --- a/src/rgw/rgw_auth_keystone.cc +++ b/src/rgw/rgw_auth_keystone.cc @@ -38,7 +38,7 @@ TokenEngine::is_applicable(const std::string& token) const noexcept } boost::optional -TokenEngine::get_from_keystone(const DoutPrefixProvider* dpp, const std::string& token) const +TokenEngine::get_from_keystone(const DoutPrefixProvider* dpp, const std::string& token, const bool& allow_expired) const { /* Unfortunately, we can't use the short form of "using" here. It's because * we're aliasing a class' member, not namespace. */ @@ -60,6 +60,11 @@ TokenEngine::get_from_keystone(const DoutPrefixProvider* dpp, const std::string& url.append("v2.0/tokens/" + token); } else if (keystone_version == rgw::keystone::ApiVersion::VER_3) { url.append("v3/auth/tokens"); + + if (allow_expired) { + url.append("?allow_expired=1"); + } + validate.append_header("X-Subject-Token", token); } @@ -188,8 +193,10 @@ TokenEngine::get_acl_strategy(const TokenEngine::token_envelope_t& token) const TokenEngine::result_t TokenEngine::authenticate(const DoutPrefixProvider* dpp, const std::string& token, + const std::string& service_token, const req_state* const s) const { + bool allow_expired = false; boost::optional t; /* This will be initialized on the first call to this method. In C++11 it's @@ -228,10 +235,63 @@ TokenEngine::authenticate(const DoutPrefixProvider* dpp, return result_t::grant(std::move(apl)); } - /* Not in cache. Go to the Keystone for validation. This happens even + /* TODO(tobias-urdin): Add config option for enabling service token support */ + + /* We have a service token and a token so we verify the service + * token and if it's invalid the request is invalid. If it's valid + * we allow an expired token to be used when doing lookup in Keystone. + * We never get to this if the token is in the cache. */ + if (! service_token.empty()) { + boost::optional st; + + const auto& service_token_id = rgw_get_token_id(service_token); + ldpp_dout(dpp, 20) << "service_token_id=" << service_token_id << dendl; + + /* Check cache for service token first. */ + st = token_cache.find(service_token_id); + if (st) { + ldpp_dout(dpp, 20) << "cached service_token.project.id=" << st->get_project_id() + << dendl; + + /* We found the service token in the cache so we allow using an expired + * token for this request. */ + allow_expired = true; + ldpp_dout(dpp, 20) << "allowing expired tokens because service_token_id=" + << service_token_id + << " was found in cache" << dendl; + } else { + /* Service token was not found in cache. Go to Keystone for validating + * the token. The allow_expired here must always be false. */ + st = get_from_keystone(dpp, service_token, allow_expired); + + if (! st) { + return result_t::deny(-EACCES); + } + + /* Verify expiration of service token. */ + if (st->expired()) { + ldpp_dout(dpp, 0) << "got expired service token: " << st->get_project_name() + << ":" << st->get_user_name() + << " expired " << st->get_expires() << dendl; + return result_t::deny(-EPERM); + } + + /* TODO(tobias-urdin): Verify role on service user with corresponding config option */ + + /* Service token is valid so we allow using an expired token for + * this request. */ + ldpp_dout(dpp, 20) << "allowing expired tokens because service_token_id=" + << service_token_id + << " is valid" << dendl; + allow_expired = true; + token_cache.add(service_token_id, *st); + } + } + + /* Token not in cache. Go to the Keystone for validation. This happens even * for the legacy PKI/PKIz token types. That's it, after the PKI/PKIz * RadosGW-side validation has been removed, we always ask Keystone. */ - t = get_from_keystone(dpp, token); + t = get_from_keystone(dpp, token, allow_expired); if (! t) { return result_t::deny(-EACCES); @@ -239,10 +299,17 @@ TokenEngine::authenticate(const DoutPrefixProvider* dpp, /* Verify expiration. */ if (t->expired()) { - ldpp_dout(dpp, 0) << "got expired token: " << t->get_project_name() - << ":" << t->get_user_name() - << " expired: " << t->get_expires() << dendl; - return result_t::deny(-EPERM); + if (allow_expired) { + ldpp_dout(dpp, 20) << "allowing expired token: " << t->get_project_name() + << ":" << t->get_user_name() + << " expired: " << t->get_expires() + << " because of valid service token" << dendl; + } else { + ldpp_dout(dpp, 0) << "got expired token: " << t->get_project_name() + << ":" << t->get_user_name() + << " expired: " << t->get_expires() << dendl; + return result_t::deny(-EPERM); + } } /* Check for necessary roles. */ diff --git a/src/rgw/rgw_auth_keystone.h b/src/rgw/rgw_auth_keystone.h index b986fc2a6aef..6d463bb8617d 100644 --- a/src/rgw/rgw_auth_keystone.h +++ b/src/rgw/rgw_auth_keystone.h @@ -39,7 +39,7 @@ class TokenEngine : public rgw::auth::Engine { bool is_applicable(const std::string& token) const noexcept; boost::optional - get_from_keystone(const DoutPrefixProvider* dpp, const std::string& token) const; + get_from_keystone(const DoutPrefixProvider* dpp, const std::string& token, const bool& allow_expired) const; acl_strategy_t get_acl_strategy(const token_envelope_t& token) const; auth_info_t get_creds_info(const token_envelope_t& token, @@ -47,6 +47,7 @@ class TokenEngine : public rgw::auth::Engine { ) const noexcept; result_t authenticate(const DoutPrefixProvider* dpp, const std::string& token, + const std::string& service_token, const req_state* s) const; public: @@ -68,7 +69,7 @@ public: result_t authenticate(const DoutPrefixProvider* dpp, const req_state* const s, optional_yield y) const override { - return authenticate(dpp, extractor->get_token(s), s); + return authenticate(dpp, extractor->get_token(s), extractor->get_service_token(s), s); } }; /* class TokenEngine */