Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
5 changed files
with
228 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 162 additions & 0 deletions
162
...ts/src/test/java/org/apache/activemq/artemis/tests/integration/jmx/JmxConnectionTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
|
||
} |
49 changes: 49 additions & 0 deletions
49
tests/integration-tests/src/test/resources/jmx-test-management.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |