← Back to team overview

nova team mailing list archive

Re: Thoughts on RBAC implementation

 

Agreed - there's not a great reason to limit to a single project manager.

On Aug 6, 2010, at 1:35 PM, Vishvananda Ishaya wrote:

> We also have the oddness of the project manager being a part of role checking but not actually being stored in ldap as a separate role.  Perhaps we should deprecate the ProjectManager item in ldap and turn it into an actual role.
> 
> On Fri, Aug 6, 2010 at 11:58 AM, Jay Pipes <jaypipes@xxxxxxxxx> wrote:
> Hi all (especially Vish and Devin)!
> 
> I've been going through the code in /nova/auth/* and have a few
> comments/suggestions on the implementation of authorization (not
> authentication) in Nova.  Love to get some feedback from y'all.
> 
> Currently, Nova's authorization is handled via role-based access
> control (RBAC).  Nothing wrong with this at all; it's very common and
> well-understood.
> 
> However, the current RBAC implementation, while quite elegant, isn't
> very flexible and is not configurable because:
> 
> 1) All the roles are hard-coded strings (netadmin, projectmanager, etc..)
> 2) All the permissions are hard-coded using the decorators in /nova/rbac.py.
> 
> As an example, here is the cloud controller's get_console_output() method:
> 
>    @rbac.allow('projectmanager', 'sysadmin')
>    def get_console_output(self, context, instance_id, **kwargs):
>        # instance_id is passed in as a list of instances
>        instance = self._get_instance(context, instance_id[0])
>        return rpc.call('%s.%s' % (FLAGS.compute_topic, instance['node_name']),
>            {"method": "get_console_output",
>             "args" : {"instance_id": instance_id[0]}})
> 
> The rbac.allow decorator establishes if the request context's user is
> in either the "projectmanager" or "sysadmin" roles.  This decorator
> pattern is very elegant and declarative.
> 
> Unfortunately, what this implementation means is that a user
> installing Nova has no say in:
> 
> 1) The names and number of roles
> 2) The access permissions for the hard-coded roles
> 
> without modifying the Nova source code, which of course isn't
> particularly friendly! :)
> 
> I propose changing the authorization code to allow a flexible RBAC
> configuration file (possibly XML?) that would allow installing users
> to implement their own security groups and permissions.  Of course,
> Nova would ship with a configuration file that matches the current
> hard-coded names and permissions...
> 
> Consider this possible configuration file:
> 
> ?xml version="1.0"?>
> <rbac>
> <roles>
>  <role id="all" desc="Anyone" />
>  <role id="projectmanager" desc="Project Manager" />
>  <role id="sysadmin" desc="Sys Admin" />
>  <role id="netadmin" desc="Network Admin" />
>  ....
> </roles>
> <resourcegroups>
>  <resourcegroup id="cloud">
>    <resource id="create_key_pair" allow="all" />
>    <resource id="delete_key_pair" allow="all" />
>    <resource id="describe_security_groups" allow="all" />
>    <resource id="create_security_group" allow="netadmin" />
>    <resource id="delete_security_group" allow="netadmin" />
>  ...
>  </resourcegroup>
> </resources>
> </rbac>
> 
> On controller initialization, the controller would call some interface
> that would parse this configuration file into an in-memory
> representation.  Imagine something like this:
> 
> // in /nova/auth/acl.py
> class ACL:
>  def __init__(self):
>    # Read config file and populate a dict of roles and resources...
>  def allowed_roles(self, resource_id):
>    # Takes a resource id and returns a set of allowed roles...
> 
> The elegant decorator pattern could, of course, still be used, but in
> a slightly different manner.  Something like this might work:
> 
> class CloudController(object):
>  ...
>  @rbac.check
>  def create_security_group(self, context, group_name, **kwargs):
>     ...
> 
> // in /nova/auth/rbac.py:
> from nova.auth import acl
> 
> acl = acl.ACL()
> 
> def check():
>    def wrap(f):
>        def wrapped_f(self, context, *args, **kwargs):
>            if context.user.is_superuser():
>                return f(self, context, *args, **kwargs)
>            f_name = f.__name__
>            roles = acl.get_allowed_roles(f_name)
>            for role in roles:
>                if __matches_role(context, role):
>                    return f(self, context, *args, **kwargs)
>            raise exception.NotAuthorized()
>        return wrapped_f
>    return wrap
> 
> def __matches_role(context, role):
>    if role == 'all':
>        return True
>    if role == 'none':
>        return False
>    return context.project.has_role(context.user.id, role)
> 
> Thoughts?
> 
> -jay
> 
> _______________________________________________
> Mailing list: https://launchpad.net/~nova
> Post to     : nova@xxxxxxxxxxxxxxxxxxx
> Unsubscribe : https://launchpad.net/~nova
> More help   : https://help.launchpad.net/ListHelp
> 
> _______________________________________________
> Mailing list: https://launchpad.net/~nova
> Post to     : nova@xxxxxxxxxxxxxxxxxxx
> Unsubscribe : https://launchpad.net/~nova
> More help   : https://help.launchpad.net/ListHelp


References