karaf-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From David Bosschaert <david.bosscha...@gmail.com>
Subject Re: Some thoughts around adding security for Karaf Shell Commands
Date Thu, 22 Aug 2013 15:07:18 GMT
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
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message