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.

peterh
  • 18,404
  • 12
  • 87
  • 115
Michael
  • 10,063
  • 18
  • 65
  • 104

2 Answers2

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.

peterh
  • 18,404
  • 12
  • 87
  • 115
  • 2
    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. – Dan Pritts Apr 10 '17 at 20:55
  • @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. – peterh Apr 11 '17 at 22:44
  • 3
    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. – Dan Pritts Apr 12 '17 at 03:08
  • 2
    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? – Thorsten Schöning Apr 29 '19 at 08:59
  • 2
    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. – Thorsten Schöning Apr 29 '19 at 09:03
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.

Community
  • 1
  • 1
Michael-O
  • 18,123
  • 6
  • 55
  • 121
  • 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? – Michael Feb 12 '16 at 18:38
  • 1
    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. – Michael-O Feb 12 '16 at 18:50
  • But I have to use Tomcat JMX :( – Michael Feb 12 '16 at 18:53
  • 3
    @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. – Michael-O Feb 12 '16 at 19:41