這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。正向 Proxy是處理一組內網用戶端發往外部機器的網路請求的一種代理方式。實際上,正向 Proxy是你的應用和你所要已連線的服務器之間的中間人。它在 HTTP(S) 協議上起作用,並且被部署在網路設施的邊緣。你通常可以在大型組織或大學中見到正向 Proxy,它被用來進行授權管理或網路安全方面的控制。我發現在使用容器或者動態雲環境工作時,正向 Proxy很有用,因為你會面臨一組伺服器和外部網路的通訊問題。如果你在 AWS、AZure 之類的動態環境下工作,你會擁有一批數量不定的伺服器和一批數量不定的公網 IP。你把應用運行在 Kubernetes 叢集上時也是一樣,容器可能遍布四處。現在假設有客戶讓你提供一個公網 IP 的範圍,因為他需要設定防火牆。你如何提供這個特性呢?這個問題有些情況下很簡單,有些情況下可能非常複雜。2015 年 12 月 1 日,有一位使用者在 [CircleCI 論壇](https://discuss.circleci.com/t/circleci-source-ip/1202)上提了這個問題,並且問題還未關閉。當然,CircleCI 很棒。我只是舉個例子,並非要埋怨他們。解決這個問題的一種可行方法是使用正向 Proxy。你可以讓一組節點以同一靜態IP運轉,然後把清單提供給客戶即可。幾乎所有雲端服務供應商都是這樣做的,比如 DigitalOcean 的浮動 IP(floating IP)、AWS 的彈性 IP(elastic IP)等。你可以通過配置自己的應用來把請求轉寄到這個(代理)池中。這樣,終點的服務所取得的IP就是正向 Proxy節點的IP,而不是內部IP。正向 Proxy可以成為你的網路設施的又一安全層,因為你可以在一個中心化的地方極其方便地掃描和控制內部網路發出來的資料包。正向 Proxy不會帶來單點故障,因為你可以運行多個正向 Proxy服務,他們具有很好的伸縮性。在底層,HTTP 的 `CONNECT` 方法就是一種正向 Proxy。> CONNECT 方法將請求串連轉化為透明 TCP/IP 通道,通常用於在未加密的 HTTP 代理上進行 SSL 加密的通訊(HTTPS)。很多用各種語言寫成的 HTTP 用戶端已經以透明的方式支援這個功能了。在此,我以一個使用 Go 語言和 [privoxy](https://www.privoxy.org/) 的小例子來告訴大家,這很簡單。首先,我們建立一個名為 `whoyare` 的應用。它是一個 HTTP 伺服器,功能是返回你的遠程地址。```gopackage mainimport ("encoding/json""net/http")func main() {http.HandleFunc("/whoyare", func(w http.ResponseWriter, r *http.Request) {w.Header().Set("Content-Type", "application/json")body, _ := json.Marshal(map[string]string{"addr": r.RemoteAddr,})w.Write(body)})http.ListenAndServe(":8080", nil)}```如果用 `GET` 方法訪問路徑 `/whoyare`,你會得到一個類似下面的 JSON 格式的響應:`{"addr": "34.35.23.54"}`,其中 `34.35.23.54` 就是你的公網地址。如果你使用的是膝上型電腦,那麼在終端上發出請求後,你應該會得到 `localhost`的結果。可以用 `curl` 來試一下:18:36 $ curl -v http://localhost:8080/whoyare* TCP_NODELAY set> GET /whoyare HTTP/1.1> User-Agent: curl/7.58.0> Accept: */*>< HTTP/1.1 200 OK< Content-Type: application/json< Date: Sun, 18 Mar 2018 17:36:40 GMT< Content-Length: 31<* Connection #0 to host localhost left intact{"addr":"localhost:38606"}我寫了另外一個程式,它用 `http.Client` 在標準輸出上列印響應。你可以在已經運行了 `whoyare` 服務的前提下運行這個程式:```gopackage mainimport ("io/ioutil""log""net/http""os")type whoiam struct {Addr string}func main() {url := "http://localhost:8080"if "" != os.Getenv("URL") {url = os.Getenv("URL")}log.Printf("Target %s.", url)resp, err := http.Get(url + "/whoyare")if err != nil {log.Fatal(err.Error())}defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)if err != nil {log.Fatal(err.Error())}println("You are " + string(body))}```這是個很簡單的例子,但是你可以將其運用在很多複雜場合。為了讓例子更清楚,我在 DigitalOcean 上建立了兩台虛擬機器:一台運行 `privoxy`,另一台運行 `whoyare`。* whoyare: public ip 188.166.17.88* privoxy: public ip 167.99.41.79Privoxy 是一個易用的正向 Proxy。相比而言,Nginx 和 Haproxy 都不太適合在這種情境下使用,因為它們不支援`CONNECT`方法。我在 Docker Hub 上建立了一個 docker 鏡像,你可以直接運行它,預設使用連接埠 8118。core@coreos-s-1vcpu-1gb-ams3-01 ~ $ docker run -it --rm -p 8118:8118gianarb/privoxy:latest2018-03-18 17:28:05.589 7fbbf41dab88 Info: Privoxy version 3.0.262018-03-18 17:28:05.589 7fbbf41dab88 Info: Program name: privoxy2018-03-18 17:28:05.591 7fbbf41dab88 Info: Loading filter file:/etc/privoxy/default.filter2018-03-18 17:28:05.599 7fbbf41dab88 Info: Loading filter file:/etc/privoxy/user.filter2018-03-18 17:28:05.599 7fbbf41dab88 Info: Loading actions file:/etc/privoxy/match-all.action2018-03-18 17:28:05.600 7fbbf41dab88 Info: Loading actions file:/etc/privoxy/default.action2018-03-18 17:28:05.607 7fbbf41dab88 Info: Loading actions file:/etc/privoxy/user.action2018-03-18 17:28:05.611 7fbbf41dab88 Info: Listening on port 8118 on IP address0.0.0.0第二步,編譯`whoyare`並且把可執行檔用scp傳送到伺服器,可使用以下命令:$ CGO_ENABLED=0 GOOS=linux go build -o bin/server_linux -a ./whoyare應用運行起來之後,我們就可以用 cURL 來直接或者通過 privoxy 發送請求了。直接發送請求如下:$ curl -v http://your-ip:8080/whoyarecURL 使用環境變數`http_proxy`來配置代理進行請求轉寄:$ http_proxy=http://167.99.41.79:8118 curl -v http://188.166.17.88:8080/whoyare* Trying 167.99.41.79...* TCP_NODELAY set* Connected to 167.99.41.79 (167.99.41.79) port 8118 (#0)> GET http://188.166.17.88:8080/whoyare HTTP/1.1> Host: 188.166.17.88:8080> User-Agent: curl/7.58.0> Accept: */*> Proxy-Connection: Keep-Alive>< HTTP/1.1 200 OK< Content-Type: application/json< Date: Sun, 18 Mar 2018 17:37:02 GMT< Content-Length: 29< Proxy-Connection: keep-alive<* Connection #0 to host 167.99.41.79 left intact{"addr":"167.99.41.79:58920"}如你所見,我設定了 `http_proxy=http://167.99.41.79:8118` 之後,響應不再包含我的公網 IP 了,而是代理的 IP。privoxy 處應該會留下如下的請求日誌:2018-03-18 17:28:22.886 7fbbf41d5ae8 Request: 188.166.17.88:8080/whoyare2018-03-18 17:32:29.495 7fbbf41d5ae8 Request: 188.166.17.88:8080/whoyare 你之前啟動並執行用戶端預設串連到 `localhost:8080`,但可以通過設定環境變數 `URL=http://188.166.17.88:8080` 來覆蓋目標地址。運行以下命令可以直接到達 `whoyare`。$ URL=http://188.166.17.88:8080 ./bin/client_linux2018/03/18 18:37:59 Target http://188.166.17.88:8080.You are {"addr":"95.248.202.252:38620"}Go語言的 `HTTP.Client` 包支援一組和代理相關的環境變數,設定這些環境變數可以對運行期間的服務立刻生效,十分靈活。export HTTP_PROXY=http://http_proxy:port/export HTTPS_PROXY=http://https_proxy:port/export NO_PROXY=127.0.0.1, localhost前兩個環境變數很簡單,一個是 HTTP 代理,一個是 HTTPS 代理。`NO_PROXY` 排除了一組主機名稱,當要訪問的主機在這個清單裡的時候,請求不經過代理。我這裡配置的是 `localhost` 和 127.0.0.1。HTT_PROXY=http://forwardproxy:8118 +--------------+ +----------------+ +----------------+ | | | | | | | client +----------^+ forward proxy +--------^+ whoyare | | | | | | | +--------------+ +----------------+ +----^-----------+ | | +---------------+ | | | | | client +-------------------------------------------+ | | +---------------+ HTTP_PROXY not configured 配置了環境變數的用戶端將會通過代理訪問,其他用戶端將直接存取。這個控制粒度很重要。你不僅可以按進程去控制是否經過代理,還可以按請求去控制,十分靈活。$ HTTP_PROXY=http://167.99.41.79:8118 URL=http://188.166.17.88:8080./bin/client_linux2018/03/18 18:39:18 Target http://188.166.17.88:8080.You are {"addr":"167.99.41.79:58922"} 可以看到,我們通過代理到達了 `whoyare`,響應中的 `addr` 是代理的地址。最後一個命令有些怪異,但它只是為了展示 `NO_PROXY` 是如何工作的。我們在設定訪問代理的同時,排除了 `whoyare` 的 URL。正如我們期望的那樣,請求沒有經過代理:$ HTTP_PROXY=http://167.99.41.79:8118 URL=http://188.166.17.88:8080 NO_PROXY=188.166.17.88 ./bin/client_linux2018/03/18 18:42:03 Target http://188.166.17.88:8080.You are {"addr":"95.248.202.252:38712"}本文應作為 Go 語言和正向 Proxy的實用介紹來閱讀。你可以訂閱我的 [rss](https://gianarb.it/atom.xml),或者在 [twitter](https://twitter.com/gianarb)上關注我。興許我以後還會介紹如何用 Go 替代 privoxy 以及如何在 Kubernetes 叢集上部署。所以,快告訴我先寫哪部分吧!
via: https://gianarb.it/blog/golang-forwarding-proxy
作者:gianarb 譯者:vincent08 校對:Unknwon
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
663 次點擊