Routing

Docker VPN 將內部流量路由到外部可用的容器

  • February 27, 2020

我有一個執行多個 docker 容器的 VPS。我有一個 nextcloud 實例,它通過 nginx 代理(來自 Let’sEncrypt 的證書)終止 SSL/TLS。我有一個 openvpn 容器。在它的 docker 網路中,我還託管更多服務(自己的綁定 dns 伺服器和 git 伺服器),我可以通過 VPN 訪問這些服務。

現在我還想通過 VPN 訪問我的 nextcloud 實例。最初我認為這不會是一個問題,因為可以通過網際網路訪問 nextcloud 實例,並且 VPN 也可以連接到網際網路。但不幸的是,我無法達到它。如果我通過 VPN 捲曲我的伺服器(http 或 https),我會得到“埠 80/443:無主機路由”。在不連接 VPN 的情況下,連接可以正常工作。

如果我使用 traceroute,我可以看到它正確地到達了我的 VPS 的公共 IP。所以我得出結論,這是路由的問題。以我的 VPS 的公共 IP 上的埠 80/443 為目標的流量不會被轉發/路由到 nginx 代理容器(暴露了上述埠)。

據我了解,docker 使用 firewalld/iptables 在容器之間路由流量。因此,其他規則適用於 VPN 流量,而不是來自網際網路的流量。**我需要如何配置,以便將到我的公共 IP 地址的 VPN 流量(伺服器內部)正確轉發到相應的容器?**我想在 VPN 和無 VPN 狀態之間保持恆定/不變的連接,這樣我的 Nextcloud 應用程序就不會感到困惑。

我嘗試過的: 我嘗試了一種解決方法的可能性。我可以在我的 VPN DNS 伺服器中為我的 nextcloud 實例添加一個自己的 DNS 條目,該條目指向 nextcloud 應用程序容器的 IP(我將在其中失去 SSL/TLS 終止)或 nginx 代理的 IP。在最後一種情況下,nginx 代理不會將流量轉發到 nextcloud 容器,因為它使用不同的主機名。如果可能的話,我想保持代理配置不變,因為它是在容器啟動時/從letsencrypt伴隨容器自動填充的。此外,證書與使用的 FQDN 不匹配。如果我嘗試使用我的真實/公共 DNS 名稱添加主區域(以便我可以使用與外部相同的 FQDN),則該 TLD 中的所有其他域不再被轉發(是否有可能配置綁定為了那個原因?)。

**TL;DR:**從 docker 容器到公共 VPS IP 地址的流量不會像來自外部的流量那樣轉發到正確的 docker 容器。

如果您還需要有關使用的容器的更多資訊,我將添加連結和我的 docker-compose 文件。

編輯:

[root@XXXXXXXX ~]# iptables -S FORWARD
-P FORWARD ACCEPT
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A FORWARD -o br-7e5cecc96f4a -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-7e5cecc96f4a -j DOCKER
-A FORWARD -i br-7e5cecc96f4a ! -o br-7e5cecc96f4a -j ACCEPT
-A FORWARD -i br-7e5cecc96f4a -o br-7e5cecc96f4a -j ACCEPT
-A FORWARD -o br-fd56ce52983e -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-fd56ce52983e -j DOCKER
-A FORWARD -i br-fd56ce52983e ! -o br-fd56ce52983e -j ACCEPT
-A FORWARD -i br-fd56ce52983e -o br-fd56ce52983e -j ACCEPT
-A FORWARD -o br-f1ef60d84b48 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-f1ef60d84b48 -j DOCKER
-A FORWARD -i br-f1ef60d84b48 ! -o br-f1ef60d84b48 -j ACCEPT
-A FORWARD -i br-f1ef60d84b48 -o br-f1ef60d84b48 -j ACCEPT
-A FORWARD -o br-b396aa5a2d35 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-b396aa5a2d35 -j DOCKER
-A FORWARD -i br-b396aa5a2d35 ! -o br-b396aa5a2d35 -j ACCEPT
-A FORWARD -i br-b396aa5a2d35 -o br-b396aa5a2d35 -j ACCEPT
-A FORWARD -o br-83ac9a15401e -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-83ac9a15401e -j DOCKER
-A FORWARD -i br-83ac9a15401e ! -o br-83ac9a15401e -j ACCEPT
-A FORWARD -i br-83ac9a15401e -o br-83ac9a15401e -j ACCEPT
-A FORWARD -d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT
-A FORWARD -i virbr0 -o virbr0 -j ACCEPT
-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i lo -j ACCEPT
-A FORWARD -j FORWARD_direct
-A FORWARD -j FORWARD_IN_ZONES_SOURCE
-A FORWARD -j FORWARD_IN_ZONES
-A FORWARD -j FORWARD_OUT_ZONES_SOURCE
-A FORWARD -j FORWARD_OUT_ZONES
-A FORWARD -m conntrack --ctstate INVALID -j DROP
-A FORWARD -j REJECT --reject-with icmp-host-prohibited

