MySQL 8.0的Public Key Retrival錯誤,毫無規律可言怎麼破?

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一、背景介紹"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"近期筆者所在部門在使用MySQL時,經常會出現如下錯誤,且這個錯誤並不會一直出現,屬於某種偶發性的錯誤:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/1c\/1c78a047953ffd7b5ac51f3777b56404.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"筆者所在部門的連接數據庫的方法有JDBC、JDBCTemplate、C3P0連接池和Druid連接池等技術,在這些連接實現方法上筆者都觀察到出現過上述的錯誤。該錯誤有一定的特性,就是偶爾會出現該錯誤,該錯誤並不會一直出現,該錯誤的出現沒有明顯的規律。而且筆者發現,底層使用JDBC連接驅動的情況下,無論上層是採用何種數據源實現技術,筆者的部門都發現會報上述錯誤。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對上述錯誤,在筆者所在部門的軟硬件環境下,筆者對MySQL爲何會產生這個問題、這個問題產生的條件、MySQL官方文檔對這個問題的闡述、如何有效避免與解決這個問題等方面在本文進行了詳細的分析與解釋。希望能夠幫助到真正遇到這個問題的DBA與開發人員。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、環境介紹"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1、實驗構架"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"筆者所在部門使用的MySQL版本爲8.0.18版本,MySQL使用組複製技術保證高可用,即MySQL Group Replication(MGR)。筆者復現問題以及實驗的架構如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/d7\/d7168db45e3dafd07aeb6212935e502c.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖2.1  實驗所採用的構架"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本測試使用的MySQL MGR集羣搭建在虛擬機之上,虛擬機的詳細硬件配置如下表所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/1d\/1d9e13a714d083effa3e8bf9503f04c9.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"表2.1  詳細硬件配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了充分測試各種類型的連接數據庫的方式在筆者所在部門使用的MySQL上的表現,筆者分別測試JDBC、JDBCTemplate、C3P0以及Druid方式連接數據庫,參與測試的軟件版本如下表所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/1e\/1ec11288ec8dea50e3962365795301b5.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"表2.2  參與測試的軟件版本"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、問題報錯展示"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在使用JDBC連接時,可能會出現“Public Key Retrieval is not allowed”的錯誤,從錯誤的Java堆棧信息中可以看出,該錯誤是在JDBC與MySQL建立Connection對象時出現的,該錯誤的錯誤堆棧如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/8e\/8e33aeab79b33b0217e07225ee63784a.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖3.1  JDBC錯誤堆棧"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在使用C3P0連接池時,也會出現“Public Key Retrieval is not allowed”的錯誤,從錯誤的Java堆棧信息中可以看出,該錯誤仍是在JDBC與MySQL建立Connection對象時出現,該錯誤會導致C3P0連接池在初始化時失敗,該錯誤的錯誤堆棧如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/ee\/ee8868ed6a046569a9c2bac49c627a2e.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖3.2  C3P0錯誤堆棧"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在使用Druid連接池時,也會出現“Public Key Retrieval is not allowed”的錯誤,與C3P0連接池一樣,會導致Druid連接池在初始化時失敗,該錯誤的錯誤堆棧如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/01\/01e503c3a00f40c50272fc3721c0b7f2.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖3.3  Druid錯誤堆棧"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"四、問題分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"筆者在查閱了網上關於“Public Key Retrieval is not allowed”錯誤的資料後,發現大多數網上關於該問題的博文對該問題的解釋都存在一定的紕漏。首先需要明確的是出現該問題的時候,MySQL配置的密碼認證插件爲如下兩種:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"sha256_password"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"caching_sha2_password"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"值得注意的是,如果使用“mysql_native_password”密碼認證插件,不會出現“Public Key Retrieval is not allowed”錯誤。MySQL配置的密碼認證方式可以通過如下命令進行查看。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/dd\/dd8f820ebcab10f4792e3729eb51261d.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接着筆者來詳細分析“Public Key Retrieval is not allowed”錯誤產生的原因。首先MySQL 8.0默認推薦使用“sha256_password”和“caching_sha2_password”這兩種認證插件。只有較老的MySQL版本仍然會使用“mysql_native_password”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據MySQL提供的官方文檔(https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/caching-sha2-pluggable-authentication.html),這兩種插件都是使用SHA256算法來對密碼進行保護。這些插件的具體執行流程如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"檢查客戶端是否禁用SSL\/TLS加密傳輸;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果客戶端未禁用SSL\/TLS加密傳輸,則客戶端在進行認證時的認證報文(傳輸用戶名和密碼的報文)是使用TLS進行傳輸的,兩種插件認爲認證報文傳輸安全,不進行任何其他操作;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"如果客戶端禁用SSL\/TLS加密傳輸,則客戶端在進行認證時的認證報文(傳輸用戶名和密碼的報文)是使用明文進行傳輸的,兩種插件認爲認證報文傳輸不安全,會單獨對明文報文中的密碼使用RSA加密方式進行加密。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1、啓用SSL\/TLS加密傳輸的客戶端認證流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上述流程所述,當客戶端未禁用SSL\/TLS加密傳輸時,通過Wireshark等抓包工具可以觀察到整個客戶端與MySQL服務端交互的過程都被TLS協議加密保護了。如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/08\/088d9e738f810dc7524e158a65ab9426.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖4.1  啓用TLS協議MySQL傳輸報文"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然會產生登錄的明文報文,但是該明文報文中的用戶信息爲空,真正的用戶信息在TLS握手階段後的密文中。如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/3d\/3d390ea0bfacd720cd63d79a58de398e.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖4.2  啓用TLS協議MySQL登錄數據包詳情"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2、禁用SSL\/TLS加密傳輸的客戶端認證流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上述流程所述,當客戶端禁用SSL\/TLS加密傳輸時(比如JDBC連接串設置useSSL=false參數),用戶的登錄信息會在明文中進行傳輸,如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/83\/838728d349afc087dc068653b33b7a79.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖4.3  禁用TLS協議MySQL傳輸報文"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"明文傳輸密碼存在安全問題,此時,兩種插件會嘗試使用RSA加密(RSA encryption)方法對明文報文中的密碼部分進行加密,加密後的密碼部分如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/22\/2222e5b0f6b691f911c813b90cb9aae3.png","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖4.4  啓用TLS協議MySQL登錄數據包詳情"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"五、解決方案"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據前面的分析,導致“Public Key Retrieval is not allowed”主要是由於當禁用SSL\/TLS協議傳輸後,客戶端會使用服務器的公鑰進行傳輸,默認情況下客戶端不會主動去找服務器拿公鑰,此時就會出現上述錯誤。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過查閱官方文檔,出現Public Key Retrieval的場景可以概括爲在禁用SSL\/TLS協議傳輸切當前用戶在服務器端沒有登錄緩存的情況下,客戶端沒有辦法拿到服務器的公鑰。具體的場景如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"新建數據庫用戶,首次登錄;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"數據庫的用戶名、密碼發生改變後登錄;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"服務器端調用FLUSH PRIVELEGES指令刷新服務器緩存。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對上述錯誤,有如下的解決方案:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"在條件允許的情況下,不要禁用SSL\/TLS協議,即不要在CLI客戶端使用--ssl-mode=disabled,或在JDBC連接串中加入useSSL=false;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果必須禁用SSL\/TLS協議,則可以嘗試使用CLI客戶端登錄一次MySQL數據庫製造登錄緩存;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"如果必須禁用SSL\/TLS協議,則可以通過增加如下參數允許客戶端獲得服務器的公鑰:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在JDBC連接串中加入allowPublicKeyRetrieval=true參數;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在CLI客戶端連接時加入--get-server-public-key參數;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在CLI客戶端連接時加入--server-public-key-path=file_name參數,指定存放在本地的公鑰文件。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者介紹"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"農行研發中心“數風雲”團隊,一支朝氣蓬勃、快速成長的技術團隊,始終致力於農行大數據、數據庫和雲計算等領域的應用實踐與技術創新,探索數據賦能,勇攀數據雲巔,爲企業數字化轉型和金融科技發展不斷貢獻力量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文轉載自:dbaplus社羣(ID:dbaplus)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文鏈接:"},{"type":"link","attrs":{"href":"https:\/\/mp.weixin.qq.com\/s\/pZz76UUQt5-ZOOlyttvDbw","title":"xxx","type":null},"content":[{"type":"text","text":"MySQL 8.0的Public Key Retrival錯誤,毫無規律可言怎麼破?"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章