Python使用sshtunnel通過(guò)跳板機(jī)連接遠(yuǎn)程中間件或內(nèi)網(wǎng)依賴(lài)服務(wù)
本著遇到問(wèn)題,針對(duì)問(wèn)題,尋找解決方案的思路,回顧一次思考過(guò)程。
背景
?公司開(kāi)發(fā)使用的是某大型平臺(tái)云環(huán)境作為內(nèi)網(wǎng),本地開(kāi)發(fā)、服務(wù)間的聯(lián)調(diào),如果使用VPN可以輕松解決。
但是如果這個(gè)VPN不穩(wěn)定,怎么辦呢?
解決思路
因?yàn)閮?nèi)網(wǎng)環(huán)境是個(gè)安全性要求較高的環(huán)境,又不能將MySQL、Redis、Kafka等中間件的服務(wù)端口直接暴露在公網(wǎng)。在平時(shí)本地查看的時(shí)候,通過(guò)自己云上一臺(tái)機(jī)器(這里可以理解為跳板機(jī)或者堡壘機(jī)),作為跳板去連接其內(nèi)網(wǎng)中的其他服務(wù)。
比如Navicat可以使用如下方式配置ssh隧道連接。

隧道連接上之后,這時(shí)的效果就是登錄到了一臺(tái)遠(yuǎn)程的機(jī)器,所以這時(shí)候訪(fǎng)問(wèn)這里中間件,比如這里的MySQL,連接就需要使用內(nèi)網(wǎng)的配置,host主機(jī)就不能再配置為遠(yuǎn)程機(jī)器的外網(wǎng)IP了,如果是連接到的遠(yuǎn)程本機(jī)則為localhost,如果是內(nèi)網(wǎng)中的其他機(jī)器,就按內(nèi)網(wǎng)地址填寫(xiě)就可以了。

Navicat配置數(shù)據(jù)庫(kù)<常規(guī)>連接
好了,配置好ssh和常規(guī),沒(méi)有問(wèn)題的話(huà),就可以正常連接并查看遠(yuǎn)程的MySQL數(shù)據(jù)庫(kù)了。
補(bǔ)充一下,可能對(duì)于ssh不太熟悉的伙伴兒來(lái)說(shuō),對(duì)登錄驗(yàn)證方式也不是很熟悉。
一般常用的,比如通過(guò)Termius(個(gè)人覺(jué)得它顏值在線(xiàn),功能在線(xiàn),很是推薦)連接遠(yuǎn)程的linux機(jī)器,使用密碼或者公鑰方式登錄。(使用方式,網(wǎng)絡(luò)上有很多文檔介紹。)
因?yàn)楸敬雾?xiàng)目是Python開(kāi)發(fā),所以想用這種方式直接在python本地開(kāi)發(fā)時(shí),采用該種方式連接數(shù)據(jù)庫(kù)。
在網(wǎng)絡(luò)上查詢(xún)了一番,發(fā)現(xiàn)通過(guò)sshtunnel這個(gè)模塊,可以快速搞定。使用代碼一看就會(huì),沒(méi)有幾行,下面簡(jiǎn)單說(shuō)下自己對(duì)于堡壘機(jī)的認(rèn)識(shí)。
簡(jiǎn)單一句話(huà)就是,一個(gè)外部訪(fǎng)問(wèn)內(nèi)網(wǎng)環(huán)境的入口只有一個(gè),比如都通過(guò)某臺(tái)機(jī)器的22號(hào)端口。這就像是疫情期間,進(jìn)小區(qū)只能從大門(mén)進(jìn),其他的側(cè)門(mén)和后門(mén)都不讓了。當(dāng)然這樣做就能達(dá)到監(jiān)控的目的,主要的好處就是安全,而且進(jìn)入內(nèi)網(wǎng)操作的人員,其行為也會(huì)被記錄(外部進(jìn)入小區(qū)的朋友,請(qǐng)登記身份證,來(lái)訪(fǎng)意向等信息)且能夠被有效審計(jì)和定責(zé)。
如下圖展示的,外部的PC只能通過(guò)bastion去連接A、B、C的三個(gè)服務(wù),但PC不能直接訪(fǎng)問(wèn)A,需要通過(guò)bastion這個(gè)跳板間接訪(fǎng)問(wèn)。

所以以上通過(guò)sshtunnel來(lái)連接中間件,嚴(yán)格講不是堡壘機(jī),因?yàn)闆](méi)有設(shè)置一套復(fù)雜的記錄策略,而只是基本思路是一樣的。
代碼如下:
```python
import redis ?# redis模塊
from sshtunnel import SSHTunnelForwarder ?# ssh連接庫(kù)
server = SSHTunnelForwarder(
? ?ssh_address_or_host=("xx.xx.xx.xx", 22), ? # ssh地址
? ?ssh_username="username", ?# ssh連接的用戶(hù)名
? ?ssh_pkey="d:/usr/.ssh/id_rsa", ?# ssh連接的本地私鑰地址,當(dāng)然也可以是密碼
? ?# ssh_password= pwd
? ?remote_bind_address=('127.0.0.1', 6379))
server.start()
db = redis.Redis(host='127.0.0.1', port=server.local_bind_port, password="123456", decode_responses=True, db=1)```
以上代碼只需要注意兩點(diǎn)就可以:
創(chuàng)建連接堡壘機(jī)的server實(shí)例時(shí),ssh_pkey參數(shù)指定本地私鑰所在目錄即可,不必寫(xiě)到文件,內(nèi)部會(huì)自動(dòng)補(bǔ)全。
其他中間件模塊連接服務(wù)時(shí),使用方式不改變,在port的地方,使用堡壘機(jī)server實(shí)例提供的端口即可。
總結(jié)
本次解決思路歸納如下:
1. 想法來(lái)源:看到Navicat的連接方式,將此種方式搬到python中(使用上,其實(shí)實(shí)現(xiàn)上本來(lái)就有)。
2. 類(lèi)比jump server: 同樣的可以通過(guò)代理方式,將其他本地依賴(lài)的服務(wù),將grpc的客戶(hù)端,通過(guò)跳板機(jī)連上,或者flask、django的服務(wù),也可以通過(guò)該方式連上。
3. 在django中集成:在settings中分環(huán)境設(shè)置即可,開(kāi)發(fā)環(huán)境正常使用,不影響其他環(huán)境的連接設(shè)置。
我是扣丁船,在代碼的海洋里揚(yáng)帆。
