我在 Ubuntu 16.04 上使用 Docker 17.04.0-ce。
我在我的主机上运行了几个公开端口的容器,其中一些应该只能通过特定的 IP 范围访问,而其他一些容器(如监听端口 80 和 443 的 nginx 代理)应该是公开访问的。
使用默认配置我的iptables配置输入链被忽略,允许从任何地方访问所有绑定端口的容器。所以我了解到我必须--iptables=false
向 docker 提供该选项,这很有效。
虽然我现在可以使用以下方式控制对不同端口的访问iptables,我的 nginx 容器不再能够看到连接客户端的 ip 地址,而只能获取网桥的 ip docker0
(172.17.41.1就我而言)。
有没有什么方法可以让 nginx 容器看到连接客户端的 ip,而不会再次失去对 iptables 的控制?
旁注:我不想把所有容器都放在主机网络上(--net=host
)。
答案1
我能够使用 来查看从我的容器连接客户端的 IP --net=host
。
- 使用
--iptables=false
,这样docker就不会弄乱你主机的iptables配置。 - 在docker主机上启用ip转发:
echo 1>/proc/sys/net/ipv4/ip_forward
--net=host
告诉 Docker 跳过将容器放置在单独的网络堆栈内。本质上,这个选择告诉 Docker 不要将容器的网络容器化!docker run -d -p 80:80 --net=host -name some-container your-image
从https://docs.docker.com/v1.5/articles/networking/#the-world:
--net=host — 告诉 Docker 跳过将容器放置在单独的网络堆栈内的步骤。本质上,这个选择告诉 Docker 不要将容器的网络容器化!虽然容器进程仍将局限于它们自己的文件系统和进程列表以及资源限制,但快速 ip addr 命令将向您显示,从网络角度来看,它们位于主 Docker 主机的“外部”,并可以完全访问其网络接口。请注意,这不允许容器重新配置主机网络堆栈(这需要 --privileged=true),但它允许容器进程像任何其他根进程一样打开低编号端口。它还允许容器访问本地网络服务,如 D-bus。这可能导致容器中的进程能够执行意外操作,如重启计算机。您应该谨慎使用此选项。
答案2
这取决于你的 IPTABLES 设置。听起来好像你在转发时伪装了,如下所示:
iptables -t nat -A POSTROUTING --out-interface docker0 -j MASQUERADE
如果希望客户端看到原始 IP,则必须禁用伪装,只使用 NAT 和 PREROUTING。网上有很多这方面的手册。
只需确保您的容器使用您的主机作为默认路由,否则您最终将无法回答,因为您的响应不是来自正确的 IP。
要说更多,您需要发布您的 iptables 配置......
答案3
默认情况下,Docker 使用用户空间代理(docker-proxy
)来公开端口。
尝试使用以下方法禁用它:
--userland-proxy=false
答案4
如果您的容器(在容器网络中)没有 IPv6 地址,但您已将 IPv4 和 IPv6 端口映射到容器中的端口,则会出现此问题。
80:8080
例如,将外部端口0.0.0.0:80
和隐式映射[::]:80
到内部端口8080
。
如果你现在连接到主机的 IPv6 地址或主机名解析为 IPv6 地址,docker用户空间代理转换源地址和目标地址(基本上是用户空间 NAT64)。这会导致数据包源(IPv4)地址显示为容器默认网关(IPv4)地址。
避免这种情况的最简单方法可能是仅映射到 IPv4。然而,这意味着没有 IPv4 连接的客户端无法访问您的容器。
另一种(可能更好的)方法是确保您的容器获得 IPv6 地址。
欲了解更多信息,请查看这问题。