java -Dcom.sun.management.jmxremote=true \ -Dcom.sun.management.jmxremote.port=31419 \ -Dcom.sun.management.jmxremote.ssl=false \ -Dcom.sun.management.jmxremote.authenticate=false \ Tomcat
Securing remote JMX
2016-09-01 java
Like it or not, JMX is one the main tools for JVM monitoring. If you are using Tomcat, Kafka or Cassandra you’ll have to setup JMX tools to monitor them.
But JMX has several drawbacks:
-
It’s based on the obsolete RMI protocol
-
It can trigger harmful functions like garbage collection
-
It can be used for nasty exploits like invoking arbitrary code
So securing JMX is not an option.
Going remote
By default, JMX is only locally accessible and secure: It can be accessed through Unix sockets. This means you need to have access to the machine and run JMX tools with the same user as your application. It’s usually enough for development but not for production.
To enable remote JMX, the documentations tells you turn on some JVM flags:
The JVM will starting listening on 0.0.0.0:31419 for JMX requests. Anybody will be able to plug any JMX tool (JConsole, JVisualVM, Mission Control…) from a remote machine.
If you have a firewall (IPTable or the like) to protect your server, and opened the 31419 TCP port, or connecting through a tunnel, the JMX tools may fail to connect to the server. At first, the JMX client connects to port 31419, but gets redirected to a randomly chosen port (RMI WTF!). To prevent this behaviour and force the JVM to use a single port:
java -Dcom.sun.management.jmxremote=true \ -Dcom.sun.management.jmxremote.port=31419 \ -Dcom.sun.management.jmxremote.rmi.port=31419 \ -Dcom.sun.management.jmxremote.ssl=false \ -Dcom.sun.management.jmxremote.authenticate=false \ Tomcat
File based authentication
This configuration is nice for debugging but not secure at all. Let’s add authentication on this JMX connection.
First create a password file, similar to /etc/password, containing login/password pairs:
admin adminpassword
user userpassword
Then create an access file, similar to /etc/groups, containing login/group pairs:
admin readwrite (1)
user readonly (2)
1 | admin has read/write access |
2 | user has read-only access |
These two files should have limited access rights.
Finally, tell the JVM to use these files to authenticate users:
java -Dcom.sun.management.jmxremote=true \ -Dcom.sun.management.jmxremote.port=31419 \ -Dcom.sun.management.jmxremote.rmi.port=31419 \ -Dcom.sun.management.jmxremote.ssl=false \ -Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password \ -Dcom.sun.management.jmxremote.access.file=/path/to/jmxremote.access \ Tomcat
Using SSL
With this configuration, JVM tools have to provide a login and password to access to MBeans. But, the password is sent over the wire without encryption. Let’s connect to JMX though SSL.
You may already known that, in the Java land, private keys and trusted certificates are stored in wallets known as keystores, and using the JKS file format. A tool named keytool provided with the JDK is used to import/export keys and certs in the keystore.
Once the keystore (containing private key matching the server name along with certificate chain) and the truststore (containing trusted certificates) are built, just reference them:
java -Dcom.sun.management.jmxremote=true \ -Dcom.sun.management.jmxremote.port=31419 \ -Dcom.sun.management.jmxremote.rmi.port=31419 \ -Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password \ -Dcom.sun.management.jmxremote.access.file=/path/to/jmxremote.access \ -Dcom.sun.management.jmxremote.registry.ssl=true \ -Djavax.net.ssl.keyStore=/path/to/keystore.jks \ -Djavax.net.ssl.keyStorePassword=keystore_password \ -Djavax.net.ssl.trustStore=/path/to/truststore.jks \ -Djavax.net.ssl.trustStorePassword=truststore_password \ Tomcat
To connect, the JMX tools using SSL, you’ll have to provide the trusted certificates. For instance, to start the JConsole:
jconsole \ -J-Djavax.net.ssl.trustStore=/path/to/truststore.jks \ -J-Djavax.net.ssl.trustStorePassword=truststore_password
JMX configuration file
Again, this configuration has problem:
-
Having passwords on the command line is not a good option, because it’s easy to use
ps
command to grab them. -
Having some many options on the command like is not elegant
Let’s place all these options in a dedicated file:
com.sun.management.jmxremote=true com.sun.management.jmxremote.port=31419 com.sun.management.jmxremote.rmi.port=31419 com.sun.management.jmxremote.password.file=/path/to/jmxremote.password com.sun.management.jmxremote.access.file=/path/to/jmxremote.access com.sun.management.jmxremote.registry.ssl=true com.sun.management.jmxremote.ssl.config.file=/path/to/jmxremote.properties (1) javax.net.ssl.keyStore=/path/to/keystore.jks javax.net.ssl.keyStorePassword=keystore_password javax.net.ssl.trustStore=/path/to/truststore.jks javax.net.ssl.trustStorePassword=truststore_password
1 | Path to file containing javax.net.ssl.* properties |
And now, the command line sums up to
java -Dcom.sun.management.config.file=/path/to/jmxremote.properties \ Tomcat
Such a JMX configuration file already exists in your JRE, it’s named JRE/lib/management/management.properties
.
Some pointers
-
JTips in French
Other posts
- 2020-11-28 Build your own CA with Ansible
- 2020-01-16 Retrieving Kafka Lag
- 2020-01-10 Home temperature monitoring
- 2019-12-10 Kafka connect plugin install
- 2019-07-03 Kafka integration tests