docker宿主機iptables配置

FANTA_cat發表於2019-01-25

背景

以前伺服器都是直接配置LNMP環境,最近手頭正好有一臺需要重新配置,想嘗試使用docker來配置多PHP版本環境。docker配置十分順利,感謝有明大佬的小冊,和DNMP專案

待解決

伺服器配置好後,在幾天的試用過程中,發現如下兩個問題:

  • NGINX PHP容器中無法獲取request的真實IP
  • PHP容器中無法訪問公網

過程

無法訪問公網,無法獲取真實IP,首先想到了防火牆的問題;關閉iptables,問題解決。可是iptables不能關呀,雖說現在雲伺服器都有安全組過濾,但防火牆是最後一道防線,不能在雲廠商的懷抱裡裸奔啊……

檢視了docker的網路部分,docker暴露容器埠是由docker-proxy來實現的,至於docker-proxy是什麼略過不表。肯定是iptables影響了docker-proxy導致資料包的源ip發生了改變,無法獲取真實IP;無法訪問公網,則是資料包找不到出口,被iptables攔截掉了,在內網轉圈圈

  • 既然docker的各容器處於內網中,於是我想到了iptables的轉發功能。

解決

  • 假設我的公網IP為 117.25.140.71

  • NGINX容器內網IP為 172.18.0.2

  • PHP容器內網IP為 172.18.0.3 172.19.0.3

把公網來的資料包直接轉發給NGINX容器,跳過docker-proxy

新增如下規則

-A PREROUTING -d 117.25.140.71 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80
-A PREROUTING -d 117.25.140.71 -p tcp -m tcp --dport 443 -j DNAT --to-destination 172.18.0.2:443
複製程式碼

重啟iptables 無法獲取真實IP問題解決

將內網的資料包轉發到公網

我的PHP容器訪問公網,需求大抵是傳送簡訊之類(無郵件傳送),用的都是http協議,所以這裡我沒有選擇全部轉發,而是隻轉發目的埠為80和443的資料包

新增如下規則

-A POSTROUTING -s 172.19.0.3 -p tcp -m tcp --dport 80 -j SNAT --to 117.25.140.71
-A POSTROUTING -s 172.19.0.3 -p tcp -m tcp --dport 443 -j SNAT --to 117.25.140.71
複製程式碼

重啟iptables發現仍然無法訪問公網,是不是落下了什麼。。 對了,既然http協議訪問其他域名提供的api服務,怎麼能少了DNS解析呢?新增DNS的支援(注意:DNS解析使用的是udp協議)

補充如下規則

-A POSTROUTING -s 172.19.0.3 -p udp -m udp --dport 53 -j SNAT --to 117.25.140.71
複製程式碼

重啟iptables,大功告成!

最後總結幾點:

  • 之所以能用宿主機iptables實現轉發,是因為宿主機是內網的閘道器
  • 我限制了PHP容器訪問公網的目的埠,如果需要傳送郵件或者訪問8080等其他埠,仍需單獨新增規則或者不限制具體埠號
  • 轉發內網資料包到公網,不要使用地址偽裝(MASQUERADE),否則會影響本文中新增的PREROUTING規則,仍然無法獲取request的真實IP(使用其他方法除外)
  • 如果直接service iptables stop,這篇文章就不用看了。。。

相關文章