Py学习  »  DATABASE

一次MySQL Connector升级引发连接失败的排查之路

DBAplus社群 • 2 年前 • 373 次点击  

作者介绍

农行研发中心“数风云”团队,一支朝气蓬勃、快速成长的技术团队,始终致力于农行大数据、数据库和云计算等领域的应用实践与技术创新,探索数据赋能,勇攀数据云巅,为企业数字化转型和金融科技发展不断贡献力量。

郭斌斌,爱可生DBA团队成员,负责MySQL数据库、爱可生云树DMP产品的日常运维,擅长数据库故障处理。对开源数据库MySQL、Redis有浓厚兴趣。


一、现象


在IBM jdk1.8.0环境上将mysql-connector-java-8.0.18升级至8.0.20后报错:“javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)”,报错详见图1。在openjdk 1.8版本上升级MySQL驱动后能正常连接。 


图1. IBM jdk1.8环境MySQL驱动升级后连接失败


初步分析为IBM jdk1.8版本中支持的协议或加密套件(Cipher suites)老旧导致SSL连接握手失败。


二、分析


1、官网资料分析


(1)MySQL支持的协议和套件


经查官网,MySQL驱动尝试连接MySQL 8.0时会尝试使用TLS版本:TLSv1,TLSv1.1,TLSv1.2,TLSv1.3。驱动TLS配置文件TlsSettings.properties(自8.0.19版本新增,详见“2.1.2 MySQL驱动更新说明”)中Cipher suites均已TLS开头,如图2。官网地址:https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-using-ssl.html。


图2. MySQL驱动TLS配置文件


(2)MySQL驱动更新说明


继续查看官网MySQL驱动发版说明,mysql connector-java-8.0.18之后8.0.19和8.0.20的变更发现:从8.0.19开始,驱动使用的Cipher suites预先在一个配置文件中进行了限制,该配置文件及目录:src/main/resources/com/mysql/cj/TlsSettings.properties(官网地址:https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-binary-installation.html)。经查验8.0.19和8.0.20均能找到该配置文件,而8.0.18版本没有该配置文件。


(3)IBM JSSE2支持的协议和套件


继续翻IBM官网,IBM JSSE2默认支持协议: SSL,TLS, TLSv1, TLSv1.1,TLSv1.2。部分Cipher suites在ORACLE中以“TLS”开头,而在IBM SDK中以”SSL”开头。IBM官网显示的53个Cipher suites中49个以SSL开头,4个以TLS开头。文档提示可以通过 将系统参数com.ibm.jsse2.overrideDefaultCSName设置为true(默认为false)使得IBM JDK中cipher suite名称与ORACLE匹配。文档解释这个差异是由于部分套件在TSL规范首次发布之前就已命名。


官网链接:

https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.security.component.80.doc/security-component/jsse2Docs/ciphersuites.html



2、抓包分析


(1)IBM jdk1.8+mysql-connector-java-8.0.20


编写脚本采用jdbc连接数据库,使用java -verbose, 获取class loader顺序,使用 java -Djavax.net.debug=true 查看 TLS 的信息:


javax.net.ssl|FINE|01|main|2021-03-30 11:47:33.493 UTC|Thread.java:1164|No available cipher suite for TLS13javax.net.ssl|FINE|01|main|2021-03-30 11:47:33.493 UTC|Thread.java:1164|No available cipher suite for TLS12javax.net.ssl|FINE|01|main|2021-03-30 11:47:33.493 UTC|Thread.java:1164|No available cipher suite for TLS11javax.net.ssl|FINE|01|main|2021-03-30 11:47:33.494 UTC|Thread.java:1164|No available cipher suite for TLS10


日志中可看到依次尝试使用TLS13、TLS12、TLS11、TLS10协议,但没有可用的加密套件(cipher suite)。


(2)IBM jdk1.8+mysql-connector-java-8.0.20+com.ibm.jsse2.overrideDefaultCSName=true


通过脚本中添加参数com.ibm.jsse2.overrideDefaultCSName=true显示连接MySQL成功。添加javax.net.debug=true观察SSL的握手阶段:


