背景
java代码编写的 spring boot 项目,需要发送重置密码邮件,因此,集成了
Spring maill
,本地window 开发测试一切顺利,能正常发出去。一通过docker 容器部署jar包,运行就报错
Error1:got bad greeting from host "smtp.exmail.qq.com", port: 465, response: [EOF]
==》 翻译意思:就是 “smtp.exmail.qq.com” 连接不上。
Error2:sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
==》 翻译意思: 就是"smtp.exmail.qq.com"
SSL认证不通过。
排查过程
- 首先,本地能正常发送邮件,服务器外部也能正常启动发送邮件,排除了代码问题和mail配置问题。但部署到容器内部就启动失败报错 ,基本锁定是容器的问题,要么是环境,要么是参数。
- 其次,网络、防火墙方面也是没问题,能
ping smtp.exmail.qq.com
,也能telnet smtp.exmail.qq.com 465
,排除网络问题。- 然后,由于容器也有正常运行过一段时间,所以我下意识的就忽略了运行参数,焦点在容器jdk版本。由于docker容器的jdk版本使用的别人编译好的
ibmjdk
,版本跟本地的不一致,不可控,为了验证是否是jdk 的问题,特意 把本地的jdk1.8_341
拷贝到服务器,并用容器编译了一个 jdk1.8容器,然后容器运行jar,启动报错,失败…
- 最终,仔细查看
java -jar
的启动参数,定位问题所在。。。
最终原因
经过漫长的排查,最终发现是因为服务器的容器启动java命令参数上,我加了
-Djavax.net.ssl.keyStore=xxx.keystore
指定了自己自定义的密码库(这是之前用jvisuialvm
监控jar服务运行情况的时候,开启了SSL证书认证,并且指定使用了自签的证书的路径,导致JDK默认的密码库路径%JAVA_HOME%/lib/security/cacerts
被覆盖,最终导致无法正常识别smtp.exmail.qq.com
(当然无法识别,我指定的自签的证书允许通过的只有服务器ip),因为浪费了一个下午,太恶心特此记录一下。
解决办法
Docker启动命令: jvisualvm 开启了TSL认证的,密码库覆盖了JDK默认的密码库,导致Error2:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath
的问题
# Docker启动命令: jvisualvm 开启了TSL认证的
docker run
-p 8080:8080 -p 1232:1232 -p 1233:1233 -p 465:465
-v /mydata/xxl-job:/data/applogs/xxl-job
--env JAVA_OPTS=-Xmx512M -Xms256m -Dfile.encoding=UTF-8 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1232 -Dcom.sun.management.jmxremote.rmi.port=1233 -Djava.rmi.server.hostname=IP -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=true -Dcom.sun.management.jmxremote.registry.ssl=true -Dcom.sun.management.jmxremote.ssl.need.client.auth=true -Djavax.net.ssl.keyStore=/data/applogs/xxl-job/visualvm/server/visualvm-server.keystore -Djavax.net.ssl.keyStorePassword=study@1024 -Djavax.net.ssl.trustStore=/data/applogs/xxl-job/visualvm/server/visualvm-server.truststore -Djavax.net.ssl.trustStorePassword=study@1024
--name xxl-job-admin xxl-job-admin:2.4.0 --restart=always -d xxl-job-admin
解决办法1:
如果既想要使用
jvisualvm
开启了TSL认证使用自定自签的TSL证书,同时又想兼顾SMTP
邮件服务的SSL认证。可以利用keeptool
工具,给 JDK默认的%JAVA_HOME%/lib/security/cacerts
导入jvisualvm
自签的密码库。我试过这个方法比较麻烦,不是容器的话在宿主机外的话倒是还好一些,但是容器内部的话,需要频繁删除容器重新部署,要求docker 就嵌套拼接好keeptool import 证书导入命令
,比较麻烦不利于维护。 所以放弃。
解决办法2,选择不使用 jvisualvm ,或者 使用 jvisualvm 但关闭了TSL自签证书认证的方式 。
注意 :jvisualvm 关闭ssl认证的方式不安全,建议只在测试环境这个弄。
# Docker启动命令: jvisualvm 关闭了SSL认证
docker run
-p 8080:8080 -p 1232:1232 -p 1233:1233 -p 465:465
-v /mydata/xxl-job:/data/applogs/xxl-job
--env JAVA_OPTS=-Xmx512M -Xms256m -Dfile.encoding=UTF-8 -Djava.rmi.server.hostname=IP -Dcom.sun.management.jmxremote.port=1232 -Dcom.sun.management.jmxremote.rmi.port=1233 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
--name xxl-job-admin
xxl-job-admin:2.4.0 --restart=always -d xxl-job-admin
参考资料
【JAVA导入可信任证书】(https://www.freesion.com/article/3510738952/#1_keytool__4)
【由于“PKIX 路径构建失败”错误,无法连接到 SSL 服务】(https://confluence.atlassian.com/kb/unable-to-connect-to-ssl-services-due-to-pkix-path-building-failed-779355358.html)