TIPS
本文基于JDK 11编写,理论支持JDK 7及更高版本
本文探讨如何基于JMX实现连接远程服务器上的进程。

匿名访问

在远程服务器上操作:
一、启动远程应用

  1. java -Djava.rmi.server.hostname=www.itmuch.com \n -Dcom.sun.management.jmxremote.port=1232 \n -Dcom.sun.management.jmxremote.rmi.port=1240 \n -Dcom.sun.management.jmxremote.authenticate=false \n -Dcom.sun.management.jmxremote.ssl=false \n -jar xxx.jar

其中:

  • java.rmi.server.hostname:指定主机名,可填IP、主机名或域名
  • com.sun.management.jmxremote.port:指定JMX监听端口,默认1099
  • com.sun.management.jmxremote.rmi.port:指定RMI的通信端口,默认随机分配
  • com.sun.management.jmxremote.authenticate:是否启用认证访问,默认true
  • com.sun.management.jmxremote.ssl:是否启用ssl,默认true

二、防火墙设置
JMX Server除使用指定的监听端口号外,还需用使用RMI通信,因此,需将JMX监听端口、RMI通信端口都开放。
TIPS
如果未设置com.sun.management.jmxremote.rmi.port,那么RMI通信端口是随机的。可使用如下命令查询指定进程使用的端口。

  1. # 使用如下命令,查询该进程使用的端口
  2. netstat -antp|grep 50194
  3. tcp6 0 0 :::8080 :::* LISTEN 50194/java
  4. tcp6 0 0 :::1240 :::* LISTEN 50194/java
  5. tcp6 0 0 :::1232 :::* LISTEN 50194/java
  6. tcp6 0 0 :::51992 :::* LISTEN 50194/java
  7. tcp6 0 0 116.255.183.43:37206 116.255.183.43:27017 ESTABLISHED 50194/java

参考如下步骤,配置防火墙规则,开放相应端口。当然,如果能接受关闭防火墙带来的风险,也可直接关闭掉防火墙。

  1. # 1. 使用如下命令,获得想要查看的应用的进程号
  2. jps
  3. 50513 Jps
  4. 50194 backend-0.0.1-SNAPSHOT.jar
  5. # 3. 使用如下命令,开放指定端口
  6. firewall-cmd --zone=public --add-port=1232/tcp --permanent
  7. firewall-cmd --zone=public --add-port=1240/tcp --permanent
  8. ....
  9. # 4. 重载防火墙规则
  10. firewall-cmd --reload
  11. # 5. 查看当前开放的端口,确认端口已成功开放
  12. firewall-cmd --zone=public --list-ports

三、远程连接,以VisualVM为例
本机启动VisualVM,配置如下:

  • File - Add JMX Connection,填入类似如图的信息:

基于JMX实现远程连接 - 图1
即可连接成功。

开启认证

上面,应用在启动的时候,使用com.sun.management.jmxremote.authenticate=false,关闭了JMX的登录认证。下面来探讨如何使用登录认证连接JMX。
可按如下步骤操作:
一、准备账号文件,命名为jmxremote.access,内容如下

  1. admin readwrite

其中:

  • admin是账号,多个账号换行继续写即可。
  • readwrite指权限,readwrite表示可读可写;readonly表示只读。

二、准备密码文件,命名为jmxremote.password,内容如下

  1. admin admin123

其中,admin是账号,admin123是密码,多个账号换行继续写即可。
三、设置jmxremote.access、jmxremote.password的文件权限

  1. chmod 600 jmxremote.access
  2. chmod 600 jmxremote.password

四、启动应用

  1. java -Djava.rmi.server.hostname=www.itmuch.com \n -Dcom.sun.management.jmxremote.port=1232 \n -Dcom.sun.management.jmxremote.rmi.port=1240 \n -Dcom.sun.management.jmxremote.authenticate=true \n -Dcom.sun.management.jmxremote.access.file=./jmxremote.access \n -Dcom.sun.management.jmxremote.password.file=./jmxremote.password \n -Dcom.sun.management.jmxremote.ssl=false \n -jar xxx.jar

五、连接时,指定上面的账号密码即可:
基于JMX实现远程连接 - 图2

开启SSL

上面使用了com.sun.management.jmxremote.ssl=false关闭了SSL,下面来探讨如何开启SSL,从而提升通信的安全性。

