[openstack-dev] [all] [clients] [keystone] lack of retrying tokens leads to overall OpenStack fragility

Nathan Kinder nkinder at redhat.com
Tue Sep 16 00:28:28 UTC 2014



On 09/12/2014 12:46 AM, Angus Lees wrote:
> On Thu, 11 Sep 2014 03:21:52 PM Steven Hardy wrote:
>> On Wed, Sep 10, 2014 at 08:46:45PM -0400, Jamie Lennox wrote:
>>> For service to service communication there are two types.
>>> 1) using the user's token like nova->cinder. If this token expires there
>>> is really nothing that nova can do except raise 401 and make the client
>>> do it again. 2) using a service user like nova->neutron. This should
>>> allow automatic reauthentication and will be fixed/standardied by
>>> sessions.
>> (1) is the problem I'm trying to solve in bug #1306294, and (for Heat at
>> least) there seems to be two solutions, neither of which I particularly
>> like:
>>
>> - Require username/password to be passed into the service (something we've
>>   been trying to banish via migrating to trusts for deferred
>>   authentication)
>> - Create a trust, and impersonate the user for the duration of the request,
>>   or after the token expires until it is completed, using the service user
>>   credentials and the trust_id.
>>
>> It's the second one which I'm deliberating over - technically it will work,
>> and we create the trust anyway (e.g for later use to do autoscaling etc),
>> but can anyone from the keystone team comment on the legitimacy of the
>> approach?
>>
>> Intuitively it seems wrong, but I can't see any other way if we want to
>> support token-only auth and cope with folks doing stuff which takes 2 hours
>> with a 1 hour token expiry?
> 
> A possible 3rd option is some sort of longer lived, but limited scope 
> "capability token".
> 
> The user would create a capability token that represents "anyone possessing 
> this token is (eg) allowed to write to swift as $user".  The token could be 
> created by keystone as a trusted 3rd party or by swift (doesn't matter which), 
> in response to a request authenticated as $user.  The client then includes 
> that token in the request *to cinder*, so cinder can pass it back to swift 
> when doing the writes.
> This capability token would be of much longer duration (long enough to 
> complete the cinder->swift task), which is ok because it is of a much more 
> limited scope (ideally as fine grained as we can bother implementing).

With UUID tokens, it would even be possible to implement a "one-time
use" sort of token.  Since Keystone needs to be asked to validate a UUID
token, the token could be invalidated by Keystone after the first
verification.  Since the token is limited based off of number of times
of usage, there should be less concerns about a long validity period
(though it would make sense to use something sane still).  This approach
wouldn't be possible with PKI tokens since Keystone is not in the
validation path.

Your idea of passing the "capability token" in the request would work
well with this, as the token only needs to be extracted and used once
instead of being passed from service to service and validated at each
hop (user>cinder->swift in your example).

The idea would be to leave normal tokens with a smaller validity period
(like the current default of an hour), but also allow one-time use
tokens to be requested.

> 
> (I like this option)
> 
> 
> A 4th option is to have much longer lived tokens everywhere (long enough for 
> this backup), but the user is able to expire it early via keystone whenever 
> they feel it might be compromised (aiui this is exactly how things work now - 
> we just need to increase the timeout).  Greater exposure to replay attacks, 
> but if detected they can still be invalidated quickly.
> 
> (This is the easiest option, it's basically just formalising what the 
> operators are already doing)
> 
> 
> A 5th option (wow) is to have the end user/client repeatedly push in fresh 
> tokens during long-running operations (and heat is the uber-example since it 
> basically wants to impersonate the user forever).  Those tokens would then 
> need to be refreshed all the way down the stack for any outstanding operations 
> that might need the new token.
> 
> (This or the 4th option seems ugly but unavoidable for "forever" services like 
> heat.  There has to be some way to invalidate their access if they go rogue, 
> either by time (and thus needs a refresh mechanism) or by invalidation-via-
> keystone (which implies the token lasts forever unless invalidated))

I think Keystone trusts are better for "forever" services, though I see
no reason why a trust token also couldn't have a limited number of uses
with a longer validity period.  The trust itself doesn't need an
expiration, so the trust can be executed at some future point in time to
get a limited use trust token.

> 
> 
> However we do it:  the "permission" to do the action should come from the 
> original user - and this is expressed as tokens coming from the original 
> client/user in some form.   By allowing services to create something without 
> the original client/user being involved, we're really just bypassing the token 
> authentication mechanism (and there are easier ways to ignore the token ;)

Yeah, this is ugly.  You give up any control you have as soon as you
hand off your token.  This is where restricted tokens would be ideal.
As it stands today, most services that are using trusts intercept the
user's token and create the trust AFAIK.  More ideal from a security
standpoint would be to ask the user to create a trust, which they
perform with a more privileged token than they ever pass around to any
service other than Keystone (like an unscoped token).  They would then
pass the trust ID to the service that needs to execute the trust at a
later date along with a more restricted scoped token.  This would
require behaviour such as I describe in this writeup:

  https://blog-nkinder.rhcloud.com/?p=101

-NGK



More information about the OpenStack-dev mailing list