karaf-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Christian Schneider <ch...@die-schneider.net>
Subject Re: Some thoughts around adding security for Karaf Shell Commands
Date Thu, 22 Aug 2013 22:14:16 GMT
Sounds great. I have not yet looked into it in detail but the concept 
sounds decent.

One thing you should keep in mind is to make the authorization 
exchangeable. For example at Talend we provide an xacml based pdp. So it 
would be great to have a hook wehere we can plug this in to
do the auth decisions. Personally I am not a fan of xacml but this shows 
that different organizations would probably like to treat this differently.

The way round your aproach sounds much more maintainable than xacml. So 
it might even be interesting to attach a pdp to your authorization impl :-)

I have one other idea. How about doing the authorization for jmx and 
commands only on the service level? At least in karaf 3 both use the 
same services so securing only the service instead of jmx and commands 
would reduce the number of config settings needed.

For example:
jmx: featureJMXBean.install(feature)
command: feature:install <feature>

Both would be secured by simply securing the FeatureService.

One thing I am not sure about btw. is doing too much magic behind the 
scenes. Like the config admin config you described that causes other 
configs to be created on the fly. Perhaps we find a simpler model that also
works. Currently I do not have a good idea how to handle it though.

Christian

Am 22.08.2013 17:07, schrieb David Bosschaert:
> Hi all,
>
> As suggested by Christian, I started looking at adding role-based access to
> OSGi services in general (in Karaf) and applying this to the Karaf commands.
> At this point I have something that kinda works. It proxies services for
> service consumers using service registry hooks and on invocation checks
> that the Subject associated with the current Access Control Context has the
> right roles. If not it blocks the service invocation by throwing a
> SecurityException.
>
> Not all services are secured this way, there is a new property in
> etc/system.properties that selects what services are processed this way
> using a simple OSGi service filter. To apply it to the shell commands it is
> set to the following:
>    karaf.secured.services=(&(osgi.command.scope=*)(osgi.command.function=*))
>
> The actual ACLs for the secured services are defined using ConfigAdmin in a
> way that's pretty much identical to what I did for the JMX acls in
> KARAF-2435, except that the PID doesn't matter. The configuration is
> matched with a service on a 'service.guard' property. The other entries
> match method names of the service. They can also match values passed in (as
> you can do with JMX), so you can define different roles for doit("foo") and
> doit("bar"). An example configuration could look like this:
>    service.guard = (objectClass=org.acme.TestServiceAPI)
>    doit = admin, viewer
>    doit["foo"] = admin
>
> So the next thing I did was look at whether this could be applied to the
> shell commands. The problem was that every little command is a separate
> service so this would potentially be a lot of configuration files for the
> administrator to maintain. You really want to define the ACLs for a single
> scope in a single configuration file, something like this (for the bundle
> scope):
>    install = manager
>    start = manager
>    list = manager, viewer
>    stop = manager
>    stop[/.*0.*/] = admin # only admin can stop the framework
>    uninstall = admin
> To fit with the general service ACL model this would have to be 5 different
> configuration files (one for each command). I thought that that was not
> very user friendly. Therefore I came up with a mechanism that accepts
> ConfigAdmin configuration for commands in the same scope like the above and
> then generates additional ConfigAdmin configuration on the fly that
> conforms to the general service ACL form.
>
> With that, enabled... let's say I'm logged in as a 'manager', with the
> above example configuration for the bundle scope, the it has the following
> effect:
>    karaf@root()> stop 50
>    # works
>    karaf@root()> stop 0
>    Error executing command: Insufficient credentials.
> Which is pretty much what I wanted to achieve :)
>
> So basically what we have here is a combination of two things:
> 1. Modification of service registrations to add a roles property which is
> then used by the CommandProcessor to only show the commands that the user
> associated with the active console can potentially execute (it could still
> reject commands based on arguments passed in).
> 2. Proxying of services (including shell command services) that check that
> the Subject associated with the current AccessControlContext has the right
> roles to make this invocation.
> The role-checking is still done outside of the service implementations. The
> actual services being secured don't need to change their code.
>
> You can see my experimental implementation at my branch here:
> https://github.com/bosschaert/karaf/commit/2668b88a7ddfb1ba93e7e732884734ff7dc0d1a3
> That branch is not finished (cleanup, tests terribly lacking, no
> optimization) and some things could be made a little more user friendly,
> but it contains the general idea... If people are happy with the general
> idea I can focus a little on tiding it up...
>
> Thoughts anyone?
>
> David
>
> On 19 August 2013 10:56, David Bosschaert <david.bosschaert@gmail.com>wrote:
>
>> Hi Christian,
>>
>> On 19 August 2013 10:29, Christian Schneider <chris@die-schneider.net>wrote:
>>
>>> The idea was to use Shiro to establish a kind of security context in a
>>> thread local. Your approach of using Subject.doas might be the better
>>> alternative though.
>>>   In any case we should recommend one standard approach to establish the
>>> security context. Perhaps we could even allow both and have adapters to
>>> establish one context from another.
>>
>> Yep, this needs to be standard.
>>
>>
>>>   On 08/15/2013 10:16 PM, Christian Schneider wrote:
>>>>>   I like the idea of adding permissions to the commands. I wonder though
>>>>>> if this is perhaps a more general problem that not only affects
>>>>>> commands.
>>>>>>
>>>>>> So how about adding a generic permission check for services? For
>>>>>> example
>>>>>> I would like to use the @RolesAllowed annotation to couple roles
or
>>>>>> permissions with service methods.
>>>>>> A service registry hook could then check that the caller has the
>>>>>> permission before allowing the call. Of course there could be other
>>>>>> additional way of adding this information like the service properties
>>>>>> you mentioned.
>>>>>>
>>>>>>   I'm not sure I like the annotation approach. One of the things
that I
>>>> would
>>>> like to enable is for customers to change the roles associated with
>>>> operations/service invocations afterwards, simply because the roles
>>>> chosen
>>>> by the developer may not match up with the roles mappings of all
>>>> organizations. With an annotation approach you'd have to modify the code
>>>> and recompile it when you want to change them. I prefer to use OSGi
>>>> ConfigAdmin for that since it completely decouples this information from
>>>> the code and can easily be modified later...
>>>>
>>>> We should also provide a generic way to attach authentication
>>>>
>>> I think there could be three levels of external configurability:
>>>
>>> 1. You could use annotations with roles like
>>> @RolesAllowed("admin")
>>> public void deleteUser(...);
>>>
>>> 2. You could use annotations to store permissions
>>> @RolesAllowed("Userservice.**deleteUser")
>>> public void deleteUser(...);
>>>
>>> Then the mapping to roles could be done by using groups in the simplest
>>> form. group UserService.deleteUser: admin, ...
>>>
>>> 3. You could completely externalize the decision. In this case a Policy
>>> Decision Point approach could make sense.
>>> You extract the meta information of a service call, give it to a pdp and
>>> get back an authorization decision.
>>
>>
>> I would favor having just option 3. I can see that option 2 provides some
>> form of indirection, but you still need to modify the service code to add
>> the annotation in the first place. That might be ok for services that have
>> their code in the Karaf codebase, but for outside services it would be
>> pretty much impossible.
>>
>> I would rather have a clear single way of configuring this so that it's
>> very clear what the definition is - if a security guy wants to figure out
>> what roles are needed for options 1 & 2 (s)he needs to have access to the
>> source to read how the annotation was declared, or otherwise rely on
>> documentation, which you are never sure is actually in sync with the code.
>>
>> If we'd opt for a combined annotation+external approach it's still quite
>> hard to get a full overview of what roles are required to invoke what if
>> you want to understand that... Hence I would simply go for having just
>> option 3 and keep everything in one place.
>>
>>
>>
>>>   information to a thread that calls a service. I tought about using
>>>>>> apache shiro for this but I am not sure if it is universal enough.
>>>>>>
>>>>>>   I don't understand why you need Shiro for this.
>>>> Isn't javax.security.auth.Subject.**doAs() the standard way to do this?
>>>>
>>> Probably it is. How does this work internally? Does it also use a thread
>>> local? How  does it work if you spawn a new thread using an executor or
>>> similar?
>>> I think we should do some examples to see how it works in practice.
>>>
>>
>> Re Subject.doAs(). The Subject would be configured via JAAS to contain the
>> appropriate roles for the current user, as we do today in Karaf (although
>> you should be able to configure Shiro to provide this info)...
>> Once you're inside a piece of code that is executed via Subject.doAs()
>> (which could be in a different thread) you can do:
>>
>>    AccessControlContext acc = AccessController.getContext();
>>    Subject subject = Subject.getSubject(acc);
>> At this point you can get all the Principals from the subject, e.g. all
>> the roles:
>>    Set<RolePrincipal> roles = subject.getPrincipals(RolePrincipal.class);
>>
>> This is all plain standard J2SE code, no library dependencies...
>>
>> Cheers,
>>
>> David
>>


-- 
  
Christian Schneider
http://www.liquid-reality.de

Open Source Architect
Talend Application Integration Division http://www.talend.com


Mime
View raw message