前导知识

先了解一下Java客户端程序在创建SSL连接的一些相关的事情:

  • Java client程序在做SSL连接的时候,会拉取server的证书,利用truststore去验证这个证书,如果truststore不存在、证书过期抑或证书不是由可信CA签发的,就意味着服务端不被信任,不允许连接。
  • 如果在程序启动时没有特别指定使用哪个truststore,就会使用$JAVA_HOME/jre/lib/security/cacerts 。也可通过System Property javax.net.ssl.trustStore 指定truststore,指定了truststore时,会使用指定的truststore + cacerts来验证。
  • cacerts存放了JDK信任的CA证书(含有public key),它里面预先已经存放了已知的权威CA证书。可通过如下命令查看。

    1. keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts

    以上过程被称为server authentication,也就是说client验证server是否可信,server authentication是最常见的,https就是这种模式。
    不过在用SSL连接JMX的时候,不仅要做server authentication,还要做client authentication,即server验证client是否可信。原理和上面一样,只不过变成server用自己的truststore里验证client的证书是否可信。
    理解这一点之后,就可以制作keystore以及truststore了。

    制作keystore和truststore

    下面,以visualvm为例,讲解如何实现基于SSL的连接。需要制作visualvm(本机)和java app(server端)的keystore和truststore。

  • 生成visualvm的keystore,导出cert,把cert导入到java-app的truststore里

  • 生成java-app的keystore,导出cert,把cert导入到visualvm的truststore里

