Hi all,


From: Adam Young <ayo...@redhat.com<mailto:ayo...@redhat.com>>
Reply-To: "OpenStack Development Mailing List (not for usage questions)" 
Date: Tuesday, May 5, 2015 at 8:34 PM
Subject: Re: [openstack-dev] [keystone] On dynamic policy, role 
hierarchies/groups/sets etc.

On 05/05/2015 07:05 AM, Henry Nash wrote:
We’ve been discussing changes to these areas for a while - and although I think 
there is general agreement among the keystone cores that we need to change 
*something*, we’ve been struggling to get agreement on exactly how..  So to try 
and ground the discussion that will (I am sure) occur in Vancouver, here’s an 
attempt to take a step back, look at what we have now, as well as where, 
perhaps, we want to get to.

This is a great summary.  Thanks Henry.

Super helpful for sure!

The core functionality all this is related to is that of how does keystone & 
policy allow the checking of whether a given API call to an OpenStack service 
should be allowed to take place or not. Within OpenStack this is a two step 
process for an API caller….1) Get yourself a token by authentication and 
getting authorised for a particular scope (e.g. a given project), and then 2) 
Use that token as part of your API call to the service you are interested in. 
Assuming you do, indeed, have the rights to execute this API, somehow steps 1) 
and 2) give the policy engine enough info to say yes or no.

So first, how does this work today and (conceptually) how should we describe 
that?  Well first of all, in fact, strictly we don’t control access at the raw 
API level.  In fact, each service defines a series “capabilities” (which 
usually, but not always, map one-to-one with an API call).  These capabilities 
represent the finest grained access control we support via the policy engine. 
Now, in theory, the most transparent way we could have implemented steps 1) and 
2) above would have been to say that users should be assigned capabilities to 
projects….and then those capabilities would be placed in the token….allowing 
the policy engine to check if they match what is needed for a given capability 
to be executed. We didn’t do that since, a) this would probably end up being 
very laborious for the administrator (there would be lots of capabilities any 
given user would need), and b) the tokens would get very big storing all those 
capabilities. Instead, it was recognised that, usually, there are sets of these 
capabilities that nearly always go together - so instead let’s allow the 
creation of such sets….and we’ll assign those to users instead. So far, so 
good. What is perhaps unusual is how this was implemented. These capability 
sets are, today, called Roles…but rather than having a role definition that 
describes the capabilities represented by that role….instead roles are just 
labels - which can be assigned to users/projects and get placed in a tokens.  
The expansion to capabilities happens through the definition of a json policy 
file (one for each service) which must be processed by the policy engine in 
order to work out what whether the roles in a token and the role->capability 
mapping means that a given API can go ahead. This implementation leads to a 
number issues (these have all been raised by others, just pulling them together 
As I understand how this works conceptually, a policy makes go/no-go decisions 
based on two kinds of properties: (1) properties about the user making the API 
call  (which are encoded in the token) and (2) the API call name and arguments. 
  Is that right?

i) The role->capability mapping is rather static. Until recently it had to be 
stored in service-specific files pushed out to the service nodes out-of-band. 
Keystone does now provide some REST APIs to store and retrieve whole policy 
files, but these are a) course-grained and b) not really used by services 
anyway yet.

ii) As more and more clouds become multi-customer (i.e. a cloud provider 
hosting multiple companies on a single OpenStack installation), cloud providers 
will want to allow those customers to administer “their bit of the cloud”. 
Keystone uses the Domains concept to allow a cloud provider to create a 
namespace for a customer to create their own projects, users and groups….and 
there is a version of the keystone policy file that allows a cloud provider to 
effectively delegate management of these items to an administrator of that 
customer (sometimes called a domain administrator).  However, Roles are not 
part of that namespace - they exists in a global namespace (within a keystone 
installation). Diverse customers may have different interpretations of what a 
“VM admin” or a “net admin” should be allowed to do for their bit of the cloud 
- but  right now that differentiation is hard to provide. We have no support 
for roles or policy that are domain specific.
I wondered if we could properly protect the API call for adding a new Role 
using the current mechanism.  So I came up with a simple example.

Suppose we want to write policy about the API call: addRole(user, role-name).  
If we’re hosting both Pepsi and Coke, we want to write a policy that says that 
only someone in the Pepsi admin role can change roles for Pepsi users (likewise 
for Coke).  We’d want to write something like…

addRole(<user>, <role>) is permitted for <caller> if
    <caller> belongs to the Pepsi-admin role and
    <user> belongs to the Pepsi role

The policy engine knows if “<caller> belongs to the Pepsi-admin role” because 
that’s part of the token.  But the policy engine doesn’t know if “<user> 
belongs to the Pepsi role” because <user> is just an argument to the API call, 
so we don’t have role info about <user>.  This helps me understand *why* we 
can’t handle the multi-customer use case right now: the policy engine doesn’t 
have all the info it needs.

