JMX in a DMZ understanding RMI settings
An SSH tunnel can allow access to a JMX endpoint that is only exposed to the local machine.
We want to manage a Java web server running in a DMZ using JMX. Here we describe how to set this up, with JMX access from clients over SSH. My last post describes how to set up connect to a port in the DMZ through an SSH tunnel (yes, it’s simple). Once we have that, JMX-managed apps can be configured to allow JMX clients to connect to the source-host of the tunnel.
How are JMX connections established?
- Managed application starts an RMI registry and binds JMX connection details in it under the name
- Client asks the RMI registry in the managed application for JMX connection details
- RMI registry responds with
- Client connects to whatever host:port it was given
You can probably persuade your friendly but stubborn sysadmin to open one (outbound) port through the firewall, but a second will be tricky. A standard JMX config uses two ports:
- RMI registry
- specified by the
- or given explicitly in
- specified by the
- Used to export JMX RMI connection objects
- dynamically allocated when the JMX connection server is created
- served to clients who ask the RMI registry for it
Usually you don’t need to know the dynamic port used here until runtime, but obviously this isn’t going to fly when connecting through a well-secured firewall.
You can specify that the connection server factory should use the same port for these two roles when creating the JMX connection server programmatically in the
JMXServiceURL, for example:
Here the first port is that used for exported JMX RMI connection objects and the second port is where the RMI registry listens. The value of
jmxHost here is ignored always.
There are many addresses that can be configured in various places.
- The system property
java.rmi.server.hostname(Oracle docs). Contrary to some writings, this has nothing to do with where any listeners are established, so this can basically anything. This is the address that clients are given by the RMI registry, to which they attempt to make JMX connections. So this should be the name of the host originating the SSH tunnel.
JMXServiceURL, which is always ignored
JMXServiceURLhas to be resolvable by the managed application. Setting this to be 127.0.0.1, localhost or the machine name all appear to do the same thing, namely to bind to all interfaces on the machine.
- If the JMX/RMIConnectorServer is created and bound into the RMI registry manually, you’ll specify the host in new instance of
JMXServiceURL. In that case the
rmiHostabove is ignored.
Points of sadness
Nowhere with this relatively simple setup can you define the bind address of the RMI registry; it always seems to bind to
0.0.0.0. Still, you can use iptables to prevent access to the port except from that host, and the SSH tunnel will do the rest.
java.rmi.server.hostname property is defined VM-wide; it’s not respected if passed in as the new connector server’s environment. So this might not be appropriate for applications using RMI in other ways. Though if you’re already running an app in a DMZ and using RMI to connect home, either you already know that and have solved these problems, or you’re completely insane.