Ubuntu下docker ufw的最佳实践是什么

新手上路,请多包涵

我刚刚试用了 Docker。它很棒,但似乎不适用于 ufw。默认情况下,docker 会稍微操作 iptables。结果不是错误,但不是我所期望的。有关更多详细信息,您可以阅读 UFW + Docker 的危险

我的目标是建立一个像

    Host (running ufw) -> docker container 1 - nginx (as a reverse proxy)
                       -> docker container 2 - node web 1
                       -> docker container 3 - node web 2
                       -> .......

我想通过 ufw 管理传入的流量(例如限制访问),因此我不希望 docker 触摸我的 iptables。这是我的测试

环境:

  • 一个新安装的 Ubuntu 14.04(内核:3.13.0-53)
  • 码头工人 1.6.2
  • ufw 转发已启用。( [Enable UFW forwarding] 2 )
  • --iptables=false 被添加到 Docker 守护进程中。

第一次尝试

docker run --name ghost -v /home/xxxx/ghost_content:/var/lib/ghost -d ghost
docker run --name nginx -p 80:80 -v /home/xxxx/nginx_site_enable:/etc/nginx/conf.d:ro --link ghost:ghost -d nginx

没运气。第一个命令很好,但第二个命令会抛出错误

Error response from daemon: Cannot start container

第二次尝试

然后我发现: 无法使用 –iptables=false #12701 链接容器

运行以下命令后,一切正常。

 sudo iptables -N DOCKER

但是,我注意到我无法在容器内建立任何出站连接。例如:

 xxxxg@ubuntu:~$ sudo docker exec -t -i nginx /bin/bash
root@b0d33f22d3f4:/# ping 74.125.21.147
PING 74.125.21.147 (74.125.21.147): 56 data bytes
^C--- 74.125.21.147 ping statistics ---
35 packets transmitted, 0 packets received, 100% packet loss
root@b0d33f22d3f4:/#

如果我从 Docker 守护程序中删除 --iptables=false ,那么容器的互联网连接将恢复正常,但 ufw 将无法“正常”工作(嗯……根据我的定义)。

那么,docker + ufw 的最佳实践是什么?谁能提供一些帮助?

原文由 Yi-Chien Chang 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.1k
2 个回答

几个月前我遇到过这样的问题,最近决定在我的博客上描述这个问题以及解决方案。这是捷径。

使用 --iptables=false 对您描述的情况没有多大帮助。这里根本不够。默认情况下,您的任何容器都不能进行任何传出连接。

在 UFW 后面拥有容器的过程中,您省略了一小步。您可以使用 --iptables=false 或创建 /etc/docker/daemon.json 文件,内容如下

{
  "iptables": false
}

结果将是相同的,但后一个选项要求您使用 service docker restart 重新启动整个 docker 服务,或者如果 docker 在禁用此功能之前有机会添加 iptables 规则,甚至需要重新启动。

完成后,只需再做两件事:

 $ sed -i -e 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/g' /etc/default/ufw
$ ufw reload

因此,您在 UFW 中设置默认转发策略以接受,并使用:

 $ iptables -t nat -A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE

这样,您要实现的是在您的 iptables 规则中禁用 docker 混乱行为,同时为 docker 提供必要的路由,以便容器可以很好地进行传出连接。不过,从此时起,UFW 规则仍将受到限制。

希望这可以为您和任何在这里寻找答案的人解决问题。

我在 https://www.mkubaczyk.com/2017/09/05/force-docker-not-bypass-ufw-rules-ubuntu-16-04/ 更全面地描述了问题和解决方案

原文由 mkubaczyk 发布,翻译遵循 CC BY-SA 3.0 许可协议

问题

这个问题已经存在很长时间了。

在 Docker 中禁用 iptables 会带来其他问题。

首先回滚更改

如果您根据我们在互联网上找到的当前解决方案修改了您的服务器,请先回滚这些更改,包括:

  • 启用 Docker 的 iptables 功能。删除所有更改,如 --iptables=false ,包括配置文件 /etc/docker/daemon.json
  • UFW 的默认 FORWARD 规则更改回默认值 DROP 而不是 ACCEPT
  • 去掉UFW配置文件 /etc/ufw/after.rules 中与Docker网络相关的规则。
  • 如果您修改了 Docker 配置文件,请先重新启动 Docker。稍后我们将修改 UFW 配置,然后我们可以重新启动它。

解决 UFW 和 Docker 问题

该方案只需要修改一个 UFW 配置文件,所有 Docker 配置和选项保持默认。不需要禁用 docker iptables 功能。

修改UFW配置文件 /etc/ufw/after.rules 在文件末尾添加如下规则:

 # BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN
COMMIT
# END UFW AND DOCKER

修改文件后使用命令 sudo systemctl restart ufw 重启UFW。现在公网不能访问任何已发布的docker端口,容器和私网可以定期互相访问,容器也可以从内部访问外网。

