五分鐘技術(shù)趣談 | MQTT設(shè)備接入時獲取真實IP地址的解決方案
Part 01
需求背景
在OneNET平臺某私有化項目中,項目方的需求是要獲取設(shè)備真實IP地址,然后根據(jù)設(shè)備的IP來統(tǒng)計處于各個省內(nèi)區(qū)域的設(shè)備數(shù)量展示到大屏上。
Part 02
查找解決方案
以MQTT設(shè)備接入為例,由于項目方使用的外層負(fù)載是Nginx軟負(fù)載,并且MQTT協(xié)議是基于TCP,只能走4層方式轉(zhuǎn)發(fā)報文,Nginx轉(zhuǎn)發(fā)報文的時候會將源TCP連接的IP地址改寫為自己的內(nèi)網(wǎng)IP地址,不能像F5這種硬負(fù)載可以直接將設(shè)備的源地址轉(zhuǎn)發(fā)到后端服務(wù)上,因此就不能直接通過配置Nginx的方式來讓MQTT接入服務(wù)獲取到設(shè)備源IP地址,也就不能實現(xiàn)項目方的需求。
經(jīng)過網(wǎng)上查詢相關(guān)解決方案,發(fā)現(xiàn)一個Internet協(xié)議叫做proxy protocl(參考資料:https://www.jianshu.com/p/cc8d592582c9),該協(xié)議可以通過為TCP包添加一個很小的頭信息,來傳遞客戶端信息(協(xié)議棧、源IP、目的IP、源端口、目的端口等),在網(wǎng)絡(luò)情況復(fù)雜又需要獲取用戶真實IP時非常有用。其本質(zhì)是在三次握手結(jié)束后由代理在連接中插入了一個攜帶了原始連接四元組信息的數(shù)據(jù)包。
圖片
proxy protocol協(xié)議流程
查閱到proxy protocol報文的格式如下圖中所示,里面包含了客戶端的源地址和端口等信息,能夠滿足我們的需求。
proxy protocol報文格式
后端服務(wù)要獲取這個特殊報文也需要在Nginx上配置開啟proxy_protocol協(xié)議,如下圖中所示。
Nginx上配置開啟proxy_protocol協(xié)議
通過wireshark工具抓包我們也發(fā)現(xiàn)包結(jié)構(gòu)和查閱到的資料是一致的,接下來我們要做的就是在MQTT協(xié)議解析的時候把這個特殊的包也要解析處理并保存這個客戶端的真實IP。
Wireshark抓proxy_protocol協(xié)議包
Part 03
實踐操作
說干就干,我們修改MQTT接入服務(wù)的源代碼是基于Netty框架實現(xiàn)的,于是我們在編解碼的時候增加了真實IP解碼器,如下圖所示。
真實IP解碼器
隨后我們在解碼器的decode方法中,將原始報文解析出來,判斷是否有proxy protocol報文,然后解析報文并提取里面的設(shè)備真實源IP地址和端口,并將之保持在Netty中的ChannelAttribute上下文中,方便后續(xù)獲取。
注意,這里的proxy protocol報文和MQTT協(xié)議中的報文是粘包在一起的,所以我們需要提取源地址后將剩余的MQTT協(xié)議包分離處理交給后續(xù)的MQTT協(xié)議解碼器進(jìn)行處理,這就是整體處理流程。
真實IP解碼器源碼
Part 04
測試認(rèn)證
重新打包部署服務(wù)后,我們根據(jù)日志看到通過Nginx負(fù)載方式能夠正常獲取到測試設(shè)備的源IP信息,滿足需求。
對于4層的UDP協(xié)議獲取設(shè)備源IP也可以參考本方案解決。