iptables和策略路由實現VPN感興趣流的截獲
阿新 • • 發佈:2018-12-22
感興趣流是VPN的術語,說的是需要進行保護的流量,也就是說需要進入VPN隧道的流量,然則仔細推敲之後,發現基於IP層加密的VPN這麼使用“流”的概念是有問題的,因為對於IP,根本不存在流的概念,實質在於IP協議根本就沒有方向。即使這樣,本文還是介紹了一種全網互通的感興趣流的截獲技術。下面是一個拓撲圖:
可以看出,這個拓撲圖中有四個網段,其中VPN端點的每一側都有兩個,兩個網段中的其中一個的資源是需要加密訪問的。雖然圖比較簡單,但是它卻很有代表性,幾乎囊括了所有的訪問情形,那就是從任意網段加密或者明文訪問任意的其它網段。那麼怎樣定義一個規則從而實現感興趣流的截獲呢?仔細思考這個問題就會發現它實際上並沒有想象的那麼簡單,因為在同一個位置IP層無法區分一個數據包是到加密網段的需要加密的包還是從加密網段發起的到非加密網段的訪問的返回包。因此必然需要一定的傳輸層資訊,使用基於五元組的conntrack機制才可以準確區分這二者。
對於IPSec,是通過配置複雜的策略資料庫來實現的,具體可以參見FreeSwan的實現框圖。而對於OpenVPN,我們可以使用更加靈活的策略路由來實現,當然僅僅使用策略路由是不夠的,還要使用conntrack模組,具體來講就是使用mangle表的規則來對一些資料包打上mark,然後根據這些mark定位策略路由表。整個過程所使用的工具就是經典強大的iproute2以及iptables 。
我使用ip_conntrack的ctstate這個match來區分下面兩類資料包:
1.源自任意地點經由此地到加密網段的訪問包。
2.源自任意地點經由此地的到達非加密網段訪問包的返回包。
ip_conntrack為一個流儲存了一組狀態,通過狀態機來切換這些狀態,本文中我們所使用到的有兩個狀態,分別是NEW和ESTABLISHED。其中NEW狀態表示基於五元組進行連線跟蹤的一個流的第一個包,而ESTABLISHED狀態則標示一個流的反方向的第一個以及後續兩個方向的所有包(這裡沒有考慮INVALID情況,也沒有考慮過期)。也就是說,當發起一個請求時,資料包到達VPN端點的時候,其ctstate一定是NEW,而此時可以捕獲其目的地址,根據目的地址是否是加密網段而進行走隧道還是走明文的抉擇,另外一種情況,如果是訪問加密網段的返回包,那麼也是要加密走隧道的,而此時該包到達VPN的另一個端點時,其ctstate已經是ESTABLISHED的了,因此可以捕獲其源地址,如果源地址是加密網段,ctstate為ESTABLISHED,那麼也是要加密的,其餘的則全部明文放過。對於兩個或者多個VPN端點,都是如此配置即可,因此我們為整個VPN所要做的,僅僅是知道到達哪些網段的流量需要加密即可,然後把上述的文字組織成指令碼即可。
本方法使用OpenVPN這種VPN再合適不過了,因為OpenVPN有豐富的介面和事件指令碼和外部網路事件聯動,同時其推送能力也會最大限度的簡化配置。如此一來,你就不必像配置IPSec VPN那樣,非常對稱的在兩端同時進行配置,而只需要在OpenVPN伺服器端進行統一的配置即可,所有的客戶端的配置都是可以推送下去的。這也是非對稱的,C/S模式的OpenVPN的絕佳舞臺。
以下的配置在所有的VPN端點上進行,對於OpenVPN伺服器端,直接配置,對於OpenVPN客戶端,相關資訊由OpenVPN伺服器的push route指令以及push setenv-fase指令進行推送。
增加一個新的策略路由表:
echo 100 vpn >> /etc/iproute2/rt_tables
配置兩條規則打mark:
iptables -t mangle -A PREROUTING -i $連線內部網路的固定入口 -m conntrack --ctstate NEW -d $dst/$dst_mask -j MARK --set-mark 100
iptables -t mangle -A PREROUTING -i $連線內部網路固定入口-m conntrack --ctstate ESTABLISHED -s $src/$src_mask -j MARK --set-mark 100
配置策略路由策略:
ip rule add fwmark 100 table vpn
在策略路由表中增加路由:
ip route add $加密網段/$加密網段掩碼 via $加密網段VPN端點對應的OpenVPN虛擬IP table vpn
…可新增多條
到此,我們就可以實現任意網段到任意網段的感興趣流量的截獲-走策略路由表,以及不感興趣流量的放行-走標準主路由表。
通過配置過程可看出這種方案和以往的其它的方案有一個不同點,那就是截獲過程和VPN是獨立的,通過策略路由表和VPN系統耦合,實際上Linux網路中,使用conntrack mark的並不僅僅只有策略路由。使用Linux網路就這點好,幾乎所有配置工具完全遵守KISS原則,做好且只做好一件事,這樣配置就可以非常靈活,代價就是需要你自己來構思如何排列組合這些配置來實現你自己的方案。
在本文介紹的截獲方式中,隱含使用了ip_conntrack模組,很多人對這個東西不是很喜歡,因為第一怕它會滿,第二怕它影響效率。其實這都是杞人憂天。在64位系統你設定ip_conntrack最大數量為655360足矣,即使32位系統超過1G的實體記憶體,你也可以設定為100000,不要指望你的Linux閘道器會有絕佳的表現,使用Linux和同類的系統相對PK一下還可以,追求絕對的表現都是浮雲,因此第二個擔心也不是什麼問題,效能?怕影響效能,能影響多少?一切以程式碼為先的程式設計師應該相信ip_conntrack的雜湊演算法或者會調整核心引數使之最優化才好。當然,如果你的預算允許,那麼直接上兩中指(Cisco),什麼問題都解決了。
每當寫一篇文章,最後我難免要感慨一番,實際上這種感慨我認為很有必要,看過《古文觀止》之類古文集的傢伙應該都知道,每一篇的最後都會有嗚呼…,嗟夫…,之類的感慨。作為技術研發人員,每看到一個問題,自己就一定要想辦法解決它,哪怕方案再拙劣,起碼成一家之言,在解決了之後,不要混雜其它的疑問,更不要挑刺,起碼自己完成了功能,實現了效果,即便它帶來了新的問題或者新的不確定性。如果真的有新的問題和不確定性,那麼就引出另一個問題,然後解決之,千萬不要將許多問題雜糅在一起,時刻注意你的目的是什麼,既然目的是網路的互通性,那麼就先不要考慮效率,至於優化,先把互通性搞定再說。
總結:
本文介紹了一種方法,可是實現任意網路之間的任意流量型別的截獲,注意,這種截獲是雙向的,其實現利用了Linux的ip_conntrack的ctstate這個match模組,配置策略路由來完成,體現了Linux網路的強大性。雖然我沒有給出完整的測試拓撲以及配置檔案,但是由於原理非常簡單,操作起來也不會很困難。正好家裡還屯著幾塊老舊的板子,這樣就可以自己DIY一臺網關了,再加入一些NAT以及防火牆的功能,其效果想必不比買到的TPLink家用路由器要差,反而還會好很多。具體的DIY過程可以參見《Linux network cookbook》這本書,該書講解的很清晰,知識點也很簡單,很適合DIY。
關於DIY:
如果真的想自己DIY一臺自己的家用mini裝置,可以實現從世界各地接入且策略性的訪問加密或者明文資源,那麼推薦看一下FreeSCO以及Endian這兩個,其中前者更加靈活一些,在這二者的基礎上,你也可以自己實現一個和它們都不同的小型閘道器。
可以看出,這個拓撲圖中有四個網段,其中VPN端點的每一側都有兩個,兩個網段中的其中一個的資源是需要加密訪問的。雖然圖比較簡單,但是它卻很有代表性,幾乎囊括了所有的訪問情形,那就是從任意網段加密或者明文訪問任意的其它網段。那麼怎樣定義一個規則從而實現感興趣流的截獲呢?仔細思考這個問題就會發現它實際上並沒有想象的那麼簡單,因為在同一個位置IP層無法區分一個數據包是到加密網段的需要加密的包還是從加密網段發起的到非加密網段的訪問的返回包。因此必然需要一定的傳輸層資訊,使用基於五元組的conntrack機制才可以準確區分這二者。
對於IPSec,是通過配置複雜的策略資料庫來實現的,具體可以參見FreeSwan的實現框圖。而對於OpenVPN,我們可以使用更加靈活的策略路由來實現,當然僅僅使用策略路由是不夠的,還要使用conntrack模組,具體來講就是使用mangle表的規則來對一些資料包打上mark,然後根據這些mark定位策略路由表。整個過程所使用的工具就是經典強大的iproute2以及iptables 。
我使用ip_conntrack的ctstate這個match來區分下面兩類資料包:
1.源自任意地點經由此地到加密網段的訪問包。
2.源自任意地點經由此地的到達非加密網段訪問包的返回包。
ip_conntrack為一個流儲存了一組狀態,通過狀態機來切換這些狀態,本文中我們所使用到的有兩個狀態,分別是NEW和ESTABLISHED。其中NEW狀態表示基於五元組進行連線跟蹤的一個流的第一個包,而ESTABLISHED狀態則標示一個流的反方向的第一個以及後續兩個方向的所有包(這裡沒有考慮INVALID情況,也沒有考慮過期)。也就是說,當發起一個請求時,資料包到達VPN端點的時候,其ctstate一定是NEW,而此時可以捕獲其目的地址,根據目的地址是否是加密網段而進行走隧道還是走明文的抉擇,另外一種情況,如果是訪問加密網段的返回包,那麼也是要加密走隧道的,而此時該包到達VPN的另一個端點時,其ctstate已經是ESTABLISHED的了,因此可以捕獲其源地址,如果源地址是加密網段,ctstate為ESTABLISHED,那麼也是要加密的,其餘的則全部明文放過。對於兩個或者多個VPN端點,都是如此配置即可,因此我們為整個VPN所要做的,僅僅是知道到達哪些網段的流量需要加密即可,然後把上述的文字組織成指令碼即可。
本方法使用OpenVPN這種VPN再合適不過了,因為OpenVPN有豐富的介面和事件指令碼和外部網路事件聯動,同時其推送能力也會最大限度的簡化配置。如此一來,你就不必像配置IPSec VPN那樣,非常對稱的在兩端同時進行配置,而只需要在OpenVPN伺服器端進行統一的配置即可,所有的客戶端的配置都是可以推送下去的。這也是非對稱的,C/S模式的OpenVPN的絕佳舞臺。
以下的配置在所有的VPN端點上進行,對於OpenVPN伺服器端,直接配置,對於OpenVPN客戶端,相關資訊由OpenVPN伺服器的push route指令以及push setenv-fase指令進行推送。
增加一個新的策略路由表:
echo 100 vpn >> /etc/iproute2/rt_tables
配置兩條規則打mark:
iptables -t mangle -A PREROUTING -i $連線內部網路的固定入口 -m conntrack --ctstate NEW -d $dst/$dst_mask -j MARK --set-mark 100
iptables -t mangle -A PREROUTING -i $連線內部網路固定入口-m conntrack --ctstate ESTABLISHED -s $src/$src_mask -j MARK --set-mark 100
配置策略路由策略:
ip rule add fwmark 100 table vpn
在策略路由表中增加路由:
ip route add $加密網段/$加密網段掩碼 via $加密網段VPN端點對應的OpenVPN虛擬IP table vpn
…可新增多條
到此,我們就可以實現任意網段到任意網段的感興趣流量的截獲-走策略路由表,以及不感興趣流量的放行-走標準主路由表。
通過配置過程可看出這種方案和以往的其它的方案有一個不同點,那就是截獲過程和VPN是獨立的,通過策略路由表和VPN系統耦合,實際上Linux網路中,使用conntrack mark的並不僅僅只有策略路由。使用Linux網路就這點好,幾乎所有配置工具完全遵守KISS原則,做好且只做好一件事,這樣配置就可以非常靈活,代價就是需要你自己來構思如何排列組合這些配置來實現你自己的方案。
在本文介紹的截獲方式中,隱含使用了ip_conntrack模組,很多人對這個東西不是很喜歡,因為第一怕它會滿,第二怕它影響效率。其實這都是杞人憂天。在64位系統你設定ip_conntrack最大數量為655360足矣,即使32位系統超過1G的實體記憶體,你也可以設定為100000,不要指望你的Linux閘道器會有絕佳的表現,使用Linux和同類的系統相對PK一下還可以,追求絕對的表現都是浮雲,因此第二個擔心也不是什麼問題,效能?怕影響效能,能影響多少?一切以程式碼為先的程式設計師應該相信ip_conntrack的雜湊演算法或者會調整核心引數使之最優化才好。當然,如果你的預算允許,那麼直接上兩中指(Cisco),什麼問題都解決了。
每當寫一篇文章,最後我難免要感慨一番,實際上這種感慨我認為很有必要,看過《古文觀止》之類古文集的傢伙應該都知道,每一篇的最後都會有嗚呼…,嗟夫…,之類的感慨。作為技術研發人員,每看到一個問題,自己就一定要想辦法解決它,哪怕方案再拙劣,起碼成一家之言,在解決了之後,不要混雜其它的疑問,更不要挑刺,起碼自己完成了功能,實現了效果,即便它帶來了新的問題或者新的不確定性。如果真的有新的問題和不確定性,那麼就引出另一個問題,然後解決之,千萬不要將許多問題雜糅在一起,時刻注意你的目的是什麼,既然目的是網路的互通性,那麼就先不要考慮效率,至於優化,先把互通性搞定再說。
總結:
本文介紹了一種方法,可是實現任意網路之間的任意流量型別的截獲,注意,這種截獲是雙向的,其實現利用了Linux的ip_conntrack的ctstate這個match模組,配置策略路由來完成,體現了Linux網路的強大性。雖然我沒有給出完整的測試拓撲以及配置檔案,但是由於原理非常簡單,操作起來也不會很困難。正好家裡還屯著幾塊老舊的板子,這樣就可以自己DIY一臺網關了,再加入一些NAT以及防火牆的功能,其效果想必不比買到的TPLink家用路由器要差,反而還會好很多。具體的DIY過程可以參見《Linux network cookbook》這本書,該書講解的很清晰,知識點也很簡單,很適合DIY。
關於DIY:
如果真的想自己DIY一臺自己的家用mini裝置,可以實現從世界各地接入且策略性的訪問加密或者明文資源,那麼推薦看一下FreeSCO以及Endian這兩個,其中前者更加靈活一些,在這二者的基礎上,你也可以自己實現一個和它們都不同的小型閘道器。