TIPS
这里只是以visualvm为例讲解,JConsole等过程也是一样的。
具体命令如下:
TIPS
下面所有命令中,以<>包裹的都是变量,需要替换成你希望的值

  1. 生成visualvm的keystore

    1. keytool -genkeypair \n -alias visualvm \n -keyalg RSA \n -validity 365 \n -storetype pkcs12 \n -keystore visualvm.keystore \n -storepass <visualvm keystore的密码> \n -keypass <同visualvm keystore的密码> \n -dname "CN=<姓名>, OU=<组织下属单位>, O=<组织名称>, L=<城市>, S=<省份>, C=<国家2字母>"

    示例:

    1. keytool -genkeypair \n -alias visualvm \n -keyalg RSA \n -validity 365 \n -storetype pkcs12 \n -keystore visualvm.keystore \n -storepass visualvm-key \n -keypass visualvm-key \n -dname "CN=damu, OU=itmuch, O=itmuch.com, L=NanJing, S=JiangSu, C=CN"

    如果出现类似 keytool 错误: java.lang.Exception: 未生成密钥对, 别名 已经存在 的异常,只需删除 visualvm.keystore 文件,然后重新执行该命令即可。

  2. 导出visualvm的cert

    1. keytool -exportcert \n -alias visualvm \n -storetype pkcs12 \n -keystore visualvm.keystore \n -file visualvm.cer \n -storepass <visualvm keystore的密码>

    示例:

    1. keytool -exportcert \n -alias visualvm \n -storetype pkcs12 \n -keystore visualvm.keystore \n -file visualvm.cer \n -storepass visualvm-key
  3. 把visualvm的cert导入到java-app的truststore里,实际上就是生成了一个truststore

    1. keytool -importcert \n -alias visualvm \n -file visualvm.cer \n -keystore java-app.truststore \n -storepass <java-app truststore的密码> \n -noprompt

    示例:

    1. keytool -importcert \n -alias visualvm \n -file visualvm.cer \n -keystore java-app.truststore \n -storepass java-app-trust \n -noprompt
  1. 生成java-app的keystore
    1. keytool -genkeypair \n -alias java-app \n -keyalg RSA \n -validity 365 \n -storetype pkcs12 \n -keystore java-app.keystore \n -storepass <java-app keystore的密码> \n -keypass <同java-app keystore的密码> \n -dname "CN=<姓名>, OU=<组织下属单位>, O=<组织名称>, L=<城市>, S=<省份>, C=<国家2字母>"
    示例:
    1. keytool -genkeypair \n -alias java-app \n -keyalg RSA \n -validity 365 \n -storetype pkcs12 \n -keystore java-app.keystore \n -storepass java-app-key \n -keypass java-app-key \n -dname "CN=damu, OU=itmuch, O=itmuch.com, L=NanJing, S=JiangSu, C=CN"
  1. 导出java-app的cert
    1. keytool -exportcert \n -alias java-app \n -storetype pkcs12 \n -keystore java-app.keystore \n -file java-app.cer \n -storepass <java-app keystore的密码>
    示例:
    1. keytool -exportcert \n -alias java-app \n -storetype pkcs12 \n -keystore java-app.keystore \n -file java-app.cer \n -storepass java-app-key
  1. 把java-app的cert导入到visualvm的truststore里
    1. keytool -importcert \n -alias java-app \n -file java-app.cer \n -keystore visualvm.truststore \n -storepass <visualvm truststore的密码> \n -noprompt
    示例:
    1. keytool -importcert \n -alias java-app \n -file java-app.cer \n -keystore visualvm.truststore \n -storepass visualvm-trust \n -noprompt
    最终可获得如下几个文件:
  • visualvm.keystore
  • visualvm.truststore
  • visualvm.cer
  • java-app.keystore
  • java-app.truststore
  • java-app.cer

    启动远程Java应用

    1. java -Djava.rmi.server.hostname=www.itmuch.com \n -Dcom.sun.management.jmxremote.port=1232 \n -Dcom.sun.management.jmxremote.rmi.port=1240 \n -Dcom.sun.management.jmxremote.authenticate=false \n -Djavax.net.ssl.keyStore=<path to java-app.keystore> \n -Djavax.net.ssl.keyStorePassword=<java-app.keystore的密码> \n -Djavax.net.ssl.trustStore=<path to java-app.truststore> \n -Djavax.net.ssl.trustStorePassword=<java-app.truststore的密码> \n -jar xxx.jar

    示例:

    1. java -Djava.rmi.server.hostname=www.itmuch.com \n -Dcom.sun.management.jmxremote.port=1232 \n -Dcom.sun.management.jmxremote.rmi.port=1240 \n -Dcom.sun.management.jmxremote.authenticate=false \n -Djavax.net.ssl.keyStore=/root/test/java-app.keystore \n -Djavax.net.ssl.keyStorePassword=java-app-key \n -Djavax.net.ssl.trustStore=/root/test/java-app.truststore \n -Djavax.net.ssl.trustStorePassword=java-app-trust \n -jar xxx.jar

    TIPS
    这里简单起见,没有开启用户认证,你也可以同时开启用户认证与SSL。

    启动VisualVM

    1. jvisualvm -J-Djavax.net.ssl.keyStore=<path to visualvm.keystore> \n -J-Djavax.net.ssl.keyStorePassword=<visualvm.keystore的密码> \n -J-Djavax.net.ssl.trustStore=<path to visualvm.truststore> \n -J-Djavax.net.ssl.trustStorePassword=<visualvm.truststore的密码>

    示例:

    1. jvisualvm -J-Djavax.net.ssl.keyStore=/Users/reno/Documents/visualvm.keystore \n -J-Djavax.net.ssl.keyStorePassword=visualvm-key \n -J-Djavax.net.ssl.trustStore=/Users/reno/Documents/visualvm.truststore \n -J-Djavax.net.ssl.trustStorePassword=visualvm-trust

    TIPS

  • 对于JDK 8,JDK内置了VisualVM,使用如上命令即可;

  • 对于JDK 11,VisualVM是独立分发的,根本没有jvisualvm这样的命令。此时,应找到visualvm的启动文件。对于macOS,启动文件是/Applications/VisualVM.app/Contents/MacOS/visualvm。因此,只需将上面的命令改为如下即可:

    1. /Applications/VisualVM.app/Contents/MacOS/visualvm \n> -J-Djavax.net.ssl.keyStore=/Users/reno/Documents/visualvm.keystore \n> -J-Djavax.net.ssl.keyStorePassword=visualvm-key \n> -J-Djavax.net.ssl.trustStore=/Users/reno/Documents/visualvm.truststore \n> -J-Djavax.net.ssl.trustStorePassword=visualvm-trust
    1. 其他操作系统类似,找到VisualVM的启动文件,并在其后跟上参数即可。

    参考文档

  • VisualVM官方文档

  • VisualVM: Monitoring Remote JVM Over SSH (JMX Or Not)
  • 利用VisualVm和JMX远程监控Java进程
  • Java_jvisualvm使用JMX连接远程机器(实践)
  • VisualVM通过密码JMX远程连接JVM
  • Monitoring and Management Using JMX Technology