TIPS
本文基于JDK 11编写,理论支持JDK 7及更高版本
本文探讨如何基于JMX实现连接远程服务器上的进程。
匿名访问
在远程服务器上操作:
一、启动远程应用
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通信端口是随机的。可使用如下命令查询指定进程使用的端口。
# 使用如下命令,查询该进程使用的端口
➜ netstat -antp|grep 50194
tcp6 0 0 :::8080 :::* LISTEN 50194/java
tcp6 0 0 :::1240 :::* LISTEN 50194/java
tcp6 0 0 :::1232 :::* LISTEN 50194/java
tcp6 0 0 :::51992 :::* LISTEN 50194/java
tcp6 0 0 116.255.183.43:37206 116.255.183.43:27017 ESTABLISHED 50194/java
参考如下步骤,配置防火墙规则,开放相应端口。当然,如果能接受关闭防火墙带来的风险,也可直接关闭掉防火墙。
# 1. 使用如下命令,获得想要查看的应用的进程号
➜ jps
50513 Jps
50194 backend-0.0.1-SNAPSHOT.jar
# 3. 使用如下命令,开放指定端口
firewall-cmd --zone=public --add-port=1232/tcp --permanent
firewall-cmd --zone=public --add-port=1240/tcp --permanent
....
# 4. 重载防火墙规则
firewall-cmd --reload
# 5. 查看当前开放的端口,确认端口已成功开放
firewall-cmd --zone=public --list-ports
三、远程连接,以VisualVM为例
本机启动VisualVM,配置如下:
- File - Add JMX Connection,填入类似如图的信息:
开启认证
上面,应用在启动的时候,使用com.sun.management.jmxremote.authenticate=false,关闭了JMX的登录认证。下面来探讨如何使用登录认证连接JMX。
可按如下步骤操作:
一、准备账号文件,命名为jmxremote.access,内容如下
admin readwrite
其中:
- admin是账号,多个账号换行继续写即可。
- readwrite指权限,readwrite表示可读可写;readonly表示只读。
二、准备密码文件,命名为jmxremote.password,内容如下
admin admin123
其中,admin是账号,admin123是密码,多个账号换行继续写即可。
三、设置jmxremote.access、jmxremote.password的文件权限
chmod 600 jmxremote.access
chmod 600 jmxremote.password
四、启动应用
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
开启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证书。可通过如下命令查看。
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
下面所有命令中,以<>包裹的都是变量,需要替换成你希望的值
生成visualvm的keystore
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字母>"
示例:
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 文件,然后重新执行该命令即可。 导出visualvm的cert
keytool -exportcert \n -alias visualvm \n -storetype pkcs12 \n -keystore visualvm.keystore \n -file visualvm.cer \n -storepass <visualvm keystore的密码>
示例:
keytool -exportcert \n -alias visualvm \n -storetype pkcs12 \n -keystore visualvm.keystore \n -file visualvm.cer \n -storepass visualvm-key
把visualvm的cert导入到java-app的truststore里,实际上就是生成了一个truststore
keytool -importcert \n -alias visualvm \n -file visualvm.cer \n -keystore java-app.truststore \n -storepass <java-app truststore的密码> \n -noprompt
示例:
keytool -importcert \n -alias visualvm \n -file visualvm.cer \n -keystore java-app.truststore \n -storepass java-app-trust \n -noprompt
- 生成java-app的keystore
示例: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字母>"
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"
- 导出java-app的cert
示例:keytool -exportcert \n -alias java-app \n -storetype pkcs12 \n -keystore java-app.keystore \n -file java-app.cer \n -storepass <java-app keystore的密码>
keytool -exportcert \n -alias java-app \n -storetype pkcs12 \n -keystore java-app.keystore \n -file java-app.cer \n -storepass java-app-key
- 把java-app的cert导入到visualvm的truststore里
示例:keytool -importcert \n -alias java-app \n -file java-app.cer \n -keystore visualvm.truststore \n -storepass <visualvm truststore的密码> \n -noprompt
最终可获得如下几个文件: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应用
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
示例:
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
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的密码>
示例:
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。因此,只需将上面的命令改为如下即可:
/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
其他操作系统类似,找到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