10 Replies Latest reply on Dec 6, 2004 9:34 PM by Gavin Ho

    Securing access to JNDI tree

    justin Newbie

      I've been reading about how to disable full access to a JNDI tree on a public JBoss server, and came across the following line in the "Securing a Default JBoss Installation" article on sourceforge:
      "You can change the port and interface which the naming service is bound on, as well as add role based security using a custom XMBean configuration."

      I've seen the JNDI service in conf/jboss-service.xml and how to change the interface and port, but am stuck on how to add role-based security to the tree using the XMBean config. Could someone point me to an example of how to do this?

      Thank you very much
      Justin

        • 1. Re: Securing access to JNDI tree
          Gavin Ho Newbie

          Yes, I am also looking for this topic. Anyway has update about it?

          • 3. Re: Securing access to JNDI tree
            Gavin Ho Newbie

             

            "scott.stark@jboss.org" wrote:
            http://www.jboss.org/wiki/Wiki.jsp?page=XMBeansforSecurity


            I followed the examples but encountered "NoSuchMethodException" when startup JBoss. The problem is JBoss startup sequence tried to use org.jboss.mx.server.MBeanInvoker interface to lookup a constructor for my Interceptor.

            Can any one help?

            Here is the output from startup log
            =======================

            org.jboss.mx.interceptors.JNDISecurityInterceptor
            java.lang.NoSuchMethodException: org.jboss.mx.interceptors.JNDISecurityInterceptor.<init>(org.jboss.mx.server.MBeanInvoker)
             at java.lang.Class.getConstructor0(Class.java:1929)
             at java.lang.Class.getConstructor(Class.java:1019)
             at org.jboss.mx.modelmbean.ModelMBeanInvoker.getInterceptors(ModelMBeanInvoker.java:552)
             at org.jboss.mx.modelmbean.ModelMBeanInvoker.configureInterceptorStack(ModelMBeanInvoker.java:458)
             at org.jboss.mx.modelmbean.XMBean.configureInterceptorStack(XMBean.java:360)
             at org.jboss.mx.modelmbean.ModelMBeanInvoker.invokePreRegister(ModelMBeanInvoker.java:350)
             at org.jboss.mx.server.AbstractMBeanInvoker.preRegister(AbstractMBeanInvoker.java:539)
             at org.jboss.mx.server.registry.BasicMBeanRegistry.invokePreRegister(BasicMBeanRegistry.java:644)
             at org.jboss.mx.server.registry.BasicMBeanRegistry.registerMBean(BasicMBeanRegistry.java:199)
             at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
             at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
             at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
             at java.lang.reflect.Method.invoke(Method.java:324)
             at org.jboss.mx.server.ReflectedDispatcher.dispatch(ReflectedDispatcher.java:60)
             at org.jboss.mx.server.Invocation.dispatch(Invocation.java:61)
             at org.jboss.mx.server.Invocation.dispatch(Invocation.java:53)
             at org.jboss.mx.server.Invocation.invoke(Invocation.java:86)
             at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:185)
             at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:473)
             at org.jboss.mx.server.MBeanServerImpl.registerMBean(MBeanServerImpl.java:1018)
             at org.jboss.mx.server.MBeanServerImpl.registerMBean(MBeanServerImpl.java:981)
             at org.jboss.mx.server.MBeanServerImpl.createMBean(MBeanServerImpl.java:318)
             at org.jboss.system.ServiceCreator.install(ServiceCreator.java:152)
             at org.jboss.system.ServiceConfigurator.internalInstall(ServiceConfigurator.java:149)
             at org.jboss.system.ServiceConfigurator.install(ServiceConfigurator.java:114)
             at org.jboss.system.ServiceController.install(ServiceController.java:198)
             at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
             at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
             at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
             at java.lang.reflect.Method.invoke(Method.java:324)
             at org.jboss.mx.server.ReflectedDispatcher.dispatch(ReflectedDispatcher.java:60)
             at org.jboss.mx.server.Invocation.dispatch(Invocation.java:61)
            


            What I did was,
            1. compile the following class
            =====================
            package org.jboss.mx.interceptors;
            
            imports ignored
            
            public final class JNDISecurityInterceptor
             extends AbstractInterceptor
            {
             private static Logger log = Logger.getLogger(JNDISecurityInterceptor.class);
             private static final Principal READER_ROLE = new SimplePrincipal("JNDIReader");
             private static final Principal WRITER_ROLE = new SimplePrincipal("JNDIWriter");
            
             private String securityDomain;
             private SubjectSecurityManager authMgr;
             private RealmMapping roleMgr;
            
             public JNDISecurityInterceptor()
             {
             super("JNDI Security Interceptor");
             }
             public String getSecurityDomain()
             {
             return securityDomain;
             }
             public void setSecurityDomain(String securityDomain) throws Exception
             {
             log.info("setSecurityDomain: "+securityDomain);
             this.securityDomain = securityDomain;
             InitialContext ctx = new InitialContext();
             this.authMgr = (SubjectSecurityManager) ctx.lookup(securityDomain);
             this.roleMgr = (RealmMapping) ctx.lookup(securityDomain);
             }
            
             // Interceptor overrides -----------------------------------------
             public Object invoke(Invocation invocation) throws InvocationException
             {
             String opName = invocation.getName();
             log.info("invoke, opName="+opName);
            
             // If this is not the invoke(Invocation) op just pass it along
             if( opName == null || opName.equals("invoke") == false )
             return invocation.nextInterceptor().invoke(invocation);
             Object[] args = invocation.getArgs();
             org.jboss.invocation.Invocation invokeInfo =
             (org.jboss.invocation.Invocation) args[0];
             // There must be a valid security manager
             if( authMgr == null || roleMgr == null )
             {
             String msg = "No security mgr configured, check securityDomain: "+securityDomain;
             throw new SecurityException(msg);
             }
            
             // Get the security context passed from the client
             Principal principal = invokeInfo.getPrincipal();
             Object credential = invokeInfo.getCredential();
             if( authMgr.isValid(principal, credential) == false )
             {
             String msg = "Failed to authenticate principal: "+principal;
             throw new SecurityException(msg);
             }
             HashSet methodRoles = new HashSet();
             Method method = invokeInfo.getMethod();
             boolean isRead = isReadMethod(method);
             if( isRead == true )
             methodRoles.add(READER_ROLE);
             else
             methodRoles.add(WRITER_ROLE);
             if( roleMgr.doesUserHaveRole(principal, methodRoles) == false )
             {
             String msg = "Failed to authorize subject: "+authMgr.getActiveSubject()
             + " principal: " + principal
             + " for access roles:" + methodRoles;
             throw new SecurityException(msg);
             }
            
             // Let the invocation go
             return invocation.nextInterceptor().invoke(invocation);
             }
            
             private boolean isReadMethod(Method method)
             {
             boolean isRead = true;
             String name = method.getName();
             String name = method.getName();
             isRead = name.equals("lookup") || name.equals("list")
             || name.equals("listBindings");
             return isRead;
             }
            }
            


            2. modified the mbean declaration for NamingService in ~/conf/jboss-service.xml
            ===================================
            <mbean code="org.jboss.naming.NamingService"
             name="jboss:service=Naming"
             xmbean-dd="xmdesc/NamingService-xmbean.xml" >
            <!-- other attributes omitted -->
             </mbean>
            

            3. create file ~/conf/xmdesc/NamingService-xmbean.xml
            ======================================
            <?xml version="1.0" encoding="UTF-8"?>
            <!DOCTYPE mbean PUBLIC
             "-//JBoss//DTD JBOSS XMBEAN 1.1//EN"
             "http://www.jboss.org/j2ee/dtd/jboss_xmbean_1_0.dtd"
            [
             ATTLIST interceptor securityDomain CDATA #IMPLIED
            ]>
            
            <mbean>
             <description>A deployment of the standard JBoss JNDI naming servr
             that employs custom interceptors to add security
             </description>
            
             <descriptors>
             <interceptors>
             <interceptor code="org.jboss.mx.interceptors.JNDISecurityInterceptor"
             securityDomain="java:/jaas/jmx-console"/>
             </interceptors>
             </descriptors>
            
             <class>org.jboss.naming.NamingService</class>
            
             <constructor>
             <description>The default constructor</description>
             <name>NamingService</name>
             </constructor>
            
             <attribute access="read-only" getMethod="getMethodMap">
             <name>MethodMap</name>
             <type>java.util.Map</type>
             </attribute>
             <attribute access="read-write" getMethod="getPort" setMethod="setPort">
             <name>Port</name>
             <type>int</type>
             </attribute>
             <attribute access="read-write" getMethod="getInstallGlobalService"
             setMethod="setInstallGlobalService">
             <name>InstallGlobalService</name>
             <type>boolean</type>
             </attribute>
            
             <!-- Operations -->
             <operation>
             <description>The start lifecycle operation</description>
             <name>start</name>
             </operation>
             <operation>
             <description>The stop lifecycle operation</description>
             <name>stop</name>
             </operation>
             <operation>
             <description>The detyped lifecycle operation (for internal use only)</description>
             <name>jbossInternalLifecycle</name>
             <parameter>
             <description>The lifecycle operation</description>
             <name>method</name>
             <type>java.lang.String</type>
             </parameter>
             <return-type>void</return-type>
             </operation>
            
             <operation>
             <description>The generic invocation operation used by detached invokers
             to route requests to the service
             </description>
             <name>invoke</name>
             <parameter>
             <description>The Naming interface method invocation encapsulation
             </description>
             <name>invocation</name>
             <type>org.jboss.invocation.Invocation</type>
             </parameter>
             <return-type>java.lang.Object</return-type>
             </operation>
            </mbean>
            


            • 4. Re: Securing access to JNDI tree
              Scott Stark Master

              That is an irrelevant debug level msg simply saying the interceptor does not have a ctor that takes an MBeanInvoker. The default ctor will be used instead.

              • 5. Re: Securing access to JNDI tree
                Gavin Ho Newbie

                Yeah, the ctor not found message is not fatal. I managed to start JBoss successfully with the interceptor attached. But I am still interesting to know how can we remove such noisy message.

                I tested using a external client program to do JNDI lookup but the invoke() method of interceptor had not being called for security checking as expected.

                I can only see the invoke method being called serveral times during JBoss startup.

                8-(

                • 6. Re: Securing access to JNDI tree
                  Gavin Ho Newbie

                  Browsed thru the source codes. The interceptor as described in URL:

                  http://www.jboss.org/wiki/Wiki.jsp?page=XMBeansforSecurity

                  only intercept calls to object org.jboss.naming.NamingService

                  But according to the class diagram in URL

                  http://docs.jboss.org/jbossas/admindevel326/html/ch3.chapter.html#ch3.jbossns.fig

                  JNDI client only interact with object org.jnp.server.NamingServer thru
                  interface org.jnp.interfaces.Name, the interceptor will not be called,
                  hence the access control still will not in place.

                  Am I right?

                  • 7. Re: Securing access to JNDI tree
                    Scott Stark Master

                    No. The org.jnp.interfaces.Naming interface is the interface exposed by the NamingService xmbean. All Naming interface invocations ultimately go through the NamingService xmbean and its associated interceptors.

                    • 8. Re: Securing access to JNDI tree
                      Gavin Ho Newbie

                       

                      "scott.stark@jboss.org" wrote:
                      No. The org.jnp.interfaces.Naming interface is the interface exposed by the NamingService xmbean. All Naming interface invocations ultimately go through the NamingService xmbean and its associated interceptors.


                      Browsed thru the source code tree jboss-3.2.5-src\naming but I really can't find out how invocations on Naming interface get thru the NamingService xmbean and its associated interceptors.

                      Can you provide some more hints?

                      • 9. Re: Securing access to JNDI tree
                        Scott Stark Master

                        Ok, I see your confusion now and its due to not showing how to expose the NamingService via the detached invoker framework as a Naming proxy. Go reread the wiki which I have updated to show all of the details for changing the conf/jboss-service.xml NamingService to use a custom security interceptor. You will have to use 3.2.6+ for this to work correctly as you need to be able to replace the bootstrap proxy with the detached invoker proxy so that all externali jndi lookups are routed to the NamingService.invoke(Invocation) operation.

                        http://www.jboss.org/wiki/Wiki.jsp?page=XMBeansforSecurity

                        • 10. Re: Securing access to JNDI tree
                          Gavin Ho Newbie

                          Great, I have set up access control to JNDI tree. Thx.