Michael
Michael

Reputation: 10319

How to configure JMX to bind to localhost only?

I run Tomcat8 using JDK8 on Centos6. I enable JMX using the following options:

CATALINA_OPTS="${CATALINA_OPTS} -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9123 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.local.only=true"

Unfortunately, when I check what ports are opened I discover that these ports listen to all IP:

netstat -plunt | grep java
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp        0      0 :::60555                            :::*                LISTEN      22752/java
tcp        0      0 ::ffff:127.0.0.1:8080               :::*                LISTEN      22752/java
tcp        0      0 :::9123                             :::*                LISTEN      22752/java
tcp        0      0 :::40867                            :::*                LISTEN      22752/java

I suppose that if I configure -Dcom.sun.management.jmxremote.local.only=true all ports should be bind to localhost only (::ffff:127.0.0.1 will appear before all ports).

How to configure JMX to bind to localhost only?

Added

I do not create JMX I use Tomcat JMX: https://tomcat.apache.org/tomcat-8.0-doc/monitoring.html.

Upvotes: 7

Views: 20898

Answers (2)

peterh
peterh

Reputation: 19235

What you ask for is unnecessary.

com.sun.management.jmxremote.local.only=true (which by the way is already the default) means it will only accept connections from localhost. It doesn't mean it will only bind to the loopback interface as you assume. Not accepting connections from something not on the local host is just another way of doing it. From sun.management.jmxremote.LocalRMIServerSocketFactory you can see it is being done like this:

// Walk through the network interfaces to see
// if any of them matches the client's address.
// If true, then the client's address is local.
while (nis.hasMoreElements()) {
    NetworkInterface ni = nis.nextElement();
    Enumeration<InetAddress> addrs = ni.getInetAddresses();
    while (addrs.hasMoreElements()) {
        InetAddress localAddr = addrs.nextElement();
        if (localAddr.equals(remoteAddr)) {
            return socket;
        }
    }
}

Why it was done like this rather than binding to loopback, I don't know. But I believe it is just as secure. (or maybe not?)

But if you really want to, then since Java 8u102 and Java 7u131 system property com.sun.management.jmxremote.host binds the underlying RMI registry to the selected network interface. The value can be any string which is accepted by InetAddress.getByName(String).

Example:

-Dcom.sun.management.jmxremote.host=localhost

see: JDK-6425769 for more information.

Links: Java 8u102 Release Notes

What the docs doesn't mention anywhere is that even when setting com.sun.management.jmxremote.host you'll still see one JMX port which is bound to all network interfaces. This is because if com.sun.management.jmxremote.local.only=true then an instance of sun.management.jmxremote.LocalRMIServerSocketFactory will be started and that one doesn't allow customization, i.e. it doesn't respect com.sun.management.jmxremote.host property. If that is a bug, an oversight in the JDK-6425769 implementation or intentional, I do not know.

Upvotes: 23

Michael-O
Michael-O

Reputation: 18405

As far as I understand this answer and read Oracle's docs about it, there seems to be no way to configure it without coding. This says in the chapter "Connector server attributes":

When using the default JRMP transport, RMI socket factories can be specified using the attributes jmx.remote.rmi.client.socket.factory and jmx.remote.rmi.server.socket.factory in the environment given to the RMIConnectorServer constructor. The values of these attributes must be of type RMIClientSocketFactory and RMIServerSocketFactory, respectively. These factories are used when creating the RMI objects associated with the connector.

The only option I see is to implement a custom factory like here and pass the classname to the property along with the JAR/class in classpath.

Correct me if I am wrong.

Upvotes: 4

Related Questions