![使用 SSL 和自签名证书在 Tomcat 7 下使 JMX 运行](https://linux22.com/image/618778/%E4%BD%BF%E7%94%A8%20SSL%20%E5%92%8C%E8%87%AA%E7%AD%BE%E5%90%8D%E8%AF%81%E4%B9%A6%E5%9C%A8%20Tomcat%207%20%E4%B8%8B%E4%BD%BF%20JMX%20%E8%BF%90%E8%A1%8C.png)
我正在尝试让 JMX 在 Tomcat 7.0.23 下使用 SSL 工作。服务器位于 AWS 中,这意味着所有主机都经过了 NAT,我需要使用 JmxRemoteLifecycleListener 明确设置 JMX 使用的两个端口。我已经阅读了很多关于这个主题的资料,但我就是无法让所有部分正常工作。
无需 SSL,我就能让 JMX 正常工作。我已下载适用于我的 Tomcat 版本的 catalina-jmx-remote.jar 版本,并将其安装在我的 tomcat/lib 目录中。我的 server.xml 包含:
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
rmiRegistryPortPlatform="1099" rmiServerPortPlatform="1098" />
当我使用以下设置启动 Tomcat 时,我可以连接不安全的会话:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=/path/to/jmxremote.access
-Djava.rmi.server.hostname=<public IP of server>
-Dcom.sun.management.jmxremote.ssl=false
但是,如果我将这些更改为以下内容,则无法建立 SSL 连接:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=/path/to/jmxremote.access
-Djava.rmi.server.hostname=<public IP of server>
-Dcom.sun.management.jmxremote.ssl=true
-Dcom.sun.management.jmxremote.registry.ssl=true
-Dcom.sun.management.jmxremote.ssl.need.client.auth=false
-Dcom.sun.management.jmxremote.authenticate=true
-Djavax.net.ssl.keyStore=/path/to/keystore.dat
-Djavax.net.ssl.keyStorePassword=<password>
-Djavax.net.ssl.trustStore=/path/to/truststore.dat
-Djavax.net.ssl.trustStorePassword=<password>
keystore.dat 仅包含通过以下方式创建的单个证书:
openssl x509 -outform der -in cert.pem -out cert.der
keytool -import -alias tomcat -keystore keystore.dat -file cert.der -storepass <password>
truststore.dat 包含 java cacerts 的完整副本以及我的自签名证书的 CA 证书:
cp $JAVA_HOME/jre/lib/security/cacerts truststore.dat
keytool -storepasswd -storepass changeit -new <password> -keystore truststore.dat
keytool -import -trustcacerts -file mycacert.pem -alias myalias -keystore truststore.dat -storepass <password>
启动 Tomcat 后,我尝试通过 jconsole 进行连接,但无法建立连接。我尝试使用 openssl 验证 SSL,但看起来 Tomcat 没有使用该证书:
$ openssl s_client -connect <host>:1099
CONNECTED(00000003)
140735160957372:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 322 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
---
我已经通过导出密钥和验证证书链来验证我的本地密钥库和信任库是否设置正确(combined.pem 是来自 truststore.dat 的所有 CA 证书,cert.pem 是来自 keystore.dat 的我的证书):
$ openssl verify -verbose -purpose sslserver -CAfile combined.pem cert.pem
cert.pem: OK
所以现在我完全不知所措了。证书和 CA 证书看起来是正确的。未加密的 JMX 连接可以工作。但我似乎无法让连接使用 SSL。我在这里遗漏了什么?
我不知道这是否只是一种转移注意力的花招,但我看不出有任何方法可以指定 JMX 使用 keyStore 中的哪个证书。我读到的一些内容暗示它只使用别名为“tomcat”的证书。对吗?
答案1
我花了一段时间才弄清楚,但最终还是搞明白了。关键在于创建自签名证书时如何设置密钥库和信任库文件。下面是我编写的 bash 脚本,便于记忆:
DNAME="CN=foo JMX, OU=prodops, O=foo.com, L=Somewhere, S=XX, C=US"
DAYS=3650
PASSWORD=<password>
CACERTS="/path/to/java/jre/lib/security/cacerts"
rm -f jconsole* tomcat*
# First, create the keystore and truststore for the application, tomcat in this case. Use $CACERTS as the basis for the new keystore & truststore so that all public CA's remain intact:
keytool -genkey -alias tomcat -keyalg RSA -validity ${DAYS} -keystore tomcat.keystore -storepass ${PASSWORD} -keypass ${PASSWORD} -dname "${DNAME}"
cp ${CACERTS} tomcat.truststore
keytool -storepasswd -keystore tomcat.truststore -storepass changeit -new ${PASSWORD}
keytool -genkey -alias tomcat -keyalg RSA -validity ${DAYS} -keystore tomcat.truststore -storepass ${PASSWORD} -keypass ${PASSWORD} -dname "${DNAME}"
# And do the same for the JMX client, jconsole in this case:
keytool -genkey -alias jconsole -keyalg RSA -validity ${DAYS} -keystore jconsole.keystore -storepass ${PASSWORD} -keypass ${PASSWORD} -dname "${DNAME}"
cp ${CACERTS} jconsole.truststore
keytool -storepasswd -keystore jconsole.truststore -storepass changeit -new ${PASSWORD}
keytool -genkey -alias jconsole -keyalg RSA -validity ${DAYS} -keystore jconsole.truststore -storepass ${PASSWORD} -keypass ${PASSWORD} -dname "${DNAME}"
# Then, export the public certificates from the keystores:
keytool -export -alias tomcat -keystore tomcat.keystore -file tomcat.cer -storepass ${PASSWORD}
keytool -export -alias jconsole -keystore jconsole.keystore -file jconsole.cer -storepass ${PASSWORD}
# Finally, import the certificates into the truststores. Again, this allows the application (tomcat) to trust the client (jconsole), and vice-versa:
keytool -import -alias jconsole -file jconsole.cer -keystore tomcat.truststore -storepass ${PASSWORD} -noprompt
keytool -import -alias tomcat -file tomcat.cer -keystore jconsole.truststore -storepass ${PASSWORD} -noprompt
rm -f *.cer