Return-Path: X-Original-To: apmail-karaf-dev-archive@minotaur.apache.org Delivered-To: apmail-karaf-dev-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 43298109DF for ; Thu, 22 Aug 2013 22:14:48 +0000 (UTC) Received: (qmail 54115 invoked by uid 500); 22 Aug 2013 22:14:48 -0000 Delivered-To: apmail-karaf-dev-archive@karaf.apache.org Received: (qmail 54094 invoked by uid 500); 22 Aug 2013 22:14:48 -0000 Mailing-List: contact dev-help@karaf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@karaf.apache.org Delivered-To: mailing list dev@karaf.apache.org Received: (qmail 54086 invoked by uid 99); 22 Aug 2013 22:14:47 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 22 Aug 2013 22:14:47 +0000 X-ASF-Spam-Status: No, hits=-0.5 required=5.0 tests=FREEMAIL_ENVFROM_END_DIGIT,RCVD_IN_DNSWL_LOW,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: domain of cschneider111@gmail.com designates 209.85.213.49 as permitted sender) Received: from [209.85.213.49] (HELO mail-yh0-f49.google.com) (209.85.213.49) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 22 Aug 2013 22:14:43 +0000 Received: by mail-yh0-f49.google.com with SMTP id v1so307694yhn.22 for ; Thu, 22 Aug 2013 15:14:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:message-id:date:from:user-agent:mime-version:to:subject :references:in-reply-to:content-type:content-transfer-encoding; bh=DxRNwcdRHtVPBk4Lii+EQJwBxvCgfKrg5UDA5FdTfGw=; b=1EFBO5IQ8zVrrcsre+iS7Fgd19zQEvPIGnn9U4z0LECmQRmHrrao93YPemcty2nAPX MnOR2f0Ylwv+mbNj3m67S5bazlTcaWlJ+Q2X5Nnwo9KgpLnOFTIP+CqGVdiiesiiMwow 9txTAeVa6w9f7oo+J4ck5Vmj+E5GAFj97tnxnZQuBIsFtCEIpjtggMyuemxoIfHOpwJn w7DZtH9j8KkG+/8XMZguoef8JPlO948ONciQ78pWyTgfrTJtl7pV4qlaHFq2CEsrZNFo LhPy4Ufs74D6fiFj0fYlifqVRrw/9AAYZnUVKbEK62Jj3YvnuLdVLPXmsmMeD7Y7hs4Y U0og== X-Received: by 10.236.157.74 with SMTP id n50mr162941yhk.162.1377209662807; Thu, 22 Aug 2013 15:14:22 -0700 (PDT) Received: from [192.168.0.102] (HSI-KBW-5-10-51-197.hsi18.kabel-badenwuerttemberg.de. [5.10.51.197]) by mx.google.com with ESMTPSA id c6sm16804682yhl.22.1969.12.31.16.00.00 (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 22 Aug 2013 15:14:22 -0700 (PDT) Sender: Christian Schneider Message-ID: <52168D38.9010704@die-schneider.net> Date: Fri, 23 Aug 2013 00:14:16 +0200 From: Christian Schneider User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20130801 Thunderbird/17.0.8 MIME-Version: 1.0 To: dev@karaf.apache.org Subject: Re: Some thoughts around adding security for Karaf Shell Commands References: <520D3728.4060308@die-schneider.net> <520D38D5.6000306@nanthrax.net> <5211E58C.6060200@die-schneider.net> In-Reply-To: Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-Virus-Checked: Checked by ClamAV on apache.org 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 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 wrote: > >> Hi Christian, >> >> On 19 August 2013 10:29, Christian Schneider 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 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