You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2020/04/09 06:29:46 UTC

[karaf] branch master updated: KARAF-6634 - Prevent JMX rebinding

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/karaf.git


The following commit(s) were added to refs/heads/master by this push:
     new c30ed5d  KARAF-6634 - Prevent JMX rebinding
     new ed83995  Merge pull request #1074 from coheigea/KARAF-6634
c30ed5d is described below

commit c30ed5d365d564ff96a334dbfa1eb3f0f67a52ea
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Thu Mar 12 16:24:33 2020 +0000

    KARAF-6634 - Prevent JMX rebinding
---
 management/server/pom.xml                          |   1 +
 .../karaf/management/ConnectorServerFactory.java   | 214 ++++++++++++++++++++-
 .../karaf/management/RmiRegistryFactory.java       | 181 -----------------
 .../karaf/management/internal/Activator.java       |  30 +--
 4 files changed, 221 insertions(+), 205 deletions(-)

diff --git a/management/server/pom.xml b/management/server/pom.xml
index 8f2e6ee..2400fa0 100644
--- a/management/server/pom.xml
+++ b/management/server/pom.xml
@@ -129,6 +129,7 @@
                             org.osgi.framework;version="[1,3)",
                             com.sun.jmx.remote.protocol;resolution:=optional,
                             com.sun.jdmk.security.sasl;resolution:=optional,
+                            sun.rmi*;resolution:=optional,
                             *
                         </Import-Package>
                         <Private-Package>
diff --git a/management/server/src/main/java/org/apache/karaf/management/ConnectorServerFactory.java b/management/server/src/main/java/org/apache/karaf/management/ConnectorServerFactory.java
index ed8e248..a8be130 100644
--- a/management/server/src/main/java/org/apache/karaf/management/ConnectorServerFactory.java
+++ b/management/server/src/main/java/org/apache/karaf/management/ConnectorServerFactory.java
@@ -18,8 +18,10 @@ package org.apache.karaf.management;
 
 import org.apache.karaf.jaas.config.KeystoreManager;
 import org.apache.karaf.management.internal.MBeanInvocationHandler;
+import org.osgi.framework.BundleContext;
 
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.lang.reflect.Proxy;
 import java.net.BindException;
 import java.net.InetAddress;
@@ -28,11 +30,23 @@ import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.SocketAddress;
 import java.net.SocketException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.nio.channels.ServerSocketChannel;
+import java.rmi.AccessException;
+import java.rmi.AlreadyBoundException;
+import java.rmi.NotBoundException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
 import java.rmi.server.RMIClientSocketFactory;
 import java.rmi.server.RMIServerSocketFactory;
+import java.rmi.server.UnicastRemoteObject;
 import java.security.GeneralSecurityException;
 import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.Map;
 
 import javax.management.JMException;
@@ -42,6 +56,7 @@ import javax.management.remote.JMXConnectorServer;
 import javax.management.remote.JMXConnectorServerFactory;
 import javax.management.remote.JMXServiceURL;
 import javax.management.remote.rmi.RMIConnectorServer;
+import javax.management.remote.rmi.RMIJRMPServerImpl;
 import javax.net.ServerSocketFactory;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLParameters;
