原文地址:[https://blog.fanscore.cn/a/53/](https://blog.fanscore.cn/a/53/) # 1. 前言 本文是[與世界分享我剛編的轉發ntunnel_mysql.php的工具](https://blog.fanscore.cn/a/47/)的後續, ...
原文地址:https://blog.fanscore.cn/a/53/
1. 前言
本文是與世界分享我剛編的轉發ntunnel_mysql.php的工具的後續,之前的實現有些拉胯,這次重構了下。需求背景是為了在本地macbook上通過開源的mysql可視化客戶端(dbeaver、Sequel Ace等)訪問我司測試環境的mysql,整個測試環境的如圖所示:
那麼就有以下幾種方式:
- 客戶端直連mysql
#Pass# 測試環境mysql只提供了內網ip,只允許測試環境上的機器連接,因此不可行 - 通過ssh隧道連接
#Pass# 測試環境機器倒是可以ssh上去,但是只能通過堡壘機接入,且堡壘機不允許ssh隧道,因此不可行 - navicat http隧道連接
#Pass# 測試環境有機器提供了公網ip開放了http服務,因此技術上是可行的,但navicat非開源免費軟體,我司禁止使用,因此不可行 - 測試環境選一臺機器建立mysql代理轉發請求
#Pass# 測試環境機器只開放了80埠,且已被nginx占用,因此不可行 - 內網穿透
這個想法很好,下次不要再想了
既然上面的方式都不行,那怎麼辦呢?因此我產生了一個大膽的想法
2. 一個大膽的想法
大概架構如下
首先,在本地pc上啟動一個sidecar
進程,該進程監聽3306
埠,實現mysql協議,將自己偽裝為一個mysql server。本地pc上的mysql客戶端連接到sidecar
,發送請求數據包給sidecar
,從sidecar
讀取響應包。
然後在測試環境某台機器上啟動transport
進程,該進程啟動http服務,由nginx代理轉發請求,相當於監聽在80埠,然後連接到測試環境的mysql server。
sidecar
會將來自客戶端的請求包通過http請求轉發給transport
,transport
將請求包轉發到測試環境對應的mysql server,然後讀取mysql的響應數據包,然後將響應數據包返回給sidecar
,sidecar
再將響應包返回給mysql客戶端。
遵循上述的基本原理,我將其實現出來: https://github.com/Orlion/hersql。但是在描述hersql
的實現細節之前我們有必要瞭解下mysql協議
3. mysql協議
mysql客戶端與服務端交互過程主要分為兩個階段:握手階段與命令階段。交互流程如下:
在最新版本中,握手過程比上面要複雜,會多幾次交互
3.1 握手階段
在握手階段,3次握手建立tcp連接後服務端會首先發送一個握手初始化包,包含了
- 協議版本號:指示所使用的協議版本。
- 伺服器版本:指示MySQL伺服器版本的字元串。
- 連接ID:在當前連接中唯一標識客戶端的整數。
- 隨機數據:包含一個隨機字元串,用於後續的身份驗證。
- 伺服器支持的特性標誌:指示伺服器支持的客戶端功能的位掩碼。
- 字元集:指示伺服器使用的預設字元集。
- 預設的身份驗證插件名(低版本沒有該數據)
隨後客戶端會發送一個登錄認證包,包含了:
- 協議版本號:指示所使用的協議版本。
- 用戶名:用於身份驗證的用戶名。
- 加密密碼:客戶端使用服務端返回的隨機數對密碼進行加密
- 資料庫名稱:連接後要使用的資料庫名稱。
- 客戶端標誌:客戶端支持的功能的位掩碼。
- 最大數據包大小:客戶端希望接收的最大數據包大小。
- 字元集:客戶端希望使用的字元集。
- 插件名稱:客戶端希望使用的身份驗證插件的名稱。
服務端收到客戶端發來的登錄認證包驗證通過後會發送一個OK包,告知客戶端連接成功,可以轉入命令交互階段
在mysql 8.0預設的身份驗證插件為
caching_sha2_password
,低版本為mysql_native_password
,兩者的驗證交互流程有所不同個,caching_sha2_password
在緩存未命中的情況下還會多幾次交互。另外如果服務端與客戶端的驗證插件不同的話,也是會多幾次交互。
3.2 命令階段
在命令階段,客戶端會發送命令請求包到服務端。數據包的第一個位元組標識了當前請求的類型,常見的命令有:
- COM_QUERY命令,執行SQL查詢語句。
- COM_INIT_DB命令,連接到指定的資料庫。
- COM_QUIT命令,關閉MySQL連接。
- COM_FIELD_LIST命令,列出指定表的欄位列表。
- COM_PING命令,向MySQL伺服器發送PING請求。
- COM_STMT_系列預處理語句命令
請求響應的模式是客戶端會發一個請求包,服務端會回覆n(n>=0)
個響應包
最後客戶端斷開連接時會主動發送一個COM_QUIT
命令包通知服務端斷開連接
4. hersql數據流轉過程
在瞭解mysql協議之後我們就可以來看下hersql的數據流轉過程了。
transport
連接mysql server
時必須要知道目標資料庫的地址與埠號(mysql client
連接的是sidecar
),所以hersql
要求mysql client
需要在資料庫名
中攜帶目標資料庫的地址與埠號。
transport
發給mysql server
的登錄請求包中需要包含用mysql server
發來的隨機數加密之後的密碼,但是mysql client
給到sidecar
的登錄請求包中的密碼是用sidecar
給的隨機數加密的,因此無法直接拿來使用,所以hersql
要求mysql client
需要在資料庫名
中攜帶密碼原文,transport
會用mysql server
給的隨機數進行加密, 這也是hersql
的局限。
5. hersql使用
上面介紹了一堆原理性的東西,那麼如何使用呢?
5.1 在一臺能夠請求目標mysql server的機器上部署hersql transport
首先你需要下載下來hersql
的源碼:https://github.com/Orlion/hersql,還需要安裝下golang
,這些都完成後你就可以啟動hersql transport
了。但是先彆著急,我先解釋下transport
的配置文件tranport.example.yaml
:
server:
# transport http服務監聽的地址
addr: :8080
log:
# 標準輸出的日誌的日誌級別
stdout_level: debug
# 文件日誌的日誌級別
level: error
# 文件日誌的文件地址
filename: ./storage/transport.log
# 日誌文件的最大大小(以MB為單位), 預設為 100MB。日誌文件超過此大小會創建個新文件繼續寫入
maxsize: 100
# maxage 是根據文件名中編碼的時間戳保留舊日誌文件的最大天數。
maxage: 168
# maxbackups 是要保留的舊日誌文件的最大數量。預設是保留所有舊日誌文件。
maxbackups: 3
# 是否應使用 gzip 壓縮旋轉的日誌文件。預設是不執行壓縮。
compress: false
你可以根據你的需求修改配置,然後就可以啟動transport
了
$ go run cmd/transport/main.go -conf=transport.example.yaml
一般情況下都是會先編譯為可執行文件,由systemd之類的工具托管transport進程,保證transport存活。這裡簡單期間直接用go run起來
5.2 在你本地機器部署啟動hersql sidecar
同樣的,你需要下載下來hersql
的源碼:https://github.com/Orlion/hersql,提前安裝好golang
。修改下sidecar
的配置文件sidecar.example.yaml
:
server:
# sidecar 監聽的地址,之後mysql client會連接這個地址
addr: 127.0.0.1:3306
# transport http server的地址
transport_addr: http://x.x.x.x:xxxx
log:
# 與transport配置相同
就可以啟動sidecar
了
$ go run cmd/sidecar/main.go -conf=sidecar.example.yaml
同樣的,一般情況下也都是會先編譯為可執行文件,mac上是launchctl之類的工具托管sidecar進程,保證sidecar存活。這裡簡單期間直接用go run起來
5.3 客戶端連接
上面的步驟都執行完成後,就可以打開mysql客戶端使用了。資料庫地址和埠號需要填寫sidecar
配置文件中的addr
地址,sidercar
不會校驗用戶名和密碼,因此用戶名密碼可以隨意填寫
重點來了: 資料庫名必須要填寫,且必須要按照以下格式填寫
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
舉個例子:
root:123456@tcp(10.10.123.123:3306)/BlogDB
如圖所示:
5.4 舉個例子
目標mysql伺服器
- 地址:10.10.123.123:3306
- 資料庫:BlogDB
- 用戶名:root
- 密碼:123456
可以直連目標mysql伺服器的機器
- 地址:10.10.123.100
- 開放埠:8080
那麼transport
可以配置為
server:
addr: :8080
sidecar
可以配置為
server:
addr: 127.0.0.1:3306
transport_addr: http://10.10.123.100:8080
客戶端連接配置
- 伺服器地址:127.0.0.1
- 埠: 3306
- 資料庫名
root:123456@tcp(10.10.123.123:3306)/BlogDB
5.5 局限
hersql
目前只支持mysql_native_password
的認證方式,mysql8預設的認證方式是caching_sha2_password
,所以如果要通過hersql
連接mysql8
需要註意登錄用戶的認證方式是否是mysql_native_password
,如果是caching_sha2_password
那暫時是無法使用的。
6. 參考資料
如果github.com/Orlion/hersql對你有幫助歡迎點個star