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

    Securing access to JNDI tree

    jcasp

      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
          gavinho

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

          • 2. Re: Securing access to JNDI tree
            starksm64
            • 3. Re: Securing access to JNDI tree
              gavinho

               

              "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
                starksm64

                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
                  gavinho

                  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
                    gavinho

                    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
                      starksm64

                      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
                        gavinho

                         

                        "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
                          starksm64

                          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
                            gavinho

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