javax.net.ssl|FINE|01|main|2021-03-30 14:13:29.736 UTC|Thread.java:1164|Consuming ServerHello handshake message ("ServerHello": {  "server version"      : "TLSv1.2",  "random"              : "33 17 60 BC C2 2E 3B D3 AA 40 E3 AC 8E EB 6B B8 8B 69 44 4B 3C 1F 2C F0 44 4F 57 4E 47 52 44 01",  "session id"          : "7C 9E C9 73 9A 3B F1 5E D4 0D D7 7F B5 55 5A 34 B3 08 F3 1F 30 37 84 2F 5E 9D B0 52 8B 62 DA 13",  "cipher suite"        : "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030)",  "compression methods" : "00",  "extensions"          : [    "renegotiation_info (65,281)": {      "renegotiated connection": []    },    "ec_point_formats (11)": {      "formats": [uncompressed, ansiX962_compressed_prime, ansiX962_compressed_char2]    },    "extended_master_secret (23)": {          }  ]}


通过日志可见握手阶段选择了TLSv1.2和cipher suite:  TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384。


(3) IBM jdk1.8+mysql-connector-java-8.0.18


用javax.net.debug=true观察mysql-connector 8.0.18的表现:


Produced ClientHello handshake message ("ClientHello": {  "client version"      : "TLSv1.2",  "random"              : "B6 0B CF 14 D5 3F 42 00 B5 5C 11 61 CA F7 E0 FD 8E F3 82 DC 96 F1 30 47 80 EC 7D A6 29 FC 44 9A",  "session id"          : "",  "cipher suites"       : "[SSL_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C), SSL_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B), SSL_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030), SSL_RSA_WITH_AES_256_GCM_SHA384(0x009D), SSL_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(0xC02E), SSL_ECDH_RSA_WITH_AES_256_GCM_SHA384(0xC032), SSL_DHE_RSA_WITH_AES_256_GCM_SHA384(0x009F), SSL_DHE_DSS_WITH_AES_256_GCM_SHA384(0x00A3), SSL_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), SSL_RSA_WITH_AES_128_GCM_SHA256(0x009C), SSL_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(0xC02D), SSL_ECDH_RSA_WITH_AES_128_GCM_SHA256(0xC031), SSL_DHE_RSA_WITH_AES_128_GCM_SHA256(0x009E), SSL_DHE_DSS_WITH_AES_128_GCM_SHA256(0x00A2), SSL_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(0xC024), SSL_ECDHE_RSA_WITH_AES_256_CBC_SHA384(0xC028), SSL_RSA_WITH_AES_256_CBC_SHA256(0x003D), SSL_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(0xC026), SSL_ECDH_RSA_WITH_AES_256_CBC_SHA384(0xC02A), SSL_DHE_RSA_WITH_AES_256_CBC_SHA256(0x006B), SSL_DHE_DSS_WITH_AES_256_CBC_SHA256(0x006A), SSL_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(0xC00A), SSL_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), SSL_RSA_WITH_AES_256_CBC_SHA(0x0035), SSL_ECDH_ECDSA_WITH_AES_256_CBC_SHA(0xC005), SSL_ECDH_RSA_WITH_AES_256_CBC_SHA(0xC00F), SSL_DHE_RSA_WITH_AES_256_CBC_SHA(0x0039), SSL_DHE_DSS_WITH_AES_256_CBC_SHA(0x0038), SSL_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(0xC023), SSL_ECDHE_RSA_WITH_AES_128_CBC_SHA256(0xC027), SSL_RSA_WITH_AES_128_CBC_SHA256(0x003C), SSL_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(0xC025), SSL_ECDH_RSA_WITH_AES_128_CBC_SHA256(0xC029), SSL_DHE_RSA_WITH_AES_128_CBC_SHA256(0x0067), SSL_DHE_DSS_WITH_AES_128_CBC_SHA256(0x0040), SSL_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(0xC009), SSL_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), SSL_RSA_WITH_AES_128_CBC_SHA(0x002F), SSL_ECDH_ECDSA_WITH_AES_128_CBC_SHA(0xC004), SSL_ECDH_RSA_WITH_AES_128_CBC_SHA(0xC00E), SSL_DHE_RSA_WITH_AES_128_CBC_SHA(0x0033), SSL_DHE_DSS_WITH_AES_128_CBC_SHA(0x0032), TLS_EMPTY_RENEGOTIATION_INFO_SCSV(0x00FF)]",  "compression methods" : "00",  "extensions"          : [    "supported_groups (10)": {      "versions": [secp256r1, secp384r1, secp521r1]    },    "ec_point_formats (11)": {      "formats": [uncompressed]    },    "signature_algorithms (13)": {      "signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha224, rsa_sha224, dsa_sha224, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]    },    "signature_algorithms_cert (50)": {      "signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha224, rsa_sha224, dsa_sha224, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]    },    "extended_master_secret (23)": {      <empty>    },    "supported_versions (43)": {      "versions": [TLSv1.2, TLSv1.1, TLSv1]    }  ]})


