Docker与IPtables
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker与IPtables相关的知识,希望对你有一定的参考价值。
参考技术A 最近,在做一些端口block的bug测试,发现在host主机用iptables直接配置多某个container端口的访问,居然没有生效。百思不得其解,查资料,解决问题。使用命令,查看各个rules的处理字节量:
iptables -nvL
发现在 FORWARD 这个chain中对 DOCKER 这个自定义链处理了数据。
然后在DOCKER这个chain里,却发现没有任何的package和bytes,都是0。非常诡异。
默认情况下,Docker deamon会在启动container的时候向iptables中添加转发的规则。它会创建一个filter chain - DOCKER。
同时,docke利用这个规则r向外暴露container的端口。但是,很不幸,这条规则将这个端口暴露给了全世界。
所以,如果你是在宿主机直接运行docker,同时这个宿主机已经拥有了关于防火墙的一些配置(利用iptables)。那么,可以考虑在创建或者运行container的时候使用 --iptables=false 。
现在我们举个例子。如果你想要启动nginx,并将容器的端口80映射到主机端口9090.
这条命令下,docker会将一些映射规则自动的加载到iptables的DOCKER chain中。这里,我发现,docker配置后的转发规则允许了所有的外来ip对这个端口进行访问。可能这并不符合生产要求(测试也有可能会出现一些安全问题)。
我们可能指向要将端口对本地进行暴露,也就是只允许本地request。当然如果你有需求,需要外接IP进行访问,还要其他的办法。
首先,我们来看下 docker run 命令的说明( DOC ):
官方给的例子:
将docker容器的8080端口绑定到local主机的80端口上。同时docker 用户手册 对其进行了详细的说明。(有时间以后在继续完善)
同时,我们可以暴露容器的80端口,但是并不将其与主机系统相关联。
这里我们用Nginx进行举例,将80端口绑定到主机的9090端口。
我们查看一下iptables的变化:
这里,我们就映射到了本地的9090端口。而外界的IP并不能访问到我们的container。
首先,我们确定场景:
对于使用 sysvinit 和 upstart 的系统,我们可以修改 /etc/default/docker ,docker的配置文件。对于systemd:
然后,重启我们的防火墙,同时重启docker daemon。然后我们就能够看到 DOCKER 这个chain以及和其相关的 FORWARD chain的配置已经没有了。
如果我们使用的是docker创建的 ethernet bridge ,命名为'docker0'。我们可以通过以下设置配置 FORWARD 的rules:
同时,如果你想要暴露一个正在运行的container的TCP端口10000,,这个container需要将这port暴露给任何IP:
同时,我们可以将这个规则加入到iptables中,让外来访问可以通过防火墙访问container:
最后,推荐一个docker的项目,可以方便的配置Filewall: DFWFW 。
同时,我们注意到了docker的行为中-p指令暴露端口,是对iptables进行操作。但是centos 7默认是使用firewalld的。查阅资料:
在实际使用过程中,没有使用iptables.service,docker的端口转发也是正常的,因为iptables一直都在。docker会创建自己的iptables链,如果firewalld重启,docker创建的链也需要重新创建。
下面是停用firewalld服务,启用iptables-services的脚本:
或者直接使用Firewall:
Docker:如何重新创建 dockers 的附加 iptables 规则?
【中文标题】Docker:如何重新创建 dockers 的附加 iptables 规则?【英文标题】:Docker: How to re-create dockers additional iptables rules? 【发布时间】:2014-11-13 02:31:47 【问题描述】:当 docker-demon 启动时,它会向 iptables 添加一些规则。
当通过iptables -F
删除所有规则时,我必须停止并重新启动 docker 恶魔以重新创建 dockers 规则。
有没有办法让 docker 重新添加它的附加规则?
【问题讨论】:
【参考方案1】:最好的方法是重启你的 docker 服务,然后它将你的 docker 规则重新添加到 iptables。 (基于 deb:sudo service docker restart
)
但是,如果您只是想在不重新启动服务的情况下恢复这些规则,我保存了我的规则,以便您检查并调整它以适合您,然后使用 sudo iptables-restore ./iptables-docker-ports.backup
加载
编辑并保存到./iptables-docker-ports.backup
# Generated by iptables-save v1.4.21 on Thu Apr 30 20:48:42 2015
*nat
:PREROUTING ACCEPT [18:1080]
:INPUT ACCEPT [18:1080]
:OUTPUT ACCEPT [22:1550]
:POSTROUTING ACCEPT [22:1550]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.17.0.1/32 -d 172.17.0.1/32 -p tcp -m tcp --dport 80 -j MASQUERADE
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 3001 -j DNAT --to-destination 172.17.0.1:80
COMMIT
# Completed on Thu Apr 30 20:48:42 2015
# Generated by iptables-save v1.4.21 on Thu Apr 30 20:48:42 2015
*filter
:INPUT ACCEPT [495:53218]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [480:89217]
:DOCKER - [0:0]
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER -d 172.17.0.1/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
COMMIT
# Completed on Thu Apr 30 20:48:42 2015
【讨论】:
非常感谢,这是我唯一找到默认 docker 配置来重置我的 iptables 规则的帖子,完美! 这将停止您的容器!重新启动 docker 服务会停止所有容器。不是解决方案。【参考方案2】:如果您在主机上运行 Ubuntu,则可以在启动 docker 守护程序后使用iptables-save
实用程序将 iptables 规则保存到文件中。然后,刷新旧规则后,您可以使用 iptables-restore
和保存的规则文件简单地恢复原始 docker 规则。
如果您不想恢复所有旧的 iptables 规则,您可以更改保存的规则文件以仅保留您需要的规则。
如果您运行的是其他操作系统,您可能会找到类似的替代方案。
【讨论】:
docker 的规则总是一样的吗?我可以将它们永久添加到我的 iptables-restore 配置文件中吗? 详细说明:我使用 iptables-persistent 和自定义规则文件作为防火墙。每当我需要打开一个端口时,我都会编辑我的规则文件并使用iptables-apply
应用它。我在我的规则文件中保留了一个干净的结构和大量用于文档的 cmets,因此覆盖它不是一种选择。
小心盲目地保存/恢复规则,因为容器的 IP 在重启时会发生变化!【参考方案3】:
默认配置下的 Docker,在 bridge
模式下运行时,确实会操纵 iptables
(很多)unless you disable it(然后您必须配置自己的 NAT 规则)。
默认的网络相关配置可能如下,尽管配置 /etc/docker/daemon.json
可能不存在(现在 you can't print effective configuration):
"userland-proxy": true,
"iptables": true,
"ip-forward": true,
"ip-masq": true,
"ipv6": false
Docker daemon 启动后,会注入以下规则(filter
):
-N DOCKER
-N DOCKER-ISOLATION-STAGE-1
-N DOCKER-ISOLATION-STAGE-2
-N DOCKER-USER
-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 DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
-A DOCKER-USER -j RETURN
为了了解 Docker 的作用,这里是 a list of Docker-generated iptables
rules 并附有简短说明。如果你刷新 iptables 规则,而 Docker 守护进程和一些容器正在运行,你可能会破坏对现有容器的访问(但可能不会破坏任何东西,下面会详细介绍)。
service docker restart
后所有默认规则都注入防火墙(您可以通过运行iptables-save
或iptables -S
、iptables -S -t nat
来检查它)。假设您想保持容器运行并且只生成缺少的 NAT 规则。
docker ps
为我们提供了正在运行的容器列表:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
865569da8d36 nginx "nginx -g 'daemon of…" 17 hours ago Up 17 hours 0.0.0.0:4564->80/tcp, 0.0.0.0:32237->80/tcp jovial_sammet
从docker inspect
我们可以得到端口映射
$ docker inspect -f '.NetworkSettings.Ports' 865569da8d36
map[80/tcp:[0.0.0.0 4564 0.0.0.0 32237]]
现在我们只需要 Docker 容器的内部 IP 地址:
$ docker inspect -f '.NetworkSettings.IPAddress' 865569da8d36
172.17.0.2
现在使用一些 bash/jq 我们可以generate the dynamic iptables rules:
$ bash docker_iptables --noop
iptables -A DOCKER -d 172.17.0.2
iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 4564 -j DNAT --to-destination 172.17.0.2:80
iptables -A DOCKER -d 172.17.0.2
iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 32237 -j DNAT --to-destination 172.17.0.2:80
所以问题的答案是:不,不停止所有容器。但是可以手动重新添加规则(注意:此脚本不涵盖所有 Docker 功能,例如,如果您要公开在 Docker 容器以外的其他网络中运行的某些服务)。
当您使用暴露的端口 (-p
) 启动 Docker 容器时:
docker run --rm -d -p 32237:80 -p 4564:80 nginx
Docker 也启动了 docker-proxy
。那是什么?
$ netstat -tulpn | grep docker-proxy
tcp 0 0 0.0.0.0:32237 0.0.0.0:* LISTEN 20487/docker-proxy
tcp 0 0 0.0.0.0:4564 0.0.0.0:* LISTEN 20479/docker-proxy
Linux 内核不允许环回流量的路由,因此无法将 netfilter NAT 规则应用于来自 127.0.0.0/8 的数据包。 docker-proxy
is generally considered as an inelegant solution to such problems。
当您在没有 Docker 规则的情况下恢复 iptables 时,容器端口可能仍可通过 docker-proxy
使用。不过这可能会带来一些performance issues in networking,因为docker-proxy
不会像内核的netfilter 一样快。
【讨论】:
以上是关于Docker与IPtables的主要内容,如果未能解决你的问题,请参考以下文章
linux下的防火墙Netfilter配置:iptables的了解与使用(详细)
Kubernetes集群搭建,基于kubeadm自动化脚本内外网两种方式部署
Kubernetes集群搭建,基于kubeadm自动化脚本内外网两种方式部署
Kubernetes集群搭建,基于kubeadm自动化脚本内外网两种方式部署