摘要
现有两个docker容器nginx、openjdk分别部署前后端服务,假设默认防火墙为firewalld
,发现在默认配置下,本地直接curl后台服务器能正确响应,nginx的代理的请求proxy_pass无法得到后台服务器的响应,只得到502 BadGateway
。
排查
关闭防火墙
首先怀疑是防火墙的问题,临时关闭防火墙试试:
systemctl stop firewalld
重新用nginx发起代理连接,发现nginx容器能向java容器通信了,看来是防火墙组织了两个容器的网络访问,先恢复防火墙再继续排查
systemctl start firewalld
查询防火墙拦截日志
查看firewalld拦截日志,注意,firewalld服务默认关闭所有类型的被拒绝访问的日志记录,即
>>>firewall-cmd --get-log-denied
>>>off
# 其中denied参数
# all: 表示记录所有被拒绝的访问,无论是单播、组播还是广播。
# unicast: 表示记录被拒绝的单播访问。
# broadcast: 表示记录被拒绝的广播访问。
# multicast: 表示记录被拒绝的组播访问。
# off: 表示关闭所有类型的被拒绝访问的日志记录。
需要在/etc/firewalld/firewalld.conf 文件中修改设置
LogDenied=all
或者输入命令
firewall-cmd --set-log-denied=all --permanent
无论哪种方法,最后都要重启服务使配置生效
firewall-cmd --reload
拦截日志在/var/log/message 中,使用tail跟踪日志输出
tail -f /var/log/messages
再测试一次便可以看到连接被拦截的原因了:
"filter_IN_public_REJECT: "
IN=docker0 OUT=
PHYSIN=veth3050f50 MAC=02:42:56:73:8c:b3:02:42:ac:11:00:02:08:00
SRC=172.17.0.2 DST=192.168.174.132
LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28276 DF
PROTO=TCP SPT=58746 DPT=11260 WINDOW=64240 RES=0x00 SYN URGP=0
日志的详细解释如下:
-
filter_IN_public_REJECT: 这部分表示这是一个在filter表、IN_public链上的规则触发的拒绝(REJECT)动作。filter表是iptables默认的表,用于决定如何处理进入的数据包;IN_public链可能是用户自定义的链,用于处理进入公共接口的数据包。
-
IN=docker0 OUT= PHYSIN=veth3050f50:IN=docker0表示数据包是从名为docker0的网络接口进入的,这是Docker默认的虚拟网桥。OUT=表示数据包没有从这个方向出去(因为是入站规则)。PHYSIN=veth3050f50表示物理入站接口是veth3050f50,这是一个虚拟以太网接口,通常用于Docker容器与docker0网桥之间的通信。
-
MAC=02:42:56:73:8c:b3:02:42:ac:11:00:02:这部分显示了数据包的源MAC地址和目标MAC地址。源MAC地址是02:42:56:73:8c:b3(可能是Docker容器的MAC地址),目标MAC地址是02:42:ac:11:00:02(属于docker0网桥上的另一个设备)。
-
SRC=172.17.0.2 DST=192.168.174.132:SRC是源IP地址,DST是目标IP地址。这里表示数据包是从IP地址172.17.0.2(Docker容器内的一个IP)发送到192.168.174.132(可能是宿主机的另一个IP或网络上的其他设备)。
-
LEN=60:数据包长度是60字节。
-
TOS=0x00 PREC=0x00:服务类型(Type of Service, TOS)和优先级(Precedence, PREC)都是0,表示没有特殊的服务类型或优先级要求。
-
TTL=64:生存时间(Time To Live, TTL)是64,表示数据包在网络中可以经过的最大路由器数量。
-
ID=36194 DF:数据包ID是36194,DF(Don’t Fragment)标志设置为1,表示数据包不应该被分片。
-
PROTO=TCP:协议是TCP(传输控制协议)。
-
SPT=58696 DPT=11260:源端口(Source Port, SPT)是58696,目标端口(Destination Port, DPT)是11260。
-
WINDOW=64240 RES=0x00 SYN URGP=0:TCP窗口大小是64240字节,保留字段(RES)是0,SYN标志位设置为1表示这是一个TCP连接建立的请求(三次握手的第一步),紧急指针(Urgent Pointer, URGP)是0。
综上所述,这条日志表示一个从Docker容器(IP地址为172.17.0.2)尝试建立到IP地址为192.168.174.132的TCP连接(端口11260)的数据包被防火墙规则拒绝。
排查小结
由上可知,但容器中发出的请求仍然视为外界的连接,它具有独立的ip地址172.17.0.2
和MAC地址02:42:56:73:8c:b3
,数据包进入的网络接口是 docker0
等,所以,需要根据这些信息为docker定制安全策略,使外来连接不能访问而仅让docker可以访问。但实际操作上是不好定制的,规则太严会导致在大量容器的部署过程出现”误杀“,规则太松有可能有安全隐患。
解决思路
方法1:关闭防火墙(不推荐)
systemctl stop firewalld && systemctl disable firewalld
方法2: 暴露端口(不推荐)
firewall-cmd --add-port xx/tcp --permanent && firewall-cmd --reload
docker17217001611260_71">方法3: 给docker的源地址172.17.0.0/16
的开放11260
端口,隔离其他网段的访问,注意,这个策略简单粗暴,本质上是对特定网段暴露了特定端口,可以基于源地址、目标地址和端口做更详细的安全策略。
firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="172.17.0.0/16" port port="11260" protocol="tcp" accept' --permanentfirewall-cmd --reload
docker_79">方法4: 开放zone区域给docker访问
在步骤三的基础上限制网络接口为docker0
的才能访问,进一步提高安全性
先删除步骤3的配置:
firewall-cmd --zone=public --remove-rich-rule='rule family="ipv4" source address="172.17.0.0/16" port port="11260" protocol="tcp" accept' --permanent
再采用新配置
- 创建了一个名为 docker 的新防火墙区域
firewall-cmd --new-zone=docker --permanent
- 将 docker0 接口分配给了 docker 区域。docker0 是 Docker 默认的虚拟网桥接口,用于容器之间的通信。
firewall-cmd --zone=docker --add-interface=docker0 --permanent
- 将 docker 区域的默认目标策略设置为 DROP,意味着默认情况下会丢弃所有进入该区域的流量。这是一种非常严格的安全策略
firewall-cmd --zone=docker --set-target=DROP --permanent
- 添加了一条富规则,允许来自 IPv4 地址范围 172.17.0.0/16 的 TCP 流量,如果目标端口是 1125,则接受这些流量。这个规则覆盖了区域的默认 DROP 策略,仅对符合条件的流量开放。
firewall-cmd --zone=docker --add-rich-rule='rule family="ipv4" source address="172.17.0.0/16" port port="1125" protocol="tcp" accept' --permanent
- 重新加载 firewalld 配置以使更改生效
firewall-cmd --reload
这些设置将影响所有通过 docker0 接口的流量,包括 Docker 容器之间的通信以及从外部网络到容器的流量(如果外部网络能够访问到 docker0 接口)。由于将默认策略设置为 DROP,并且只添加了一条非常具体的允许规则,因此需要确保这条规则涵盖了所有需要允许的流量,否则可能会导致意外的连接问题
小结
总之,四个思路安全性由低到高,然而越严格的策略越容易”误杀“,本人能力有限,在这里提出自己的思路,可根据对服务配置的理解设置更高效的安全策略。