1. 程式人生 > >OpenVPN的高階路由技術-內部路由

OpenVPN的高階路由技術-內部路由

               

1.server模式以及點對點模式的OpenVPN

前文好幾次說過,雖然OpenVPN也可以建立隧道,該隧道封裝了IP資料報或者以太幀,然而卻和使用IPSec VPN的網路拓撲無法做到相容,這是因為在網對網模式下,預設配置下,使用tun虛擬網絡卡模式的OpenVPN的客戶端虛擬網絡卡上要強制做SNAT,否則便通不過OpenVPN伺服器端的檢查。這是因為OpenVPN伺服器是根據分配給客戶端的虛擬IP地址來維護客戶端和自己儲存的session(也就是multi_instance)的對應關係的。為了定位是哪個客戶端發來了封裝的資料包,OpenVPN需要解析出封裝載荷中的源IP地址,從而找到和自己儲存的multi_instance的對應關係。初看起來,這好像是OpenVPN伺服器設計時端偷了一個懶,然而下一節會說明,事實並不是那樣。        實際上,一般認為萬萬不該使用帶內資料來定位session。OpenVPN這麼做了,於是它將抹去真實客戶端的IP地址(僅限TUN模式),所有的客戶端網路內的資料發起者的IP地址都將偽裝成OpenVPN客戶端的虛擬IP地址。實際上,完全可以使用客戶端提交的證書資訊生成一個唯一的鍵值,用這個鍵值來標識具體客戶端,控制資料永遠不要和真實資料雜糅(一個例外是xml檔案包含base64編碼的情景,可是巧的是,base64編碼原則保證它永遠不會編碼到”<”,”>”等字元)。然而這不容易做到。我們來看看這是為什麼。        本質原因就是OpenVPN協議的設計缺陷--姑且先這麼說吧,在OpenVPN的協議頭中,絲毫找不到任何可以定位多個安全session中一個的資訊,而這個資訊在IPSec的封裝比如ESP頭中是有的,由SPI來標識。對於OpenVPN的協議頭中(OpenVPN協議解析-網路結構之外),只有16位長度,5位操作碼以及3位的和sesion軟切換相關的key ID,並沒有什麼能標識該封裝屬於哪個安全session,安全session都在在外部維護的,因此必須在資料封裝中找一個欄位能標識它屬於哪個session,也就是屬於哪個multi_instance。最終確定的就是載荷中的原始IP地址。這麼做當然很簡單,但是為server模式下的網對網擴充套件製造了困難。        雖然完全在socket和reliablity層,multi_instance的context中的tls_multi已經可以定位是哪個客戶端了,但是這些都是在OpenVPN資料包封裝外部維護的,因此正如程式碼註釋中所說,要確保這個封裝載荷確實屬於“那個客戶端”,這也是防止地址欺騙的一種方式,後面會談到,必須通過複雜的配置才能做到網對網的透明互聯。        不要把OpenVPN想成什麼太偉大的東西,它有缺陷是超級正常的,本著實用主義和拿來主義的原則,難道這個真的很重要麼?只要夠我們用就可以了吧。還好,如果你非要用最簡單的方式實現和IPSec的網路拓撲相容,那麼OpenVPN也不是做不到,那就是它的點對點模式。我們之前說的都是它常用的server模式,對於點對點模式,是無需檢查載荷的源地址的,和IPSec VPN的拓撲是相容的,但是卻犧牲了多對一的良好擴充套件性。        事實上,server模式預設本來就不是讓你用於網對網拓撲的,而是適用於主機對網路的拓撲,換句話說,一般而言,這種模式的OpenVPN客戶端一般都是直接安裝於主機之上,而不是安裝於路由器或者帶有轉發功能的伺服器上的。對於點對點模式,OpenVPN的伺服器和OpenVPN的客戶端唯一的確定了一條隧道,此時也就不必再檢查載荷中的源IP地址到底是不是這個客戶端的了。確定自己的需求,配置不同的引數選項即可讓OpenVPN為你服務,這也就是OpenVPN的強大之處。

2.OpenVPN的高階路由技術

如果你只知道OpenVPN在伺服器端會對資料載荷的源地址進行檢查從而確定它一定是OpenVPN的虛擬地址的話,那麼你將喪失掉OpenVPN的一大部分功能。實際上OpenVPN可以通過較為複雜的配置做到透明的網對網的拓撲,但是預設情況下不行。這個也許就是OpenVPN勝出IPSec的地方吧,上一節說OpenVPN沒有把session相關的資訊封裝於OpenVPN的協議頭中是一種協議設計的缺陷,然而它這麼做也許就是想將靈活性留給配置,這樣看來,這也是它的一大優勢之所在。如果你看上了server模式的優勢,並且一定要使用該模式而不選擇使用點對點模式的話,你就必須需要進行相對複雜的配置。        IPSec預設就是網對網的透明互聯,而OpenVPN卻需要複雜的配置,這就是它們的不同之所在。下面說一下OpenVPN的路由原理以及如何進行具體的配置

