我刚试过Docker。它真棒,但似乎与ufw不太合作。默认情况下,docker会稍微操纵iptables。结果不是错误,而是我所期望的。 有关详细信息,请参阅The dangers of 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。这是我的测试
环境:
--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
第二次尝试
然后我发现了这个:unable to link containers with --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的最佳做法是什么?任何人都可以提供一些帮助吗?
感谢。
巴特。
答案 0 :(得分:31)
这个问题已经存在很长时间了。
在Docker中禁用iptables还会带来其他问题。
如果您已根据我们在Internet上找到的当前解决方案修改了服务器,请首先回滚这些更改,包括:
--iptables=false
之类的更改,包括配置文件/etc/docker/daemon.json
。DROP
,而不是ACCEPT
。/etc/ufw/after.rules
中删除与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
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
命令来管理容器的防火墙规则
脚本ufw-docker现在支持Docker Swarm。请查看最新的代码以获取更多信息,https://github.com/chaifeng/ufw-docker
以Docker Swarm模式安装在Swarm模式下使用时,我们只能在管理器节点上使用此脚本来管理防火墙规则。
after.rules
文件以Docker Swarm模式运行,此脚本将添加全局服务ufw-docker-agent
。图像chaifeng/ufw-docker-agent也是从该项目中自动构建的。
答案 1 :(得分:15)
我几个月前遇到过这样的问题,最近决定在我的博客上描述这个问题以及解决方案。这是捷径。
使用nohup: invalid option -- 'X'
Try `nohup --help' for more information
对您描述的案例没有多大帮助。这里还不够。默认情况下,您的容器都不能执行任何传出连接。
在这里有一个小步骤,你想要在UFW后面放置容器。您可以使用--iptables=false
或创建--iptables=false
文件,其内容如下
/etc/docker/daemon.json
结果将是相同的,但后一个选项要求您使用{
"iptables": false
}
重新启动整个docker服务,或者如果docker在您禁用此功能之前有机会添加iptables规则,则甚至会重新启动。
完成后,再做两件事:
service docker restart
因此您在UFW中设置默认转发策略以进行接受,并使用:
$ sed -i -e 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/g' /etc/default/ufw
$ ufw reload
这样你实现的是在iptables规则中禁用docker messy行为,同时docker提供了必要的路由,因此容器可以很好地完成传出连接。不过,UFW规则仍然会受到限制。
希望这能为您和任何在此寻找答案的问题解决问题。
我在https://www.mkubaczyk.com/2017/09/05/force-docker-not-bypass-ufw-rules-ubuntu-16-04/
更全面地描述了问题和解决方案答案 2 :(得分:10)
这里并不是说解决方案是错误的,但是对于寻求快速单步指令的人来说,它们看起来有点“恐怖”和错误修剪。我最近也遇到了这个问题,已经在线阅读了所有类似的答案,并且在撰写本文时没有发现任何快速而清晰的内容。 出乎意料的是,我的替代解决方案易于理解和管理,并且有效:只需在主机外部实施防火墙。
将防火墙作为头等公民似乎有很多好处。
答案 3 :(得分:1)
很抱歉挖掘这个旧线程。我遇到了同样的问题,它帮助我将 ufw 限制为特定的 ip 和接口。因为默认情况下 ufw 应用于所有网络接口,也是来自 docker 的内部接口。这就是为什么所有这些漂亮的 docker 端口转发故事(如 -p80:8080)都不起作用的原因。为了克服这个问题,只需指定一个特定的接口和 ip 应该应用什么 ufw。就我而言,它是在服务器上向世界公开的那个。
ufw allow in on eth0 to ip_of_eth0 port 22 proto tcp
ufw allow in on eth0 to ip_of_eth0 port 80 proto tcp
ufw allow in on eth0 to ip_of_eth0 port 443 proto tcp
将 eth0 更改为您想要的接口。
有了这个解决方案,现在可以不用搞乱 iptables 或 /etc/docker/daemon.json 标志中的 iptables:false 只公开真正需要的端口。
从外部计算机输出nmap:
Starting Nmap 7.91 ( https://nmap.org ) at <time>
Nmap scan report for <domain> (ip)
Host is up (0.042s latency).
Not shown: 997 filtered ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
Nmap done: 1 IP address (1 host up) scanned in 11.44 seconds
答案 4 :(得分:0)
不太确定您的要求,但从我可以收集的内容中,您希望更好地控制谁可以访问在Docker中运行的应用程序?我在这里回答了类似的问题,通过前端代理而不是IP表Block external access to docker containers
来控制流量希望这有帮助
迪伦
使用上述方法,您可以使用UFW仅允许到端口80(即代理)的传入连接。这样可以将任何端口暴露降至最低,并且可以通过代理配置控制流量。 DNS
答案 5 :(得分:0)
对于这里有什么价值,@mkubaczyk's answer是对整个设置中涉及更多桥接网络的情况的补充。这些可能由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
显然,这不会很好地扩展。
还值得注意的是,整个基本概念将掩盖在容器中运行的应用程序的任何连接的来源。
答案 6 :(得分:0)
我花了两个小时来尝试以上和其他帖子中的建议。 唯一有效的解决方案来自于Github thread中的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配置。
答案 7 :(得分:0)
我不喜欢iptables所需的操作开销:docker守护程序中的false标志。实际上,从我的角度来看,如果我错了,请纠正我,所有解决方案都太过复杂了。
只需将它插入/etc/ufw/after.rules中的* filter部分之前:
*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
无需烦恼docker网络或不必要的黑客攻击。
答案 8 :(得分:0)
我刚刚编写了一个小脚本来解决ufw防火墙和docker iptables问题。您可以查看仓库here。它很大程度上基于chaifeng/ufw-docker的ufw规则,但是可以自动执行手动过程。您所要做的就是运行带有特殊标签UFW_MANAGED=TRUE
的容器。请让我知道您的反馈。代码当然是废话,但希望它能起作用。
答案 9 :(得分:0)
总结@mkubaczyk 的帖子:
cat << EOF >> /etc/docker/daemon.json
{
"iptables": false
}
EOF
echo "DOCKER_OPTS=\"--iptables=false\"" >> /etc/default/docker
service docker restart
sed -i -e 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/g' /etc/default/ufw
cat << EOF >> /etc/ufw/before.rules
# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
# Forward traffic through eth0 - Change to match you out-interface
-A POSTROUTING -s 10.66.66.0/24 -o ens0 -j MASQUERADE
# don't delete the 'COMMIT' line or these nat table rules won't
# be processed
COMMIT
EOF
ufw reload