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


75

我刚刚试用了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)
  • Docker 1.6.2
  • 启用UFW转发。(启用UFW转发
  • --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:/# 

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

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

谢谢。

巴特


iptables -N DOCKER用该名称启动一个新链...也许您可以保持iptables启用(我的意思是不删除--iptables=false,然后就可以为链启动运行“ post命令”。我没有最佳实践的答案o_O
OscarAkaElvis

Answers:


77

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

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

您只需要走一小步,就可以在这里将集装箱放在UFW后面。您可以使用--iptables=false或创建/etc/docker/daemon.json内容如下的文件

{
  "iptables": false
}

结果将是相同的,但是后一个选项要求您使用以下命令重新启动整个docker服务 service docker restart或者如果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/上更全面地描述了问题和解决方案


1
我添加了一些有关如何从实际Docker网络配置中设置iptables规则的信息。您可能对此很感兴趣。
时髦的未来

1
您才是真正的MVP

这样很好!但是,nat iptables规则似乎在重新启动时被刷新。我如何坚持下去?
Thomas Ebert

3
要坚持iptables规则,请根据您的服务器发行版安装linux软件包iptables-persistent,在我的情况下(Debian)是sudo apt install iptables-persistent,软件包安装会将NAT规则添加到在启动时执行的持久文件中。
afboteros

1
谢谢,@ afboteros,应该知道...毕竟我使用简单的防火墙是有原因的;-)
Thomas Ebert

104

问题

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

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

先回滚更改

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

  • 启用Docker的iptables功能。删除所有类似的更改--iptables=false,包括配置文件/etc/docker/daemon.json
  • UFW的默认FORWARD规则改回默认设置,DROP而不是ACCEPT
  • 在UFW配置文件中删除与Docker网络相关的规则/etc/ufw/after.rules
  • 如果您已修改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-inputchain。但是请小心避免暴露不应公开的服务。

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


更新:2018年10月6日

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

以Docker Swarm模式安装

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

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

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


2
完善!我以为我不得不更换172.16.0.0172.17.0.0
OverCoder

2
@Feng您的解决方案看起来很有趣。还有一个稍微简单的建议在这里。我还没有时间分析差异,但是也许您可以对此发表评论?还有一件事:我认为您可以将“内部端口”重命名为“主机端口”。我对“内部端口”的含义感到困惑。在docker手册中,它始终是主机端口或容器端口,我发现更清晰。
MichaelHärtl18年

1
@MichaelHärtl我在回答的末尾添加了原因。如果有任何不清楚的描述,请告诉我。谢谢!

1
我在您的github仓库
Laurent

1
我不明白为什么未将其标记为最佳答案...第一天解决方案使我迷失了整整一天,直到找到答案。
托马斯

13

我花了两个小时来尝试上述建议和其他职位的建议。唯一可行的解​​决方案来自Github线程中Tsuna的帖子:

在末尾附加以下内容/etc/ufw/after.rules(用面向外部的接口替换eth0):

# Put Docker behind UFW
*filter
:DOCKER-USER - [0:0]
:ufw-user-input - [0:0]

-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A DOCKER-USER -m conntrack --ctstate INVALID -j DROP
-A DOCKER-USER -i eth0 -j ufw-user-input
-A DOCKER-USER -i eth0 -j DROP
COMMIT

并撤消以下所有内容:

  • 从/etc/docker/daemon.json中删除“ iptables”:“ false”
  • 在/ etc / default / ufw中恢复为DEFAULT_FORWARD_POLICY =“ DROP”
  • 删除与docker相关的对/etc/ufw/before.rules的更改
  • 重新启动后,请确保测试一切正常。我仍然认为Docker开箱即用的行为是危险的,由于Docker在其他安全的iptables配置中打孔,因此更多人将继续无意识地将内部服务公开给外界。

10

并不是说这里的解决方案是错误的,但是对于寻求快速单步指令的人来说,它们看起来有些“恐怖”和错误修剪。我最近也遇到了这个问题,已经在线阅读了所有类似的答案,并且在撰写本文时还没有发现任何快速清晰的内容。令人惊讶的是,我的替代解决方案易于理解和管理,并且有效:只需在主机外部实施防火墙即可

  • Digital Ocean具有令人惊叹的防火墙,所见即所得的风格为零额外成本。
  • AWS提供安全组
  • 等等

将防火墙视为头等公民似乎有很多好处。


1
你救了我!
disfated

1

对于它的价值,这里是@mkubaczyk答案的附录,以说明整个设置中涉及更多的桥接网络的情况。这些可以由Docker-Compose项目提供,并且鉴于这些项目由来控制,这是如何生成正确的规则systemd

/etc/systemd/system/compose-project@.service

[Unit]
Description=Docker-Compose project: %I
After=docker.service
BindsTo=docker.service
AssertPathIsDirectory=/<projects_path>/%I
AssertFileNotEmpty=/<projects_path>/%I/docker-compose.yml

[Service]
Type=simple
Restart=always
WorkingDirectory=/<projects_path>/%I
ExecStartPre=/usr/bin/docker-compose up --no-start --remove-orphans
ExecStartPre=+/usr/local/bin/update-iptables-for-docker-bridges
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose stop --timeout 30
TimeoutStopSec=30
User=<…>
StandardOutput=null

[Install]
WantedBy=multi-user.target

/usr/local/bin/update-iptables-for-docker-bridges

#!/bin/sh

for network in $(docker network ls --filter 'driver=bridge' --quiet); do
  iface=$(docker network inspect --format '{{index .Options "com.docker.network.bridge.name"}}' ${network})
  [ -z $iface ] && iface="br-${network}"
  subnet=$(docker network inspect --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' ${network})
  rule="! --out-interface ${iface} --source ${subnet} --jump MASQUERADE"
  iptables --table nat --check POSTROUTING ${rule} || iptables --table nat --append POSTROUTING ${rule}
done

显然,这不能很好地扩展。

同样值得注意的是,整个基本概念将掩盖容器中运行的应用程序的任何连接源。


1

我不喜欢iptables所需的操作开销:docker守护进程中的false标志。实际上,从我的角度来看,如果我错了,请纠正我,所有解决方案都太复杂了。

只需将其插入* filter部分之前的/etc/ufw/after.rules中:

*mangle
# Allow a whitelisted ip to access postgres port
-I PREROUTING 1 -s <whitelisted_ip> -p tcp --dport 5432 -j ACCEPT
# Allow everyone to access port 8080
-I PREROUTING 2 -p tcp --dport 8080 -j ACCEPT
# Drop everything else
-I PREROUTING 3 -p tcp -j DROP
COMMIT

无需弄混码头工人网络或不必要的黑客。


0

不太确定您的要求是什么,但是根据我的收集,您想更好地控制谁可以访问您在Docker中运行的应用程序?我在这里回答了类似的问题,以通过前端代理而不是IP表来控制流量阻止外部访问Docker容器

希望这可以帮助

迪伦

编辑

通过上述方法,您便可以使用UFW仅允许进入端口80的传入连接(即代理)。这样可以使任何端口暴露在最低限度,并具有额外的好处,您可以通过代理配置和DNS控制流量


0

我刚刚编写了一个小脚本来解决ufw防火墙和docker iptables问题。您可以在这里查看回购。它很大程度上基于chaifeng / ufw-docker的ufw规则,但是可以自动执行手动过程。您所要做的就是运行带有特殊标签的容器UFW_MANAGED=TRUE。请让我知道您的反馈。当然,代码是废话,但希望它能工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.