2.1.TAP模式虛擬網絡卡對源地址的檢查以及路由配置思路

最好的資源是原始碼。在server模式下,multi.c檔案處理資料包的路由,OpenVPN封裝的資料到達伺服器端的時候,multi_process_incoming_link函式處理之,tap情況下,OpenVPN解析載荷包的源MAC地址而不是IP地址,它必須通過檢查才可以,這個檢查可以通過使用者定義的plugin或者script來完成,預設情況下,源MAC地址必須是分配給該客戶端的虛擬網絡卡的MAC地址。然而同時也允許客戶端修改自己虛擬網絡卡的MAC地址,這種情況發生在MAC地址衝突的情況下。修改過的地址具體能否通過檢查,就要看伺服器端的plugin或者script的策略了。        在tap模式下,OpenVPN所在的節點可以看做是一臺網橋,這種情況下,IP包都是透傳的,不管源IP是什麼,OpenVPN伺服器都是不管的。        因此,tap模式下,IP方式的網對網拓撲是可行的。最關鍵的是新增路由,只要有路由,資料通訊就可能。OpenVPN客戶端是一臺網橋,同時它也是一臺路由器,其上需要配置到達OpenVPN伺服器側網路的路由,而這個路由一般都是OpenVPN伺服器推送下去的。我們看看TAP模式下如何來進行配置:

2.2.TUN模式虛擬網絡卡對源地址的檢查以及路由配置思路

同樣在multi_process_incoming_link函式中,tun模式對載荷資料包源地址的檢查是針對IP地址進行的而不是MAC地址。對於IP網際網路絡而言,這就需要更多的配置來讓執行邏輯通過檢查。預設情況下,載荷資料包的源IP地址必須是OpenVPN伺服器分配給該客戶端的虛擬IP地址。        綜合2.1和2.2我們發現,針對載荷源地址的檢查其實是在OpenVPN執行使用者接入控制之後的又一層檢查,該檢查一般可以用於和防火牆的聯動。        下面看一下程式碼是怎麼處理這個檢查的。在函式multi_process_incoming_link中,如果判斷是TAP網絡卡模式,那麼呼叫mroute_extract_addr_from_packet解析出載荷的源地址,解析的過程在函式mroute_extract_addr_ether中:
memcpy (src->addr, eth->source, 6);
可見解析出來的是以太頭的MAC地址。再接下來會呼叫:
if (multi_learn_addr (m, m->pending, &src, 0) == m->pending)
來判斷是否可以為源地址為src的載荷發起者轉發該資料包。在判斷中會呼叫使用者配置的plugin或者script來協助抉擇。 

2.3.如何配置TUN模式的OpenVPN從而通過檢查

在得到配置之前,首先要理解一個OpenVPN中的引數選項iroute,解釋起來就是internal route,其實就是獨立於系統路由之外的OpenVPN的路由,該路由起到了訪問控制的作用,特別是是在多對一即server模式的OpenVPN拓撲中,該機制可以在防止地址欺騙的同時更加靈活的針對每一個接入的客戶端進行單獨配置。在多對一的情況下,必須要有機制檢查訪問內網資源的使用者就是開始接入的那個使用者,由於OpenVPN是第三層的VPN,而且基於獨立於OpenVPN程序之外的虛擬網絡卡,那麼一定要防止單獨的客戶端盜用其它接入客戶端的地址的情況。在特定客戶端的上下文中配置iroute選項,它是一個ip子網,預設是客戶端虛擬ip地址掩碼是32位,你可以在保證路由以及IP地址不混亂的前提下任意配置它,OpenVPN僅僅讓載荷資料包的源IP地址在iroute選項中配置的子網內的主機通過檢查,其它資料載荷一律drop。比如客戶端虛擬IP地址是172.16.0.2,而OpenVPN伺服器針對該客戶端的iroute引數是10.0.0.0/24,那麼只要載荷資料包的源IP地址在10.0.0.0/24這個子網中,一律可以通過檢查。        iroute是OpenVPN內部維護的一個路由,它主要用於維護和定位多個客戶端所在的子網以及所掛接的子網,鑑於此,OpenVPN對所謂的網對網拓撲的支援其實超級靈活,它能做到這個虛擬專用網到哪裡終止以及從哪裡開始。        對於TUN模式的虛擬網絡卡構建的VPN,原始碼裡是這樣說的(還是在multi_process_incoming_link函式中):
else if (multi_get_instance_by_virtual_addr (m, &src, true) != m->pending)…
在multi_get_instance_by_virtual_addr裡面實現判斷是否可以轉發。其引數src是載荷包的源IP地址,在multi_get_instance_by_virtual_addr函式中,有以下的迴圈:
for (i = 0; i < rh->n_net_len; ++i) {tryaddr = *addr;tryaddr.type |= MR_WITH_NETBITS;    tryaddr.netbits = rh->net_len[i];    mroute_addr_mask_host_bits (&tryaddr); /* look up a possible route with netbits netmask */    route = (struct multi_route *) hash_lookup (m->vhash, &tryaddr);  if (route && multi_route_defined (m, route)) {        /* found an applicable route, cache host route */        struct multi_instance *mi = route->instance;        multi_learn_addr (m, mi, addr, MULTI_ROUTE_CACHE|MULTI_ROUTE_AGEABLE);        ret = mi;        break;     }}
該迴圈就是判斷特定客戶端是否配置了iroute引數指定的內部路由,如果配置了,那麼載荷包的源IP地址是否在iroute引數指定的子網內,如果是,則向tun網絡卡轉發該包,如果不是,則丟棄之。舉個例子就是,如果OpenVPN伺服器中配置一個特定客戶端的iroute引數為:10.0.0.0/24192.168.0.0/24102.168.1.0/24那麼只要載荷包的源IP地址在這些網段內,都可以被OpenVPN轉發。我們看看TUN模式下如何來進行配置:

