OpenVPN有了可以推送的setenv以及反向的push-peer-info之後,理論上可以實現任意資訊的雙向推送,這實在太方便了,然而方便是需要付出代價的,那就是容易混亂和出錯以及帶來安全性的缺失。這個世界上,有兩個相互敵視力的群體一直存在著,一個是如何使得節點更加互連,另一個就是如何針對通訊進行存取控制。換句話就是有人希望人人都能互連,有人卻希望人與人之間的通訊受到控制,這也是電腦網路的現狀。正好OpenVPN作為一個虛擬網路實現,它類比了一台網路裝置,該裝置也同樣要扮演上述的兩類角色中的之一角。
OpenVPN之所以沒有雷厲風行的實現push-peer-info正是糾結於上述兩個共存的矛盾,你希望開啟所有人家的門,但是卻不想讓任何人開啟你家的門,對於OpenVPN,它同時扮演了兩類角色,因為它既有用戶端又有伺服器。拋開用戶端的push-peer-info不談,因為這個在以前的文章中已經談過了,下面說說伺服器端可以push的setenv會有什麼問題。用一句話說,那就是這樣對於用戶端而言是不安全的。
不安全的因素何在?我們知道OpenVPN內建了豐富的事件介面可以為外部程式聯動,我們可以使用plugin或者script來做到這些。我們知道,在常見的作業系統中,每一個可以執行程式都用一些所謂的環境變數,這體現在main函數的第三個參數,如果熟悉exec系統調用也可以從其參數中看出這點,這些環境變數影響了程式的行為,如果這麼說有點抽象,那麼就具體說一個環境變數LD_PRELOAD,這是一個很危險的環境變數,它可以更改系統庫函數的行為到你自訂的同名庫函數,說白了就是提供了一個系統庫函數重載的介面,因此該環境變數在靈活的背後也隱藏了很多危險。具體可以參見這篇博文:《警惕UNIX下的LD_PRELOAD環境變數》。如果OpenVPN伺服器端push過來這麼一個LD_PRELOAD環境變數,然後用戶端執行了一個script或者在其plugin中exec了一個外部程式,那麼用戶端將面臨被劫持的危險。
OpenVPN本來是一個讓網路更安全的軟體,它決不能做這種壞事,那麼它是怎麼解決的呢?OpenVPN為每一個option都綁定了一個permission用於區分這個option是自己配置的還是對端push過來的,對於某些具有潛在危險的option,比如setenv,將在apply對端push的option的時候,在permission中體現為deny,這樣就不再接受這個option。然而同時又不能為了安全性而損失靈活性,因此OpenVPN提供了一個setenv-safe這個可以安全push到對端的option,它為什麼安全呢?因為它在你push到對端的環境變數前面加了一個magic值,很簡單,就是一個字串“OPENVPN_”,即使你push了一個LD_PRELOAD,OpenVPN也會將其改為OPENVPN_LD_PRELOAD,而作業系統是不認識這個環境變數的,從而保證了安全。
有很多關於OpenVPN為何這麼設計而不那麼設計的爭論,其實光看代碼根本找不到答案,有時候一些蛛絲馬跡還得從代碼注釋,Changelog或者其maillist中去尋找。