@@ -66,6 +81,8 @@ public class ConnectorServerFactory {
     private boolean threaded = false;
     private boolean daemon = false;
     private JMXConnectorServer connectorServer;
+    private Remote remoteServerStub;
+    private RMIJRMPServerImpl rmiServer;
     private JMXConnectorServer jmxmpConnectorServer;
 
     private long keyStoreAvailabilityTimeout = 5000;
@@ -80,6 +97,14 @@ public class ConnectorServerFactory {
     private String trustStore;
     private String keyAlias;
 
+    private int port = Registry.REGISTRY_PORT;
+    private String host;
+    private Registry registry;
+    private boolean locate;
+    private boolean create = true;
+    private boolean locallyCreated;
+    private BundleContext bundleContext;
+
     public MBeanServer getServer() {
         return server;
     }
@@ -269,12 +294,88 @@ public class ConnectorServerFactory {
         return this.authenticatorType.equals(AuthenticatorType.CERTIFICATE);
     }
 
+    /**
+     * @return the create
+     */
+    public boolean isCreate() {
+        return create;
+    }
+
+    /**
+     * @param create the create to set
+     */
+    public void setCreate(boolean create) {
+        this.create = create;
+    }
+
+    /**
+     * @return the locate
+     */
+    public boolean isLocate() {
+        return locate;
+    }
+
+    /**
+     * @param locate the locate to set
+     */
+    public void setLocate(boolean locate) {
+        this.locate = locate;
+    }
+
+    /**
+     * @return the port
+     */
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * @param port the port to set
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
     public void init() throws Exception {
 
+        JMXServiceURL url = new JMXServiceURL(this.serviceUrl);
+
+        if (registry == null && locate) {
+            try {
+                Registry reg = LocateRegistry.getRegistry(host, getPort());
+                reg.list();
+                registry = reg;
+            } catch (RemoteException e) {
+                // ignore
+            }
+        }
+        if (registry == null && create) {
+            registry = new JmxRegistry(getPort(), getBindingName(url));
+            locallyCreated = true;
+        }
+        if (registry != null) {
+            // register the registry as an OSGi service
+            Hashtable<String, Object> props = new Hashtable<>();
+            props.put("port", getPort());
+            props.put("host", getHost());
+            bundleContext.registerService(Registry.class, registry, props);
+        }
+
         if (this.server == null) {
             throw new IllegalArgumentException("server must be set");
         }
-        JMXServiceURL url = new JMXServiceURL(this.serviceUrl);
         if ( isClientAuth() ) {
             this.secured = true;
         }
@@ -291,7 +392,11 @@ public class ConnectorServerFactory {
 
         MBeanInvocationHandler handler = new MBeanInvocationHandler(server, guard);
         MBeanServer guardedServer = (MBeanServer) Proxy.newProxyInstance(server.getClass().getClassLoader(), new Class[]{ MBeanServer.class }, handler);
-        this.connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, this.environment, guardedServer);
+
+        rmiServer = new RMIJRMPServerImpl(getServerPort(serviceUrl), null, null, environment);
+
+        // Create the connector server now.
+        this.connectorServer = new RMIConnectorServer(url, environment, rmiServer, guardedServer);
 
         if (this.objectName != null) {
             this.server.registerMBean(this.connectorServer, this.objectName);
@@ -311,6 +416,7 @@ public class ConnectorServerFactory {
                     try {
                         Thread.currentThread().setContextClassLoader(ConnectorServerFactory.class.getClassLoader());
                         connectorServer.start();
+                        remoteServerStub = rmiServer.toStub();
                         if (jmxmpEnabled && jmxmpConnectorServer != null) {
                             jmxmpConnectorServer.start();
                         }
@@ -334,6 +440,7 @@ public class ConnectorServerFactory {
                 connectorThread.start();
             } else {
                 this.connectorServer.start();
+                remoteServerStub = rmiServer.toStub();
                 if (jmxmpEnabled && jmxmpConnectorServer != null) {
                     jmxmpConnectorServer.start();
                 }
@@ -349,6 +456,45 @@ public class ConnectorServerFactory {
         }
     }
 
+    protected static String getBindingName(final JMXServiceURL jmxServiceURL) {
+        final String urlPath = jmxServiceURL.getURLPath();
+
+        try {
+            if (urlPath.startsWith("/jndi/")) {
+                return new URI(urlPath.substring(6)).getPath()
+                        .replaceAll("^/+", "").replaceAll("/+$", "");
+            }
+        } catch (URISyntaxException e) {
+            // ignore
+        }
+
+        return "jmxrmi"; // use the default
+    }
+
+    static int getServerPort(final String url) {
+        int portStart = url.indexOf("localhost") + 10;
+        int portEnd;
+        int port = 0;
+        if (portStart > 0) {
+            portEnd = indexNotOfNumber(url, portStart);
+            if (portEnd > portStart) {
+                final String portString = url.substring(portStart, portEnd);
+                port = Integer.parseInt(portString);
+            }
+        }
+        return port;
+    }
+
+    private static int indexNotOfNumber(String str, int index) {
+        int i = 0;
+        for (i = index; i < str.length(); i++) {
+            if (str.charAt(i) < '0' || str.charAt(i) > '9') {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     public void destroy() throws Exception {
         try {
             if (this.connectorServer != null) {
@@ -357,6 +503,33 @@ public class ConnectorServerFactory {
             if (this.jmxmpEnabled && this.jmxmpConnectorServer != null) {
                 this.jmxmpConnectorServer.stop();
             }
+
+            if (registry != null && locallyCreated) {
+                Registry reg = registry;
+                registry = null;
+                UnicastRemoteObject.unexportObject(reg, true);
+
+                // clear TCPEndpointCache
+                try {
+                    Class<?> cls = getClass().getClassLoader().loadClass("sun.rmi.transport.tcp.TCPEndpoint");
+                    Field localEndpointsField = cls.getDeclaredField("localEndpoints");
+                    Field ssfField = cls.getDeclaredField("ssf");
+                    localEndpointsField.setAccessible(true);
+                    ssfField.setAccessible(true);
+                    Object localEndpoints = localEndpointsField.get(null);
+                    if (localEndpoints != null) {
+                        Map<Object, Object> map = (Map<Object, Object>) localEndpoints;
+                        for (Iterator<Object> it = map.keySet().iterator(); it.hasNext(); ) {
+                            Object key = it.next();
+                            Object ssf = ssfField.get(key);
+                            if (ssf != null && ssf.getClass().getPackage().getName().equals("org.apache.karaf.management")) {
+                                it.remove();
+                            }
+                        }
+                    }
+                } catch (Exception ignored) {
+                }
+            }
         } finally {
             if (this.objectName != null) {
                 doUnregister(this.objectName);
@@ -403,7 +576,7 @@ public class ConnectorServerFactory {
         private String[] enabledProtocols;
         private String[] enabledCipherSuites;
 
-        public KarafSslRMIServerSocketFactory(SSLServerSocketFactory sssf, boolean clientAuth, String rmiServerHost, 
+        public KarafSslRMIServerSocketFactory(SSLServerSocketFactory sssf, boolean clientAuth, String rmiServerHost,
                                               String[] enabledProtocols,
                                               String[] enabledCipherSuites) {
             this.sssf = sssf;
@@ -766,4 +939,39 @@ public class ConnectorServerFactory {
         this.enabledCipherSuites = enabledCipherSuites;
     }
 
+    /*
+     * Better to use the internal API than re-invent the wheel.
+     */
+    @SuppressWarnings("restriction")
+    private class JmxRegistry extends sun.rmi.registry.RegistryImpl {
+        private final String lookupName;
+
+        JmxRegistry(final int port, final String lookupName) throws RemoteException {
+            super(port);
+            this.lookupName = lookupName;
+        }
+
+        @Override
+        public Remote lookup(String s) throws RemoteException, NotBoundException {
+            return lookupName.equals(s) ? remoteServerStub : null;
+        }
+
+        @Override
+        public void bind(String s, Remote remote) throws RemoteException, AlreadyBoundException, AccessException {
+        }
+
+        @Override
+        public void unbind(String s) throws RemoteException, NotBoundException, AccessException {
+        }
+
+        @Override
+        public void rebind(String s, Remote remote) throws RemoteException, AccessException {
+        }
+
+        @Override
+        public String[] list() throws RemoteException {
+            return new String[] {lookupName};
+        }
+    }
+
 }
diff --git a/management/server/src/main/java/org/apache/karaf/management/RmiRegistryFactory.java b/management/server/src/main/java/org/apache/karaf/management/RmiRegistryFactory.java
deleted file mode 100644
index a08ef20..0000000
--- a/management/server/src/main/java/org/apache/karaf/management/RmiRegistryFactory.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.karaf.management;
-
-import org.osgi.framework.BundleContext;
-
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.UnknownHostException;
-import java.rmi.RemoteException;
-import java.rmi.registry.LocateRegistry;
-import java.rmi.registry.Registry;
-import java.rmi.server.RMIClientSocketFactory;
-import java.rmi.server.RMIServerSocketFactory;
-import java.rmi.server.RMISocketFactory;
-import java.rmi.server.UnicastRemoteObject;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.Map;
-
-public class RmiRegistryFactory {
-
-    private int port = Registry.REGISTRY_PORT;
-    private String host;
-    private Registry registry;
-    private boolean locate;
-    private boolean create = true;
-    private boolean locallyCreated;
-
-    private BundleContext bundleContext;
-    
-    /**
-     * @return the create
-     */
-    public boolean isCreate() {
-        return create;
-    }
-
-    /**
-     * @param create the create to set
-     */
-    public void setCreate(boolean create) {
-        this.create = create;
-    }
-
-    /**
-     * @return the locate
-     */
-    public boolean isLocate() {
-        return locate;
-    }
-
-    /**
-     * @param locate the locate to set
-     */
-    public void setLocate(boolean locate) {
-        this.locate = locate;
-    }
-
-    /**
-     * @return the port
-     */
-    public int getPort() {
-        return port;
-    }
-
-    /**
-     * @param port the port to set
-     */
-    public void setPort(int port) {
-        this.port = port;
-    }
-
-    public String getHost() {
-        return host;
-    }
-
-    public void setHost(String host) {
-        this.host = host;
-    }
-
-    public Object getObject() throws Exception {
-        return registry;
-    }
-
-    public void setBundleContext(BundleContext bundleContext) {
-        this.bundleContext = bundleContext;
-    }
-
-    public void init() throws RemoteException, UnknownHostException {
-        if (registry == null && locate) {
-            try {
-                Registry reg = LocateRegistry.getRegistry(host, getPort());
-                reg.list();
-                registry = reg;
-            } catch (RemoteException e) {
-                // ignore
-            }
-        }
-        if (registry == null && create) {
-            if (host != null && !host.isEmpty()) {
-                RMIClientSocketFactory socketFactory = RMISocketFactory.getDefaultSocketFactory();
-                InetAddress addr = InetAddress.getByName(host);
-                RMIServerSocketFactory serverSocketFactory = new KarafServerSocketFactory(addr, port);
-
-                registry = LocateRegistry.createRegistry(getPort(), socketFactory, serverSocketFactory);
-            } else {
-                registry = LocateRegistry.createRegistry(getPort());
-            }
-            locallyCreated = true;
-        }
-        if (registry != null) {
-            // register the registry as an OSGi service
-            Hashtable<String, Object> props = new Hashtable<>();
-            props.put("port", getPort());
-            props.put("host", getHost());
-            bundleContext.registerService(Registry.class, registry, props);
-        }
-    }
-
-    public void destroy() throws RemoteException {
-        if (registry != null && locallyCreated) {
-            Registry reg = registry;
-            registry = null;
-            UnicastRemoteObject.unexportObject(reg, true);
-
-            // clear TCPEndpointCache
-            try {
-                Class<?> cls = getClass().getClassLoader().loadClass("sun.rmi.transport.tcp.TCPEndpoint");
-                Field localEndpointsField = cls.getDeclaredField("localEndpoints");
-                Field ssfField = cls.getDeclaredField("ssf");
-                localEndpointsField.setAccessible(true);
-                ssfField.setAccessible(true);
-                Object localEndpoints = localEndpointsField.get(null);
-                if (localEndpoints != null) {
-                    Map<Object, Object> map = (Map<Object, Object>) localEndpoints;
-                    for (Iterator<Object> it = map.keySet().iterator(); it.hasNext(); ) {
-                        Object key = it.next();
-                        Object ssf = ssfField.get(key);
-                        if (ssf != null && ssf.getClass().getPackage().getName().equals("org.apache.karaf.management")) {
-                            it.remove();
-                        }
-                    }
-                }
-            } catch (Exception ignored) {
-            }
-        }
-    }
-
-    private static class KarafServerSocketFactory implements RMIServerSocketFactory {
-        private final int port;
-        private final InetAddress addr;
-
-        private KarafServerSocketFactory(InetAddress addr, int port) {
-            this.addr = addr;
-            this.port = port;
-        }
-
-        @Override
-        public ServerSocket createServerSocket(int i) throws IOException {
-            return new ServerSocket(port, 0, addr);
-        }
-    }
-
-}
diff --git a/management/server/src/main/java/org/apache/karaf/management/internal/Activator.java b/management/server/src/main/java/org/apache/karaf/management/internal/Activator.java
index e7ab240..6d34226 100644
--- a/management/server/src/main/java/org/apache/karaf/management/internal/Activator.java
+++ b/management/server/src/main/java/org/apache/karaf/management/internal/Activator.java
@@ -28,7 +28,6 @@ import org.apache.karaf.management.ConnectorServerFactory;
 import org.apache.karaf.management.JaasAuthenticator;
 import org.apache.karaf.management.KarafMBeanServerGuard;
 import org.apache.karaf.management.MBeanServerFactory;
-import org.apache.karaf.management.RmiRegistryFactory;
 import org.apache.karaf.util.tracker.BaseActivator;
 import org.apache.karaf.util.tracker.annotation.Managed;
 import org.apache.karaf.util.tracker.annotation.ProvideService;
@@ -51,13 +50,12 @@ import org.slf4j.LoggerFactory;
 )
 @Managed("org.apache.karaf.management")
 public class Activator extends BaseActivator implements ManagedService {
-    
-    private static final Logger LOG = LoggerFactory.getLogger(Activator.class); 
+
+    private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
 
     private ConnectorServerFactory connectorServerFactory;
-    private RmiRegistryFactory rmiRegistryFactory;
     private MBeanServerFactory mbeanServerFactory;
-    
+
     private ServiceTracker<KeystoreInstance, KeystoreInstance> keystoreInstanceServiceTracker;
 
     private EventAdminLogger eventAdminLogger;
@@ -128,14 +126,6 @@ public class Activator extends BaseActivator implements ManagedService {
         guard.setLogger(eventAdminLogger);
         guard.setConfigAdmin(configurationAdmin);
 
-        rmiRegistryFactory = new RmiRegistryFactory();
-        rmiRegistryFactory.setCreate(createRmiRegistry);
-        rmiRegistryFactory.setLocate(locateRmiRegistry);
-        rmiRegistryFactory.setHost(rmiRegistryHost);
-        rmiRegistryFactory.setPort(rmiRegistryPort);
-        rmiRegistryFactory.setBundleContext(bundleContext);
-        rmiRegistryFactory.init();
-
         mbeanServerFactory = new MBeanServerFactory();
         mbeanServerFactory.setLocateExistingServerIfPossible(locateExistingMBeanServerIfPossible);
         mbeanServerFactory.init();
@@ -147,6 +137,12 @@ public class Activator extends BaseActivator implements ManagedService {
         jaasAuthenticator.setRealm(jmxRealm);
 
         connectorServerFactory = new ConnectorServerFactory();
+        connectorServerFactory.setCreate(createRmiRegistry);
+        connectorServerFactory.setLocate(locateRmiRegistry);
+        connectorServerFactory.setHost(rmiRegistryHost);
+        connectorServerFactory.setPort(rmiRegistryPort);
+        connectorServerFactory.setBundleContext(bundleContext);
+
         connectorServerFactory.setServer(mbeanServer);
         connectorServerFactory.setServiceUrl(serviceUrl);
         connectorServerFactory.setGuard(guard);
@@ -236,14 +232,6 @@ public class Activator extends BaseActivator implements ManagedService {
             }
             mbeanServerFactory = null;
         }
-        if (rmiRegistryFactory != null) {
-            try {
-                rmiRegistryFactory.destroy();
-            } catch (Exception e) {
-                logger.warn("Error destroying RMIRegistryFactory", e);
-            }
-            rmiRegistryFactory = null;
-        }
         if (keystoreInstanceServiceTracker != null) {
             try {
                 keystoreInstanceServiceTracker.close();