Keystone explicitly lists the token identifiers for the revoked tokens if the token revocation lists. This lends itself to simple revocation checking: is the ID on the list? No? Token is still good. However, the number of drawbacks in this approach overwhelm the simplicity of it, and indicate a need for a better solution.
Problems
There is a race condition between when a token is published and a service fetches the revocation list. Since this is a configurable value, there might be a significant delay between token revocation and the remote service being aware of the revocation. However, if a malicious third party were able to fetch the revocation list, they would have a set of newly revoked tokens to use. Thus, the token revocation list is an administrative-only API. However, this is not the grounds for a scalable secure system. For one thing , it means that only administrators can check token validity. It also multiplies the effect of a compromise of one of the administrator accounts.
Token revocation is often a side effect of some other operation. For example, changing a password revokes all tokens issued prior to the password change. Disabling a trusts will disable all tokens created from that trust. Removing a role assignment from a user revokes all tokens that have that role assignment. The need to track down all of these tokens calls for a significant amount of bookkeeping on the token back end. While SQL is well suited for this kind of complex relationship, the key value store based backends are not. The memcache backend has had significant performance degradation due to the need to maintain an active list of all tokens issued to a specific user.
There is an outstanding bug in the memcache backend due to the fact that the token revocation list is stored in memory and not persisted to disk. If memcached is restarted, all of the revoked tokens will drop off the revocation list; Tokens become magically “unrevoked.” Dealing with this bug is the starting point of the design for the new approach to tokens.
Design Criteria
- Revocations should be stored in a separate backend from tokens. if the tokens are stored in ephemeral memcached, the revocations should be stored in persistent memory.
- Instead of a list of revoked tokens, the revocation list should be set of criteria. The data from the token can then be compared to the criteria to determine if the token is valid or not.
- Valid token revocation criteria must include:
- All tokens for a given domain issued prior to a given time.
- All tokens for a given project issued prior to a given time.
- All tokens for a given user issued prior to a given time.
- All non-delegated tokens for a user that have a specific role assignment and were issued prior to a given time.
- All tokens issued based on delegation agreements from a specified user (trustor) that has a specific role assignment and were issued prior to a given time.
- All tokens created from a specified trust_id or oauth token_id
Token Chaining
A token can be used to fetch another token. However, when a token is revoked by identifier, only the explicitly identified token is revoked. A better system would be to revoke that token and all tokens created from it, or that it was created from as the are all “fruit from a poisened tree.” There is no way to link from one token to another in the current system. While it would be trivial to add one, it would be yet another bit of overhead to an already overly complicated solution.
It turns out, however, that there is a way to link tokens together. When a token is used to request another token, Keystone restricts the new token to the expiration time of the original token. We can take advantage of this linkage to revoke all tokens of a specific class that have the same expiration date. This will possibly have a few “false positives” for revocation tests, but the number should be vanishingly small.
Implementation
As with all the public APIs from Keystone, we will not remove the existing API until they have been deprecated for a number of releases. That means that support for the existing revocation mechanisms must at least be available along side the new token revocation mechanism. However, an Open Stack deployer should be able to bypass the shortcomings of the existing mechanism. Thus, the new token revocation mechanism will be activated by default. In addition, a configuration option will allow the deactivation of the existing revocation API. If this configuration value is set, all of the tracking for token revocation in the token backend will be disabled, and the current (undocumented) revocation API calls will return 404.