Skip to content

Commit

Permalink
rgw/auth: Add service token logic for Keystone
Browse files Browse the repository at this point in the history
Signed-off-by: Tobias Urdin <tobias.urdin@binero.com>
  • Loading branch information
tobias-urdin committed Sep 2, 2022
1 parent 8f7e84b commit 1396092
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 9 deletions.
81 changes: 74 additions & 7 deletions src/rgw/rgw_auth_keystone.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ TokenEngine::is_applicable(const std::string& token) const noexcept
}

boost::optional<TokenEngine::token_envelope_t>
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. */
Expand All @@ -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);
}

Expand Down Expand Up @@ -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<TokenEngine::token_envelope_t> t;

/* This will be initialized on the first call to this method. In C++11 it's
Expand Down Expand Up @@ -228,21 +235,81 @@ 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<TokenEngine::token_envelope_t> 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);
}

/* 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. */
Expand Down
5 changes: 3 additions & 2 deletions src/rgw/rgw_auth_keystone.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ class TokenEngine : public rgw::auth::Engine {
bool is_applicable(const std::string& token) const noexcept;

boost::optional<token_envelope_t>
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,
const std::vector<std::string>& admin_roles
) const noexcept;
result_t authenticate(const DoutPrefixProvider* dpp,
const std::string& token,
const std::string& service_token,
const req_state* s) const;

public:
Expand All @@ -68,7 +69,7 @@ class TokenEngine : public rgw::auth::Engine {

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 */

Expand Down

0 comments on commit 1396092

Please sign in to comment.