But otherwise, it seems, we could handle the multi-customer use-case using 
mechanism that already exists.  Are there other examples where they can’t write 
policy because the engine doesn’t have enough info?

iii) Although as stated in ii) above, you can write a policy file that 
differentiates between various levels of admin, or fine-tunes access to certain 
capabilities, the reality is that doing this is pretty un-intuative. The 
structure of a policy.json file that tries to do this is, indeed, complex (see 
Keystone’s as an example: 
 Adding more capability to this will likely only make the situation worse.

We have a number of specs taking shape to try and address the above (a number 
of them competing), so I wanted to propose with a set of guidelines for these:

a) Making the policy centrally sourced (i.e. in keystone) and more dynamic 
seems eminently sensible. We’ll need to work on notifications etc. for how 
services know the policy has changed, of course. Such a centralised capability 
allows us to not just use a json file to store policy, but perhaps a database - 
allowing more fine-grained access to policy rules via an API. See: 
https://review.openstack.org/#/c/147651/ and 
https://review.openstack.org/#/c/133814/ as examples.

b) One of the core disagreements has been around whether any additional 
structure we add to roles is processed at token generation time or at token 
analysis time by the policy engine. To be honest, I don’t think our deployers 
care - as long as we don’t break something like making tokens even bigger.  
What they will care about is whether they can hold in the heads the concepts 
for what it is they need to set up to achieve the policy framework that want. 
Let’s concentrate on making this easy for them, and under the hood we’ll solve 
the bits and bites.
Just so I’m following… At one end of the design spectrum, the token contains 
nothing but an unforgeable ID giving the identity of the user, and at the other 
end of the spectrum, the token encodes all information necessary for the 
policy-engine to make access control decisions.  If we choose to make it 
contain just the unforgeable ID, each service would be responsible for mapping 
a user ID to all the policy-relevant info about that user.  If the token 
contains all policy-relevant data, it gets huge, but there’s no burden on the 

Currently we’re somewhere in between where the token contains the unforgeable 
ID and the names of a few things (roles), and each service needs to know how to 
map those roles into policy-relevant data.  That sound about right?


c) We have had competing suggestions for role sets/group/hierarchies (see: 
https://review.openstack.org/#/c/125704/ and 
https://review.openstack.org/#/c/133855/ ). I would suggest that we go for a 
base functionality of role sets (where a role set can contain roles or other 
role sets)….where these can either be global in scope or
While I agree with the basic approach, I would argue instead that a Role is a 
set of capabilities, and so we don't need role sets, we need capability sets 
(which we have) and then we say a role can contain other roles.  The set of 
capabilites is then defined as the union of the capabilities assigned to it 
directly and the capabilites assigned to subordinate roles.

The set can be easily defined in the policy.json file.  So the requirment then 
is to keep the Keystone view of these nested roles in sync.  The database 
driven approach makes this simpler, but this can be done today by hand with the 
existing policy file.  Demonstrating this is part of my dynamic policy 

domain specific.  Both need to be supported and it must be possible for a cloud 
provider to delegate to a domain admin the ability to create their own role 
sets. Whether roles sets are processed by the policy engine or at token 
generation time (see b) above) is something we need to hash out. I’m actually 
Ok with either…as long as one development route is not inordinately longer than 
the other - and, at least for me, domain specific role sets must be in any 
first implementation (this is the customer need I see most). I wouldn’t rule 
out a development plan where we 1) get the API right, 2) implement it so that 
the tokens and policy doesn’t have to change (i.e. we expand role sets at token 
generation time), and then 3) push this capability into the policy engine 
itself.  If we can skip 2) and get to 3) quickly, more the better.
I think do step 3 first;  we can make the policy engine handle the rules 
inferences for roles as sets of  capabilities.  Policy generation from the 
database happens second, and the API for more fine grained control happens 

d) I’d like to keep in mind an eventual destination where services could 
“register their capabilities” via an API, policy rules and roles/sets can then 
be created via APIs that then allow assignments to be made in terms that make 
sense to a domain administrator (i.e. in terms that are meaningful to them), 
that make a customer hosted on a shared cloud feel that this really is "their 
That should work. In order for a user to get access to those new capabilites 
we'd have three choices:

1.  Add them to an existing role
2.  Add them to a new role and assign that new role as a subset of an existing 
3.  Add them to a new role and assign them to the user directly.


OpenStack Development Mailing List (not for usage questions)

OpenStack Development Mailing List (not for usage questions)
Unsubscribe: openstack-dev-requ...@lists.openstack.org?subject:unsubscribe

Reply via email to