我有一个运行多个 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中。它使用nat表
DNAT
中的规则。对于这些规则,Docker 创建了一个单独的链,以便在nat表中应用相同的规则。规则前面有过滤掉来自 Docker 网桥的所有流量的跳转。所以这是第一个障碍。DOCKER
PREROUTING
OUTPUT
DNAT
RETURN
它看起来有点像这样:
如果您仅将端口公开给该本地地址,则该
DNAT
规则也可以有。由于之前的规则,-d address
来自任何 Docker 网桥的流量都不能达到规则。最重要的是,该规则不允许返回通过流量来自的同一桥。无论如何这都不是必需的,因为从同一座桥上您已经可以到达INTERNALPORT。DNAT
RETURN
DNAT
DNAT
不同网桥上容器之间的流量限制是在iptables的过滤表中实现的。两条自定义链在链的开头,该链的默认策略是. 一个用于具有用户定义桥的容器,另一个用于具有 Docker 桥的容器:. 那条链子又用了。两者的结合基本上说,如果流量离开一个 Docker 网桥,然后进入另一个 Docker 网桥,那么它(没有 ICMP 信号,所以连接只是挂起.....)
FORWARD
DROP
DOCKER-ISOLATION-STAGE-1
DOCKER-ISOLATION-STAGE-2
DROP
它看起来像这样:
因此,如果您想要来自 bridge one的流量,为通过 bridge 2
DNAT
上的容器暴露在外部的端口点击 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 守护程序重新启动时重建规则集,而不是在您停止或启动或创建容器时。您可以尝试对服务重新启动进行任何更改以保持它们的持久性。