如果要允许公网访问 Docker 容器提供的服务,例如容器的服务端口为 80 。运行以下命令以允许公共网络访问此服务:

 ufw route allow proto tcp from any to any port 80

该命令允许公网访问容器端口为 80 的所有已发布端口。

注意:如果我们使用选项 -p 8080:80 发布端口,我们应该使用容器端口 80 ,而不是主机端口 8080

如果有多个容器的服务端口为 80,但我们只希望外部网络访问特定的容器。例如,如果容器的私有地址为 172.17.0.2,则使用以下命令:

 ufw route allow proto tcp from any to 172.17.0.2 port 80

如果服务的网络协议是UDP,例如DNS服务,可以使用如下命令允许外网访问所有已发布的DNS服务:

 ufw route allow proto udp from any to any port 53

同样,如果只针对特定的容器,比如IP地址172.17.0.2:

 ufw route allow proto udp from any to 172.17.0.2 port 53

这个怎么运作?

以下规则允许专用网络能够相互访问。通常,专用网络比公共网络更受信任。

 -A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

以下规则允许 UFW 管理是否允许公共网络访问 Docker 容器提供的服务。这样我们就可以在一个地方管理所有防火墙规则。

 -A DOCKER-USER -j ufw-user-forward

以下规则阻止所有公共网络发起的连接请求,但允许内部网络访问外部网络。对于 TCP 协议,它阻止了从公共网络主动建立 TCP 连接。对于 UDP 协议,所有小于 32767 的端口的访问都会被阻止。为什么是这个端口?由于UDP协议是无状态的,所以不可能像TCP那样阻塞发起连接请求的握手信号。对于 GNU/Linux,我们可以在文件 /proc/sys/net/ipv4/ip_local_port_range 中找到本地端口范围。默认范围是 32768 60999 。从正在运行的容器访问 UDP 协议服务时,会从端口范围中随机选择一个本地端口,服务器会将数据返回到这个随机端口。因此,我们可以假设所有容器内UDP协议的监听端口都小于32768。这就是我们不希望公网访问小于32768的UDP端口的原因。

 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN

更多的

https://github.com/chaifeng/ufw-docker

 sudo wget -O /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
chmod +x /usr/local/bin/ufw-docker

用法

ufw-docker help
ufw-docker install
ufw-docker status
ufw-docker allow webapp
ufw-docker allow webapp 80
ufw-docker allow webapp 53/udp
ufw-docker list webapp
ufw-docker delete allow webapp 80/tcp
ufw-docker delete allow webapp


更新:2018-09-10

选择 ufw-user-forward 的原因,而不是 ufw-user-input

使用 ufw-user-input

临:

易于使用和理解,支持旧版本的 Ubuntu。

例如,要允许公众访问容器端口为 8080 的已发布端口,请使用以下命令:

 ufw allow 8080

缺点:

它不仅暴露了容器的端口,还暴露了主机的端口。

例如,如果主机上正在运行服务,并且端口是 8080 。命令 ufw allow 8080 允许公网访问服务和所有容器端口为 8080 的已发布端口。但是我们只想公开在主机上运行的服务,或者只是在容器内运行的服务,而不是两者。

为了避免这个问题,我们可能需要对所有容器使用类似于以下的命令:

 ufw allow proto tcp from any to 172.16.0.3 port 8080

使用 ufw-user-forward

临:

无法通过同一命令同时暴露在主机和容器上运行的服务。

比如我们要发布容器的端口 8080 ,使用如下命令:

 ufw route allow 8080

公网可以访问容器端口为 8080 的所有已发布端口。

但是公网仍然无法访问主机的端口 8080 。如果我们想这样做,请执行以下命令,以允许公共单独访问主机上的端口:

 ufw allow 8080

缺点:

不支持旧版本的 Ubuntu,命令有点复杂。但是你可以使用我的脚本 https://github.com/chaifeng/ufw-docker

结论

如果我们使用的是旧版本的 Ubuntu,我们可以使用 ufw-user-input 链。但是要小心避免暴露不应该暴露的服务。

如果我们使用的是支持 ufw route 子命令的较新版本的Ubuntu,我们最好使用 ufw-user-forward 链,并使用 ufw route 命令来管理防火墙容器规则。


更新:2018 年 10 月 6 日

ufw-docker 脚本现在支持 Docker Swarm。更多请查看最新代码, https://github.com/chaifeng/ufw-docker

安装 Docker Swarm 模式

在 Swarm 模式下使用时,我们只能在管理器节点上使用此脚本来管理防火墙规则。

  • 修改所有节点上的所有 after.rules 文件,包括managers和workers
  • 在管理器节点上部署此脚本

在 Docker Swarm 模式下运行,此脚本将添加一个全局服务 ufw-docker-agent 。镜像 chaifeng/ufw-docker-agent 也是从这个项目中自动构建的。

原文由 Feng 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题