限制特定 docker 容器对 AWS EC2 元数据的访问

tim*_*bod 0 iptables amazon-ec2 docker

我在 AWS EC2 实例上运行 docker,并且希望阻止某些容器访问 EC2 实例元数据(IP 地址为 169.254.169.254)。我认为我可以通过在存在以下 ip 表规则的情况下以特定用户(例如 userx)运行这些容器来实现此目的:

$ iptables -A OUTPUT -m owner --uid-owner userx -d 169.254.169.254 -j DROP
Run Code Online (Sandbox Code Playgroud)

当容器通过主机网络运行时,这会按预期阻止连接:

$ docker run -it --rm --network host -u $(id -u userx):$(id -g userx) appropriate/curl  http://169.254.169.254/latest/meta-data/
...blocks..
Run Code Online (Sandbox Code Playgroud)

但遗憾的是,当容器在自己的网络中运行时允许连接

$ docker run -it --rm -u $(id -u userx):$(id -g userx) appropriate/curl  http://169.254.169.254/latest/meta-data/
...show metadata...
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?或者,是否有其他一些技术可以为特定容器提供完整的网络访问权限,同时阻止实例元数据?

Dir*_*aio 5

您的问题是OUTPUT无法捕获来自容器的数据包。FORWARD做。

这是为什么?

每个 Docker 容器都在自己的网络命名空间中运行。每个网络命名空间都有自己的路由表和 iptables 规则,并且其行为就像一台单独的物理机一样。

在 iptables 中:

  • INPUT匹配发往本地进程的数据包
  • FORWARD匹配传入一个网络接口并传出另一个网络接口(通过路由)的数据包。
  • OUTPUT匹配来自本地进程的数据包

关键是“本地进程”是指“这个网络命名空间中的进程”,而不是“本机中的进程”。

我们来分析一下这是怎么回事:

  • 数据包由 Docker 容器的网络命名空间中的进程生成。
  • 它们遍历OUTPUT容器网络命名空间 iptables 中的 iptables 链。(这是空的!)
  • 它们被路由出接口veth
  • 它们从接口到达主机的网络命名空间veth
  • 主机网络命名空间查阅路由表并决定它们需要离开例如eth0
  • 它们遍历FORWARD主机网络命名空间中的 iptables 链。
  • 他们出去了eth0

因此,解决方案是将您的规则放入链中FORWARD

问题是这-m ownerFORWARD. 根据man iptables-extensions

该匹配仅在 OUTPUT 和 POSTROUTING 链中有效。转发的数据包没有任何与其关联的套接字。

您可以对容器的 IP 地址进行硬编码,或者将要过滤的容器放入特殊网络中,并匹配整个范围。与此类似的东西应该有效:

    # single container
    iptables -A FORWARD -s 172.17.0.4 -d 169.254.169.254 -j DROP

    # or entire network
    iptables -A FORWARD -s 172.17.0.0/16 -d 169.254.169.254 -j DROP
Run Code Online (Sandbox Code Playgroud)

另外,owner无论哪种方式使用可能都不是一个好主意,因为 docker 容器内的进程可以通过例如 setuid 二进制文件(如 sudo)更改其 uid(如果镜像中有的话)。