How to configure JMX to bind to localhost only?
Asked Answered
C

2

7

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.

Cupro answered 12/2, 2016 at 16:14 Comment(2)
maybe java.rmi.server.hostname (-Djava.rmi.server.hostname=127.0.0.1)?Bespeak
Not, the result is the sameCupro
R
4

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.

Rosenda answered 12/2, 2016 at 18:29 Comment(4)
The problem that I do not create JMX I use Tomcat JMX: https://tomcat.apache.org/tomcat-8.0-doc/monitoring.html. So, I guess in this case I can not bind the port to the localhost. Correct?Cupro
I don't think so. In my opinion, you need to pack up the custom code in a JAR and probably add it to the boot classpath along with bootstrap.jar. Start off with something simple and not Tomcat, to remove testing overhead.Rosenda
But I have to use Tomcat JMX :(Cupro
@Michael: You probably do not understand. There is not Tomcat JMX. Tomcat uses the plain JMX implementation provided by the VM. Nothing special. You should give it a try.Rosenda
H
23

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.

Hengelo answered 21/1, 2017 at 22:30 Comment(5)
It's not necessarily just as secure to do the address check as it is to bind to localhost. If you bind to localhost only, then no matter what, an attacker can't talk to you and convince you to do something you shouldn't. If you accept network connections, an attacker might possibly be able to exploit some vulnerability before the remote address check happens. There may be other implementation details which do make it "just as secure" but in principle, it's better to bind to localhost.Conquian
@DanPritts. I don't disagree. However, I'm assuming the JDK developers must have had a good reason to do it the way they did.Hengelo
Sure, they might have had a good reason, like, it was hard to do it the "right" way so they did it this way instead. Or they might not have had a good reason at all, perhaps it was done many years ago when people didn't think as hard about security. Bottom line, it's not "just as secure" unless there are other mitigating factors. The addition of the feature to bind to a specific interface suggests there weren't, although i don't know that for a fact.Conquian
Even though local.only=true is configured, I'm able to create a connection from another host successfully: tcp6 0 0 192.168.100.20:7091 192.168.100.34:54737 ESTABLISHED .20 is hosting RMX, .34 is my client. That shouldn't be possible, shouldn't it?Seeing
Additionally, with setting -Dcom.sun.management.jmxremote.host=localhost I really only see one LISTENING on 127.0.0.1 and I'm not able to connect with my client like before. tcp6 0 0 127.0.0.1:7091 :::* LISTEN So it seems things have changed in the meanwhile, am using openjdk version "1.8.0_191" on UB 16.04 LTS Server.Seeing
R
4

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.

Rosenda answered 12/2, 2016 at 18:29 Comment(4)
The problem that I do not create JMX I use Tomcat JMX: https://tomcat.apache.org/tomcat-8.0-doc/monitoring.html. So, I guess in this case I can not bind the port to the localhost. Correct?Cupro
I don't think so. In my opinion, you need to pack up the custom code in a JAR and probably add it to the boot classpath along with bootstrap.jar. Start off with something simple and not Tomcat, to remove testing overhead.Rosenda
But I have to use Tomcat JMX :(Cupro
@Michael: You probably do not understand. There is not Tomcat JMX. Tomcat uses the plain JMX implementation provided by the VM. Nothing special. You should give it a try.Rosenda

© 2022 - 2024 — McMap. All rights reserved.