Discussions around Keystone during the Juno Design Summit got me thinking about Keystone tokens and all of the places they get sent once they leave the user’s hands. A user usually gets a token from Keystone, then initiates an operation against a service like Nova. This service may then send off the users token to another service like Glance or Cinder to perform some portion of the intended operation. A users token may much more powerful that is needed for the intended operation, so there is a lot of trust that the user is putting into OpenStack services to not use the token in unintended or unauthorized ways. I feel that there is a lot of improvement that we can make here. Before discussing these improvements, we should review the current behavior around scoped Keystone tokens.
With Keystone, one can currently get tokens that are scoped to a project, or tokens that are unscoped. The idea of an unscoped token is that it can later be used to authenticate to Keystone to get a project scoped token. Aside from this ability, there is no use for an unscoped token as it can’t be used to perform any actions on other services since it contains no roles and no project. The unscoped token simply proves that a user was authenticated by Keystone.
With the current functionality, one is allowed to use an existing token to authenticate to Keystone to get a new token with a different project scope. The token used to authenticate can be scoped or unscoped. This allows one to switch from project to project without providing their actual user credentials. It also allows one to use a project scoped token to get an unscoped token.
The current behavior has some convenience properties, but it provides no actual security benefit. The project and roles in a token are designed to limit what the token actually can be used for. Given that Keystone tokens are bearer tokens, one needs to be concerned about any service that has access to the token performing operations that were not intended by the user. All services that receive the token must be trusted to not maliciously use the token.
Ideally, one would be able to supply a token that is only authorized to perform the intended task to minimize the exposure if the token is obtained my a malicious party. Unfortunately, this is not currently possible. If a user has a low set of privileges on a particular project, a token scoped to that project can still be used to obtain a token scoped to another project that might potentially have a high set of privileges. Essentially, this means that any token for a particular user can indirectly be used to perform any action that user is allowed to perform.
To allow for a user to create restricted tokens, we would need to make some changes to the way Keystone works when authenticating using an existing token. The number one rule should be that one can only use an existing token to create a more restrictive token. Said another way, privilege elevation should not be possible when authenticating with an existing Keystone token.
Project scoped tokens
In order to support the desired behavior, one would no longer be able to change the project scope by solely using an existing project scoped token. If the ability to change project scope without supplying the original user credential is needed, one should use an unscoped token to obtain a project scoped token.
An unscoped token should be analogous to a TGT in Kerberos. The unscoped token proves that a user authenticated to Keystone, but it can’t actually be used to perform any action other than obtaining a scoped token. The only way to obtain an unscoped token should be for the user to use their original credentials to authenticate to Keystone.
To describe how this would work, let’s consider the Horizon use-case. A user would authenticate to Horizon as usual via the login form. Horizon would then obtain an unscoped token from Keystone on the user’s behalf. When a project is selected in Horizon, the unscoped token will be used to authenticate to Keystone to obtain a new token that is scoped for the selected project. Horizon will retain the unscoped token in case a new project is selected before the user logs out or the session expires.
Project scoped tokens that can’t be used for privilege escalation are a step in the right direction, but it would be better to have the ability to create even more restrictive tokens to perform specific tasks. Ideally, a token would only have the roles required to perform the operations that it will be used for. This would require the ability to request a token
that only contains a subset of the roles that a user has on a project. I refer to this concept as role-filtered tokens.
The way that role filtered tokens would work is that a user can request a new token using an existing token, but they would include a list of roles that they want this new token to contain. Keystone would only honor this request if the original token that is used for authentication contains the requested roles. Keystone would also check if the requested
roles are still valid in the assignment database. This behavior would allow the capabilities of a token to be restricted, and anyone that has possession of the restricted token would not be able to use it to obtain a new token with greater capabilities.
To utilize role-filtered tokens, one would need to have some knowledge of the roles that are required to perform the operations that the token will be used for, but I think that this is fine in some circumstances. It might be possible to eventually allow a service to read a central policy to determine what roles are needed to perform a particular operation on
another service so it can restrict a token before sending if off. I think that this would have a significant security benefit over the current way that Keystone tokens work.