ARTEMIS-2169 allow config of JMX RMI registry port
Previously the port was always random. This caused problems with remote JMX connections that needed to overcome firewalls. As of this patch it's possible to make the RMI port static and whitelist it in the firewall settings.
This commit is contained in:
parent
b3e767caec
commit
6d0641b438
|
@ -96,6 +96,9 @@ public class ManagementFactory {
|
||||||
if (jmxConnector.getConnectorHost() != null) {
|
if (jmxConnector.getConnectorHost() != null) {
|
||||||
jmxConnectorConfiguration.setConnectorHost(jmxConnector.getConnectorHost());
|
jmxConnectorConfiguration.setConnectorHost(jmxConnector.getConnectorHost());
|
||||||
}
|
}
|
||||||
|
if (jmxConnector.getRmiRegistryPort() != null) {
|
||||||
|
jmxConnectorConfiguration.setRmiRegistryPort(jmxConnector.getRmiRegistryPort());
|
||||||
|
}
|
||||||
if (jmxConnector.getJmxRealm() != null) {
|
if (jmxConnector.getJmxRealm() != null) {
|
||||||
jmxConnectorConfiguration.setJmxRealm(jmxConnector.getJmxRealm());
|
jmxConnectorConfiguration.setJmxRealm(jmxConnector.getJmxRealm());
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,9 @@ public class JMXConnectorDTO {
|
||||||
@XmlAttribute (name = "connector-port", required = true)
|
@XmlAttribute (name = "connector-port", required = true)
|
||||||
Integer connectorPort;
|
Integer connectorPort;
|
||||||
|
|
||||||
|
@XmlAttribute (name = "rmi-registry-port")
|
||||||
|
Integer rmiRegistryPort;
|
||||||
|
|
||||||
@XmlAttribute (name = "jmx-realm")
|
@XmlAttribute (name = "jmx-realm")
|
||||||
String jmxRealm;
|
String jmxRealm;
|
||||||
|
|
||||||
|
@ -75,6 +78,10 @@ public class JMXConnectorDTO {
|
||||||
return connectorPort;
|
return connectorPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getRmiRegistryPort() {
|
||||||
|
return rmiRegistryPort;
|
||||||
|
}
|
||||||
|
|
||||||
public String getJmxRealm() {
|
public String getJmxRealm() {
|
||||||
return jmxRealm;
|
return jmxRealm;
|
||||||
}
|
}
|
||||||
|
|
|
@ -423,7 +423,13 @@ You can also configure the connector using the following:
|
||||||
- `connector-port`
|
- `connector-port`
|
||||||
|
|
||||||
The port to expose the agent on.
|
The port to expose the agent on.
|
||||||
|
|
||||||
|
- `rmi-registry-port`
|
||||||
|
|
||||||
|
The port that the RMI registry binds to. If not set, the port is
|
||||||
|
always random. Set to avoid problems with remote JMX connections
|
||||||
|
tunnelled through firewall.
|
||||||
|
|
||||||
- `jmx-realm`
|
- `jmx-realm`
|
||||||
|
|
||||||
The jmx realm to use for authentication, defaults to `activemq` to match the
|
The jmx realm to use for authentication, defaults to `activemq` to match the
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* <br>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <br>
|
||||||
|
* 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.activemq.artemis.tests.integration.jmx;
|
||||||
|
|
||||||
|
import com.sun.jmx.remote.internal.ProxyRef;
|
||||||
|
import org.apache.activemq.artemis.cli.Artemis;
|
||||||
|
import org.apache.activemq.artemis.cli.commands.Run;
|
||||||
|
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import sun.rmi.server.UnicastRef;
|
||||||
|
import sun.rmi.transport.LiveRef;
|
||||||
|
|
||||||
|
import javax.management.remote.JMXConnector;
|
||||||
|
import javax.management.remote.JMXConnectorFactory;
|
||||||
|
import javax.management.remote.JMXServiceURL;
|
||||||
|
import javax.management.remote.rmi.RMIConnection;
|
||||||
|
import javax.management.remote.rmi.RMIConnector;
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.rmi.server.RemoteObject;
|
||||||
|
import java.rmi.server.RemoteRef;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test checks JMX connection to Artemis with both necessary ports set up so that it's easier to tunnel through
|
||||||
|
* firewalls.
|
||||||
|
*/
|
||||||
|
public class JmxConnectionTest extends ActiveMQTestBase {
|
||||||
|
|
||||||
|
private static final String MANAGEMENT_XML = "/jmx-test-management.xml";
|
||||||
|
|
||||||
|
// Make sure these values are always the same as in the MANAGEMENT_XML configuration file
|
||||||
|
private static final String JMX_SERVER_HOSTNAME = "127.0.0.1";
|
||||||
|
private static final int JMX_SERVER_PORT = 10099;
|
||||||
|
private static final int RMI_REGISTRY_PORT = 10098;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
/* This needs to be disabled because otherwise there would be a lot of complains about Artemis' own running threads
|
||||||
|
* and I suppose that this kind of leaks is most likely tested somewhere else.
|
||||||
|
*/
|
||||||
|
disableCheckThread();
|
||||||
|
|
||||||
|
// Artemis instance dir
|
||||||
|
File instanceDir = new File(temporaryFolder.getRoot(), "instance");
|
||||||
|
|
||||||
|
// Create new Artemis instance
|
||||||
|
Run.setEmbedded(true);
|
||||||
|
Artemis.main("create", instanceDir.getAbsolutePath(), "--silent", "--no-fsync", "--no-autotune",
|
||||||
|
"--no-web", "--no-amqp-acceptor", "--no-mqtt-acceptor", "--no-stomp-acceptor", "--no-hornetq-acceptor");
|
||||||
|
|
||||||
|
// Configure (this is THE subject of testing)
|
||||||
|
File managementConfigFile = new File(instanceDir, "etc/management.xml");
|
||||||
|
Files.copy(getClass().getResourceAsStream(MANAGEMENT_XML), managementConfigFile.toPath(),
|
||||||
|
StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
|
// Point the server to the instance directory
|
||||||
|
System.setProperty("artemis.instance", instanceDir.getAbsolutePath());
|
||||||
|
|
||||||
|
// Without this, the RMI server would bind to the default interface IP (the user's local IP mostly)
|
||||||
|
System.setProperty("java.rmi.server.hostname", JMX_SERVER_HOSTNAME);
|
||||||
|
|
||||||
|
// Enable guest login module (dunno why this isn't automatic)
|
||||||
|
System.setProperty("java.security.auth.login.config", instanceDir.getAbsolutePath() + "/etc/login.config");
|
||||||
|
|
||||||
|
// Run Artemis server
|
||||||
|
Artemis.internalExecute("run");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJmxConnection() throws Exception {
|
||||||
|
|
||||||
|
// I don't specify both ports here manually on purpose. See actual RMI registry connection port extraction below.
|
||||||
|
String urlString = "service:jmx:rmi:///jndi/rmi://" + JMX_SERVER_HOSTNAME + ":" + JMX_SERVER_PORT + "/jmxrmi";
|
||||||
|
|
||||||
|
JMXServiceURL url = new JMXServiceURL(urlString);
|
||||||
|
JMXConnector jmxConnector;
|
||||||
|
|
||||||
|
try {
|
||||||
|
jmxConnector = JMXConnectorFactory.connect(url);
|
||||||
|
logAndSystemOut("Successfully connected to: " + urlString);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logAndSystemOut("JMX connection failed: " + urlString, e);
|
||||||
|
Assert.fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
/* Now I need to extract the RMI registry port to make sure it's equal to the configured one. It's gonna be
|
||||||
|
* messy because I have to use reflection to reach the information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Assert.assertTrue(jmxConnector instanceof RMIConnector);
|
||||||
|
|
||||||
|
// 1. RMIConnector::connection is expected to be RMIConnectionImpl_Stub
|
||||||
|
Field connectionField = RMIConnector.class.getDeclaredField("connection");
|
||||||
|
connectionField.setAccessible(true);
|
||||||
|
RMIConnection rmiConnection = (RMIConnection) connectionField.get(jmxConnector);
|
||||||
|
|
||||||
|
// 2. RMIConnectionImpl_Stub extends RemoteStub which extends RemoteObject
|
||||||
|
Assert.assertTrue(rmiConnection instanceof RemoteObject);
|
||||||
|
RemoteObject remoteObject = (RemoteObject) rmiConnection;
|
||||||
|
|
||||||
|
// 3. RemoteObject::getRef is hereby expected to return ProxyRef
|
||||||
|
RemoteRef remoteRef = remoteObject.getRef();
|
||||||
|
Assert.assertTrue(remoteRef instanceof ProxyRef);
|
||||||
|
ProxyRef proxyRef = (ProxyRef) remoteRef;
|
||||||
|
|
||||||
|
// 4. ProxyRef::ref is expected to contain UnicastRef (UnicastRef2 resp.)
|
||||||
|
Field refField = ProxyRef.class.getDeclaredField("ref");
|
||||||
|
refField.setAccessible(true);
|
||||||
|
remoteRef = (RemoteRef) refField.get(proxyRef);
|
||||||
|
Assert.assertTrue(remoteRef instanceof UnicastRef);
|
||||||
|
|
||||||
|
// 5. UnicastRef::getLiveRef returns LiveRef
|
||||||
|
LiveRef liveRef = ((UnicastRef) remoteRef).getLiveRef();
|
||||||
|
|
||||||
|
// 6. LiveRef::getPort is expected to be the same as the RMI registry port configured via management.xml
|
||||||
|
/* Accidentally, it can happen that even with the RMI registry port unconfigured the randomly selected port
|
||||||
|
* will be the same as expected by the test which will make it succeed. But it's highly unlikely.
|
||||||
|
*/
|
||||||
|
Assert.assertEquals(RMI_REGISTRY_PORT, liveRef.getPort());
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
jmxConnector.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
@Override
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
Artemis.internalExecute("stop");
|
||||||
|
Run.latchRunning.await(5, TimeUnit.SECONDS);
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
<management-context xmlns="http://activemq.org/schema">
|
||||||
|
<connector connector-port="10099" connector-host="127.0.0.1" rmi-registry-port="10098" />
|
||||||
|
<authorisation>
|
||||||
|
<whitelist>
|
||||||
|
<entry domain="hawtio"/>
|
||||||
|
</whitelist>
|
||||||
|
<default-access>
|
||||||
|
<access method="list*" roles="${role}"/>
|
||||||
|
<access method="get*" roles="${role}"/>
|
||||||
|
<access method="is*" roles="${role}"/>
|
||||||
|
<access method="set*" roles="${role}"/>
|
||||||
|
<access method="*" roles="${role}"/>
|
||||||
|
</default-access>
|
||||||
|
<role-access>
|
||||||
|
<match domain="org.apache.activemq.artemis">
|
||||||
|
<access method="list*" roles="${role}"/>
|
||||||
|
<access method="get*" roles="${role}"/>
|
||||||
|
<access method="is*" roles="${role}"/>
|
||||||
|
<access method="set*" roles="${role}"/>
|
||||||
|
<access method="*" roles="${role}"/>
|
||||||
|
</match>
|
||||||
|
<!--example of how to configure a specific object-->
|
||||||
|
<!--<match domain="org.apache.activemq.artemis" key="subcomponent=queues">
|
||||||
|
<access method="list*" roles="view,update,amq"/>
|
||||||
|
<access method="get*" roles="view,update,amq"/>
|
||||||
|
<access method="is*" roles="view,update,amq"/>
|
||||||
|
<access method="set*" roles="update,amq"/>
|
||||||
|
<access method="*" roles="amq"/>
|
||||||
|
</match>-->
|
||||||
|
</role-access>
|
||||||
|
</authorisation>
|
||||||
|
</management-context>
|
Loading…
Reference in New Issue