2.4.為何說網對網通透拓撲的配置很複雜

上文看下來,配置也不算太複雜。然而複雜性不在於配置本身,而在於和UI的聯動。沒有幾個人會在使用的時候手工去進行配置,少數幾個這樣做的人幾乎都是技術人員,並且要精通IP路由技術,精通防火牆技術等。大多數的人只希望通過人性化設計的配置介面來進行配置,那麼介面如何來配置這些引數,就需要大量的自動化指令碼,編寫這些指令碼並且做到沒有漏洞,不重複,無缺漏,其本身就是一件很有工作量的事情。

2.5.OpenVPN的redirect-gateway選項

作為一個第三層的VPN,OpenVPN推送的路由很容易和本地路由衝突從而造成資料包環路,要知道的是,OpenVPN實施的路由和系統的IP路由完全一樣,在協議棧層面上看,沒有任何區別,況且,操作OpenVPN的人大多數又沒有操作Cisco裝置的人的那種資質,因此把路由搞混亂從而導致網路down掉是常見的事。        有時候,手工配置路由的過程反而比通過UI來配置來的更清晰,然而這僅僅是針對對IP路由有著很深刻理解的人員來說的。OpenVPN作為一款通用的VPN,完全可以為使用者著想,努力去避免路由配置失誤導致的路由環路。下面舉一個例子來說明路由環路:

客戶端所在網段:192.168.1.0/24  預設閘道器:192.168.1.1

伺服器外網口所在網段:192.168.2.0/24伺服器端的OpenVPN的push route配置:route 192.168.3.0/24;route 192.168.1.0/24…我們發現,伺服器將客戶端自己的網段也推送下來了,這樣難道不會造成混亂麼,本來要通過客戶端的預設閘道器定址到OpenVPN的真實地址或者通過直連路由定址到直連網段的主機,現在一律要走虛擬網絡卡了,而虛擬網絡卡出來的包最終經過封裝後要由192.168.1.0/24網段的物理網絡卡出去的,出去時由於路由指向虛擬網絡卡,包又一次進入虛擬網絡卡…結果資料包一直在累計,越來越大,就是發不出去,在本機的192.168.1.0/24網段的物理網絡卡和虛擬網絡卡之間環來環去…        除了上述的問題,還有一個問題涉及到安全方面,那就是一旦連線到VPN,客戶端主機就要斷開其它不安全的網路連線,以防不安全因素通過VPN隧道滲入內網,那就要阻斷這些不安全的網路,最直接的方式就是刪去到達不安全連線的路由。幸運的是,OpenVPN可以解決這個問題。        redirect-gateway選項解決了這個問題,它將客戶端的預設閘道器指向了虛擬專用網,也就是VPN隧道本身。然而它並沒有很魯莽的刪除掉原來的預設閘道器,而是首先在原始的路由表中查詢到到達OpenVPN伺服器的路由,然後根據查詢結果OpenVPN新增一條到達OpenVPN伺服器的主機路由,第二步就是刪除原來的預設路由(刪除前儲存一份,以備恢復),這樣的話,預設閘道器就指向VPN隧道了,既阻斷了不安全網路,又保證了OpenVPN伺服器的可達性。當然,這個redirect-gateway選項還有自身的引數,定製通過哪種方式來操作上述步驟,都比較簡單,可以查閱man手冊。

4.    總結

OpenVPN是一個極其複雜又極其靈活的VPN,它開放了甚多的網路配置介面,比如IP路由的介面,它可以通過事件機制對VPN隧道狀態進行實時的監控和保持更新。OpenVPN將網路層的諸多操作集於自身,同時又將策略留給配置人員,保持了極大的靈活性,在強大的功能和配置的簡潔之間找到了一個極佳的平衡點。OpenVPN在橫向拓撲上支援server模式和點對點模式,在縱向協議上支援二層模式和三層模式,可以通過配置進行任意的擴充套件。實際上,它可以模擬任意的網路拓撲,滿足你的任意需求,當然,功能強大的代價就是配置的複雜,所謂的配置的複雜並不單單是指如何可以手工的配置這些,而更多的是指如何可以保持以及維護這些配置,根據現實環境的變化靈活的更改配置,在整個過程中,最小化工作量。