預設情況下,Docker 不允許連接到不同網橋的任何兩個容器之間的流量。而且它還不允許從容器到已由 docker 本身映射到外部的埠的流量。這都是用iptables實現的。

首先,埠到外部的映射也發生在iptables中。它使用natDNAT中的規則。對於這些規則,Docker 創建了一個單獨的鏈,以便在nat表中應用相同的規則。規則前面有過濾掉來自 Docker 網橋的所有流量的跳轉。所以這是第一個障礙。DOCKER``PREROUTING``OUTPUT``DNAT``RETURN

它看起來有點像這樣:

-A DOCKER -i br-one -j RETURN
-A DOCKER -i br-two -j RETURN
-A DOCKER ! -i br-one -p tcp -m tcp --dport EXPOSEDPORT -j DNAT --to-destination 172.17.0.2:INTERNALPORT

如果您僅將埠公開給該本地地址,則該DNAT規則也可以有。由於之前的規則,-d address來自任何 Docker 網橋的流量都不能達到規則。最重要的是,該規則不允許返回通過流量來自的同一橋。無論如何這都不是必需的,因為從同一座橋上您已經可以到達INTERNALPORTDNAT``RETURN``DNAT``DNAT

不同網橋上容器之間的流量限制是在iptables的過濾表中實現的。兩條自定義鏈在鏈的開頭,該鏈的預設策略是. 一個用於具有使用者定義橋的容器,另一個用於具有 Docker 橋的容器:. 那條鍊子又用了。兩者的結合基本上說,如果流量離開一個 Docker 網橋,然後進入另一個 Docker 網橋,那麼它(沒有 ICMP 信號,所以連接只是掛起…..)FORWARD``DROP``DOCKER-ISOLATION-STAGE-1``DOCKER-ISOLATION-STAGE-2``DROP

它看起來像這樣:

-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A DOCKER-ISOLATION-STAGE-1 -i br-one ! -o br-one -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -i br-two ! -o br-two -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-2 -o br-one -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-two -j DROP

因此,如果您想要來自 bridge one的流量,為通過 bridge 2DNAT上的容器暴露在外部的埠點擊 a並且您希望流量返回以獲得完整連接,那麼您必須做幾件事:

  • 刪除nat表中RETURN阻止來自鏈中流量的規則。您必須刪除源網橋的。如果您不想允許來自該網橋的容器訪問暴露的埠,您可以保留目標網橋。DNAT``DOCKER``RETURN``RETURN``DNAT

    • iptables -t nat -D DOCKER -i br-one -j RETURN
    • iptables -t nat -D DOCKER -i br-two -j RETURN #可選如果 br-one -> br-two
  • 從過濾表中的鏈中刪除DROP兩個網橋的規則。DOCKER-ISOLATION-STAGE-2

    • iptables -t filter -D DOCKER-ISOLATION-STAGE-2 -o br-one -j DROP
    • iptables -t filter -D DOCKER-ISOLATION-STAGE-2 -o br-two -j DROP

現在線路是開放的。

Docker 不會經常刷新它的規則(至少在我測試的 19.03 版本中不會)。似乎它僅在 docker 守護程序重新啟動時重建規則集,而不是在您停止或啟動或創建容器時。您可以嘗試對服務重新啟動進行任何更改以保持它們的持久性。

引用自:https://serverfault.com/questions/994200