可以看到MySQL connector 8.0.18在建立TLS连接时, 就已经选择了TLSv1.2和cipher suite列表,列表中大部分cipher suite以“SSL”开头。


3、结论


通过官网资料和抓包分析,MySQL connector 8.0.18未对cipher suite作TLS限制,故能连接成功。当驱动升级至MySQL connector 8.0.19或8.0.20版本,MySQL对cipher suite进行了限制,且均已TLS开头,而IBM JSSE2大部分套件还保持以SSL开头的命名,故而导致连接握手时因没有可用套件而失败。


三、方案


由于本次驱动升级是在IBM WAS环境上进行,还需要在WAS环境上验证。


1、方案1


建议IBM jdk升级版本兼容MySQL协议和加密套件,经与厂商确认此方案暂不可行。


2、方案2


依据IBM官方文档建议将系统参数com.ibm.jsse2.overrideDefaultCSName设置为true,在IBM jdk1.8.0_281+MySQL connector 8.0.20上通过脚本调用方式连接成功。


经咨询IBM厂商,参数com.ibm.jsse2.overrideDefaultCSName是java 1.8.0_211开始才有的,对应用是否有影响取决于应用如何用SSL,如果用WAS的配置则没有关系,如果用自己的,需检查code里是否定义了cipher用SSL还是TLS开头。该参数在WAS中配置过程如下:


服务器-》您的服务器-》进程定义-》java虚拟机-》定制属性,添加:

  • 名字:com.ibm.jsse2.overrideDefaultCSName

  • 值:true


保存配置后同步,重新启动生效。


本方法经项目组在WAS9.0.5.5+jdk 1.8.0_211环境上验证并未生效。最终通过将WAS环境上jdk升级到1.8.0_281,并使用com.ibm.jsse2.overrideDefaultCSName=true,才生效。


WAS9已经将WAS和jdk分开,可单独升级jdk,不用升级WAS版本。


3、方案3


jdbc连接带上参数useSSL=false和allowPublicKeyRetrieval=true,连接成功。本方案作为IBM jdk升级之前的缓释方案供项目组临时采用。


配置参数AllowPublicKeyRetrieval=True,可能会导致恶意代理通过中间人攻击(MITM)获取到明文密码,所以,JDBC默认关闭该参数,需谨慎开启。


题外话:若仅配置useSSL=false,未配置AllowPublicKeyRetrieval=True时,当MySQL服务端应用用户认证缓存失效时,应用端可能会出现Public Key Retrieval is not allowed的错误无法连接数据库。此情况需确保数据库服务端应用用户认证缓存不失效(即在MySQL服务端应用用户缓存失效操作后,进行一次应用用户认证登录)。


4、方案4


在IBM厂商建议下,如果jdk不升级,继续使用JDK 1.8.0_211,尝试使用如下参数: 


JVM通用参数里设置: 


-Dcom.unboundid.util.SSLUtil.defaultSSLProtocol=TLSV1.2 -Dcom.unboundid.util.SSLUtil.enabledSSLProtocols=TLSV1.2


经以上方案验证:方案4未生效,方案3为临时缓释措施,最终采用了升级jdk+com.ibm.jsse2.overrideDefaultCSName=true的方案2。


dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/117597
 
373 次点击