问题描述
docker下的oracle数据库服务存在漏洞,解决难度较高,于是通过firewalld限制高危端口开放,只允许本机访问。发现即使配置规则firewalld把oracle 1521漏洞端口不开放出去,但其他服务器依然能访问这给服务器的1521端口,也就是说firewalld配置的规则失效。
第一种方案
docker容器在创建时会在iptables的INPUT表的DOCKER链下自动创建规则放行端口,为了保证docker启动的每一个容器都能被外界访问(自私!),所以导致firewalld不放行端口也能被外界访问。
Firewalld服务启动时,会将iptables上的规则全部清空,并把firewalld上的防火墙策略规则写入到iptables中,此时docker因为防火墙规则被删除就会不正常,需要重启systemctl restart docker服务来重新刷写防火墙规则到iptables上,所以我们在管理Docker端口时,应该使用iptables防火墙而不是firewalld防火墙服务
首先在docker容器启动时不让它自动在iptables上船舰规则,在docker配置文件中添加 “iptables”: false
[root@localhost ~]# vi /etc/docker/daemon.json
{
"data-root": "/opt/docker",
"iptables": false
}
#重启生效
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl restart docker
此时我们就可以随便关闭我们不想放行的端口了,做到使用iptables管理Docker容器端口。
引发问题
发现以上做法可以屏蔽端口了,但是出现容器不能与宿主机其他服务器通讯,原因是加入了 “iptables”: false后,不能在iptables添加任何规则了,正常来说docker启动时不止是添加几条容器端口开放这么简单,docker容器启动时还需要在nat表添加-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE这些规则,这些规则是处理docker的SNAT地址伪装用的,直白来说就是用来容器内部访问外部网络的。具体含义是将容器网络网段发送到外部的数据包(! -o docker0)伪装成宿主机的ip,就是将数据包的原来的容器ip换成了宿主机ip做了一次snat,没经过源IP地址转换是不能正常通讯的。
#网络不通
[root@localhost ~]# docker exec -it tomcat bash
root@b287f29a1b60:/usr/local/tomcat# ping 172.21.36.43 #另一台宿主机IP
PING 172.21.36.43 (172.21.36.43) 56(84) bytes of data.
^C
--- 172.21.36.43 ping statistics ---
8 packets transmitted, 0 received, 100% packet loss, time 9ms
#iptables规则消失
[root@localhost ~]# iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
解决方法:
在docker启动配置中加入配置
vi /etc/systemd/system/docker.service
[Service]
ExecStartPre=/usr/sbin/iptables -t nat -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
ExecStopPost=/usr/sbin/iptables -t nat -D POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
#加载并重启docker
systemctl daemon-reload
systemctl restart docker
#172.17.0.0/16 IP为容器的IP网段,查看容器的IP网段,需要查看容器的网络模式,没指定的话一般为bridge
如果容器里使用了不同的网络,每个网络都要开放,没有开放的网络不能与外网通讯
问题解决,
不过iptables -I INPUT -p tcp --dport 1521 -j DROP
加入这条规制,本地也无法通过IP访问,所以容器内部要么通过容器名相互访问,要么再加一条允许本地IP访问策略
iptables -I INPUT -s 172.21.36.67 -t tcp -j ACCEPT
第二种方案
以至于后来我才发现第二种解决办法,看了第二种方案你就会觉得第一种方案有多傻,我们要实现防火墙管理Docker端口,只需要在iptables的filter表的DOCKER-USER链下添加规则即可。
#只允许10.108.11.178访问容器的1521端口
[root@data dciproject]# iptables -I DOCKER-USER ! -s 10.108.11.178 -p tcp --dport 1521 -j DROP
[root@data dciproject]# iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9090
ACCEPT tcp -- 10.108.11.178 0.0.0.0/0 tcp dpt:1521
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8091
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8085
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9695
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:7001
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8086
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8083
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8089
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:19080
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8088
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:6379
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:445
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:139
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22223
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:7443
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:7010
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:10020
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:10010
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:6443
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:6080
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:5432
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9011
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8082
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8081
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:1521
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER-USER all -- 0.0.0.0/0 0.0.0.0/0
DOCKER-ISOLATION-STAGE-1 all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain DOCKER (5 references)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 10.1.1.2 tcp dpt:8080
ACCEPT tcp -- 0.0.0.0/0 192.168.0.2 tcp dpt:5000
ACCEPT tcp -- 0.0.0.0/0 10.1.0.2 tcp dpt:9090
ACCEPT tcp -- 0.0.0.0/0 10.1.3.2 tcp dpt:5432
ACCEPT tcp -- 0.0.0.0/0 10.1.1.3 tcp dpt:8080
ACCEPT tcp -- 0.0.0.0/0 10.1.1.3 tcp dpt:1521
ACCEPT tcp -- 0.0.0.0/0 10.1.0.3 tcp dpt:8080
ACCEPT tcp -- 0.0.0.0/0 192.168.0.3 tcp dpt:8085
ACCEPT tcp -- 0.0.0.0/0 192.168.0.3 tcp dpt:80
ACCEPT tcp -- 0.0.0.0/0 10.1.0.4 tcp dpt:5432
ACCEPT tcp -- 0.0.0.0/0 10.1.1.4 tcp dpt:15675
ACCEPT tcp -- 0.0.0.0/0 10.1.1.4 tcp dpt:15672
ACCEPT tcp -- 0.0.0.0/0 10.1.1.4 tcp dpt:5672
ACCEPT tcp -- 0.0.0.0/0 10.1.1.4 tcp dpt:1883
ACCEPT tcp -- 0.0.0.0/0 10.1.1.5 tcp dpt:6379
ACCEPT tcp -- 0.0.0.0/0 10.1.1.6 tcp dpt:8080
ACCEPT tcp -- 0.0.0.0/0 10.1.0.6 tcp dpt:8080
ACCEPT tcp -- 0.0.0.0/0 10.1.1.7 tcp dpt:8080
ACCEPT tcp -- 0.0.0.0/0 10.1.1.8 tcp dpt:19080
ACCEPT tcp -- 0.0.0.0/0 10.1.0.8 tcp dpt:9191
ACCEPT tcp -- 0.0.0.0/0 10.1.0.8 tcp dpt:8080
ACCEPT tcp -- 0.0.0.0/0 10.1.1.9 tcp dpt:27017
ACCEPT tcp -- 0.0.0.0/0 10.1.0.9 tcp dpt:8080
ACCEPT tcp -- 0.0.0.0/0 10.1.0.11 tcp dpt:8000
ACCEPT tcp -- 0.0.0.0/0 10.1.0.12 tcp dpt:8080
ACCEPT tcp -- 0.0.0.0/0 10.1.0.13 tcp dpt:5432
Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target prot opt source destination
DOCKER-ISOLATION-STAGE-2 all -- 0.0.0.0/0 0.0.0.0/0
DOCKER-ISOLATION-STAGE-2 all -- 0.0.0.0/0 0.0.0.0/0
DOCKER-ISOLATION-STAGE-2 all -- 0.0.0.0/0 0.0.0.0/0
DOCKER-ISOLATION-STAGE-2 all -- 0.0.0.0/0 0.0.0.0/0
DOCKER-ISOLATION-STAGE-2 all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-ISOLATION-STAGE-2 (5 references)
target prot opt source destination
DROP all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-USER (1 references)
target prot opt source destination
DROP tcp -- !10.108.11.178 0.0.0.0/0 tcp dpt:1521
RETURN all -- 0.0.0.0/0 0.0.0.0/0
Chain l (0 references)
target prot opt source destination
[root@data dciproject]#
#永久保存iptables规则
[root@data dciproject]# yum install iptables -y && yum install iptables-services -y
[root@data dciproject]# systemctl start iptables.service && systemctl enable iptables.service
[root@data dciproject]# service iptables save
在docjer启动的时创建的众多链中,DOCKER-USER链优先级最高,用于自定义转发规则,可以限制进入容器的源IP地址:
iptables -I DOCKER-USER ! -s 10.108.11.178 -p tcp --dport 1521 -j DROP
其他记录点:docker在启动时是会在iptables上添加规则的,而firewalld服务启动时,会将iptables上的规则全部清空,并把firewalld上的防火墙策略规则写入到iptables中,此时docker因为防火墙规则被删除就会不正常,需要重启systemctl restart docker服务来重新刷写防火墙规则到iptables上,所以我们在管理Docker端口时,应该使用iptables防火墙而不是firewalld防火墙服务。
参考地址:https://blog.csdn.net/m0_49946916/article/details/109277325
又引发新的问题
按照第二种方案处理后,确实实现了iptables对docker容器的管理,实现了只允许特定IP访问特定容器端口,但又引发新的问题,容器内部之间也无法通过防火墙,这个是我没想到的,还要手动将容器的网段地址添加到DOCKER-USER链中去,才能保证容器内部相互通讯。
进入容器内部测试,发现网络不通
添加docker容器的网段
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
4ac677e2fd2f bridge bridge local
f28591fefa67 docker-project_default bridge local
f32fea4be351 host host local
dc907d235925 none null local
[root@localhost ~]#
[root@localhost ~]# docker inspect docker-project_default | grep Subnet
"Subnet": "172.18.0.0/16", #所有docker使用docker-project_default bridge的网段
[root@localhost ~]#
放行容器网段访问所有端口
[root@localhost ~]# iptables -I DOCKER-USER -p tcp -s 172.18.0.12/16 -j ACCEPT
[root@localhost ~]# iptables -n -L DOCKER-USER
Chain DOCKER-USER (1 references)
target prot opt source destination
ACCEPT tcp -- 172.18.0.0/16 0.0.0.0/0 #确保容器网络规则在第一位
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:5432
RETURN all -- 0.0.0.0/0 0.0.0.0/0
测试容器访问其他容器端口,成功
[root@localhost ~]# docker exec -it tomcat bash
root@2aedfa874301:/usr/local/tomcat# telnet postgres 5432
Trying 172.18.0.2...
Connected to postgres.
Escape character is '^]'.
三.Iptables基础命令
关闭特定端口
iptables -A INPUT -p tcp --dport 1521 -j ACCEPT
允许特定IP访问所有端口
iptables -I INPUT -s 10.108.11.178 -j ACCEPT
允许特定IP访问特定端口
iptables -I DOCKER-USER ! -s 10.108.11.178 -p tcp --dport 1521 -j DROP
删除规则
iptables -t filter -nL --line-number
iptables -D INPUT 1
清空所有规则
iptables -F
iptables -X
iptables -Z
永久保存规则
#第一种方案
yum install iptables iptables-services -y
systemctl start iptables.service && systemctl enable iptables.service
service iptables save
#第二种方案
保存
iptables-save > /etc/iptables.rules
恢复
iptables–restore < /etc/iptables.rules