限制 docker 容器互联网访问

Restrict docker container internet access

TLDR; - 向 docker 容器添加了一些 iptable 规则以限制互联网访问。工作正常,除了现在我无法从主机访问容器应用程序,但可以从容器本身内部访问

我有一个容器 运行 一个网络应用程序。此容器使用 mysqlredis 等。每个依赖项都是远程的,可通过特定端口上的 ip 地址访问。

因此,例如,mysql 可通过 ip 13.255.255.255

访问

我想要的是让容器只能使用 mysql ip 地址,而不能使用任何其他地址。很少有 curl 请求来自我不想超出主机网络的代码。

我在 docker 中添加了一个入口点脚本,它在容器中添加了一些 iptables 规则。

ALLOWED_CIDR1=172.0.0.0/16
ALLOWED_CIDR2=13.255.255.255 #For mysql access

#iptables -P FORWARD DROP # we aren't a router
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -s 127.0.0.1 -j ACCEPT
iptables -A OUTPUT -d 127.0.0.1 -j ACCEPT
iptables -P INPUT DROP # Drop everything we don't accept

iptables -A INPUT -s 0.0.0.0 -j ACCEPT
iptables -A INPUT -s ::1 -j ACCEPT
iptables -A OUTPUT -d ::1 -j ACCEPT
iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
iptables -P INPUT DROP
iptables -P OUTPUT DROP

当我运行容器并做

docker-compose exec <container-name> curl http://google.com

我得到以下回应:

curl: (6) Could not resolve host: google.com

这是意料之中的。现在,当我做

docker-compose exec <container-name> curl http://0.0.0.0

我收到以下回复:

"Hello World!"

这又是意料之中的事情。但是,当我从我的主机执行 curl http://0.0.0.0 时,以下是输出

* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: 0.0.0.0
> User-Agent: curl/7.62.0
> Accept: */*
> // Hangs here

所以,我无法从主机连接到 http://0.0.0.0,但可以从 docker.

内部连接

我太蠢了,竟然忽略了我自己发布的 iptables 规则。

规则集错误

ALLOWED_CIDR1=172.0.0.0/16
ALLOWED_CIDR2=13.255.255.255 #For mysql access

#iptables -P FORWARD DROP # we aren't a router
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -s 127.0.0.1 -j ACCEPT
iptables -A OUTPUT -d 127.0.0.1 -j ACCEPT
iptables -P INPUT DROP # Drop everything we don't accept

iptables -A INPUT -s 0.0.0.0 -j ACCEPT
iptables -A INPUT -s ::1 -j ACCEPT
iptables -A OUTPUT -d ::1 -j ACCEPT
iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
iptables -P INPUT DROP
iptables -P OUTPUT DROP

你可以看到,在上面的部分:

  1. IP 0.0.0.0
  2. 没有OUTPUT ACCEPT
  3. ip 192.168.x.x 没有 OUTPUT ACCEPT 规则,它是我的 docker-0 网络接口
  4. 的 IP 地址

如果在启动容器时使用网络模式bridged,docker和主机都使用docker0网络接口进行通信(这恰好是我的情况)。

我注意到的另一件事是,我根本不需要 0.0.0.0127.0.0.1 规则。由于入口点脚本将在 docker 容器中添加 iptable 规则,我们可能永远不想从容器本身访问 webapp。因此,为什么要费心使用 127.0.0.1?

总而言之,这是我所做的:

  1. 获取 docker0 网络的 IP 地址。 ip addr show docker0。它输出 192.168.144.1/20
  2. 我在我的入口点 iptable 规则中添加了 192.168.0.0/16 到 ACCEPT 规则,它在第 1 点覆盖了我的 IP 地址
  3. 现在我可以从外部访问我的容器

我的 iptable 规则现在看起来像这样:

ALLOWED_CIDR1=172.0.0.0/16
ALLOWED_CIDR2=13.255.255.255
ALLOWED_CIDR3=192.168.0.0/16

iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -P INPUT DROP # Drop everything we don't accept
iptables -P OUTPUT DROP

iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
iptables -A INPUT -s $ALLOWED_CIDR2 -j ACCEPT
iptables -A INPUT -s $ALLOWED_CIDR3 -j ACCEPT
iptables -A OUTPUT -d $ALLOWED_CIDR1 -j ACCEPT
iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
iptables -A OUTPUT -d $ALLOWED_CIDR3 -j ACCEPT