iptables限制docker端口禁止某台主机访问

背景:

在Linux上docker映射了端口,想着对服务端口进行限制指定IP访问,发现在filter表的INPUT链限制无效

环境:

主机192.168.56.132上的docker容器部署了nginx并将容器80端口映射到主机8000端口

[root@localhost ~]# docker ps -a
CONTAINER ID   IMAGE                          COMMAND                  CREATED       STATUS       PORTS                                   NAMES
79e2d2824b97   dockerproxy.cn/library/nginx   "/docker-entrypoint.…"   4 hours ago   Up 2 hours   0.0.0.0:8000->80/tcp, :::8000->80/tcp   nginx-test
[root@localhost ~]# netstat -ntpl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN      83216/docker-proxy                  
tcp6       0      0 :::8000                 :::*                    LISTEN      83220/docker-proxy

需求:

仅允许192.168.56.128主机访问此容器,其他主机全部禁止访问。
此时默认iptables策略为:

[root@localhost ~]# iptables -nL
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy DROP)
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           

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain DOCKER (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  0.0.0.0/0            172.17.0.2           tcp dpt:80

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           
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           

Chain DOCKER-ISOLATION-STAGE-2 (1 references)
target     prot opt source               destination         
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         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0   

操作:

按照常规操作添加策略

在INPUT表中直接添加drop所有地址访问8000端口

# 添加策略
[root@server ~]# iptables  -I INPUT -p tcp --dport 8000 -j DROP
# 查看
[root@server ~]# iptables -nL INPUT
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8000
使用其他机器测试发现所添加防火墙策略无效
[root@client ~]# curl  192.168.56.132:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

那尝试禁用80端口看看是否有效
[root@server ~]# iptables  -I INPUT -p tcp --dport 80 -j DROP
[root@server ~]# iptables -nL INPUT
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8000


测试连接,发现策略依旧无效。
[root@client ~]# curl  192.168.56.132:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

查找资料发现
Docker安装了两个定制的iptables链,名为DOCKER-USER和DOCKER,它确保传入的数据包总是首先由这两个链检查。
Docker的所有iptables规则都被添加到Docker链中。不要手动操作该链条。如果您需要添加在Docker规则之前加载的规则,请将它们添加到DOCKER-USER链中。这些规则是在Docker自动创建任何规则之前应用的。
添加到转发链中的规则——无论是手动添加的,还是由另一个基于iptables的防火墙添加的——都会在这些链之后进行评估。这意味着如果您通过Docker暴露一个端口,无论您的防火墙配置了什么规则,该端口都会被暴露。如果您希望这些规则即使在端口通过Docker暴露时也适用,那么您必须将这些规则添加到DOCKER-USER链中。

在DOCKER-USER链中配置策略

Docker容器创建时会自动创建iptables策略,Docker使用的规则链是DOCKER-USER,所以需使用iptables对DOCKER-USER链做限制才可以完成需求。
加上试试

# 先删掉return的策略,不然会被直接返回进行下一步操作
[root@server ~]# iptables  -D DOCKER-USER -j RETURN
[root@server ~]# iptables  -I DOCKER-USER -p tcp --dport 8000 -j DROP
[root@server ~]# iptables -nL DOCKER-USER
Chain DOCKER-USER (1 references)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8000     
访问测试,还是不行!?
[root@client ~]# curl  192.168.56.132:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
加上日志追踪看一下到底什么原因
[root@server ~]# echo 'kern.=debug     /var/log/kern.debug.log' >>  /etc/rsyslog.conf 
[root@server ~]# touch /var/log/kern.debug.log 
[root@server ~]# systemctl restart rsyslog

[root@server ~]# iptables -I DOCKER-USER 1  -p tcp  -m limit --limit 4/min -j LOG --log-prefix "Iptables-DROP: " --log-level 7
[root@server ~]# iptables -nL DOCKER-USER
Chain DOCKER-USER (1 references)
target     prot opt source               destination         
LOG        tcp  --  0.0.0.0/0            0.0.0.0/0            limit: avg 4/min burst 5 LOG flags 0 level 7 prefix "Iptables-DROP: "
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8000  

# 其他机器测试连接一下,肯定是可以连通的。
[root@client ~]# curl  192.168.56.132:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

# 查看日志
[root@server ~]# tailf /var/log/kern.debug.log 
Oct 28 14:55:45 server kernel: Iptables-DROP: IN=ens33 OUT=docker0 MAC=00:0c:29:87:29:40:00:50:56:c0:00:08:08:00 SRC=192.168.56.1 DST=172.17.0.2 LEN=40 TOS=0x00 PREC=0x00 TTL=127 ID=35633 DF PROTO=TCP SPT=43556 DPT=80 WINDOW=0 RES=0x00 ACK FIN URGP=0 

# 重点部分
SRC=192.168.56.1 # 源地址
DST=172.17.0.2  # 目标地址
DPT=80 # 目标端口

在日志中发现原地址,目标地址和目标端口都有,现在就发现问题了,目标端口不是我们所映射出来的8000端口而是容器内部的80端口,那尝试禁止访问80端口试试

[root@server ~]# iptables  -I DOCKER-USER -p tcp --dport 80 -j DROP
[root@server ~]# iptables -nL DOCKER-USER
Chain DOCKER-USER (1 references)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
LOG        tcp  --  0.0.0.0/0            0.0.0.0/0            limit: avg 4/min burst 5 LOG flags 0 level 7 prefix "Iptables-DROP: "
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8000
        
测试访问,发现被拦截了,那我们的需求就达到了,只要放开想要放行的IP地址就可以了,
[root@client ~]# curl  192.168.56.132:8000
curl: (7) Failed to connect to 192.168.56.132 port 8000: Connection timed out

#server端放行192.168.56.128
[root@server ~]# iptables  -I DOCKER-USER -s 192.168.56.128 -p tcp --dport 80 -j ACCEPT
[root@server ~]# iptables -nL DOCKER-USER
Chain DOCKER-USER (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  192.168.56.128       0.0.0.0/0            tcp dpt:80
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
LOG        tcp  --  0.0.0.0/0            0.0.0.0/0            limit: avg 4/min burst 5 LOG flags 0 level 7 prefix "Iptables-DROP: "
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8000

#再次测试,发现可行了,找个其他机器测试一下,发现除了放行的地址其他地址无法访问。
[root@client ~]# curl  192.168.56.132:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

会出现的问题:

问题复现:

上面操作就完成了我们的需求,但是这样做会有一个问题,如果我机器上容器比较多,多个容器内部端口都是80端口,那是不是也就不通了,答案是:是的,因为他是根据内部端口禁用了。
再拉一个nginx测试一下,映射了一个9000端口出来


[root@server ~]# docker ps
CONTAINER ID   IMAGE                          COMMAND                  CREATED         STATUS             PORTS                                   NAMES
4a7040a0040c   dockerproxy.cn/library/nginx   "/docker-entrypoint.…"   6 minutes ago   Up 6 minutes       0.0.0.0:9000->80/tcp, :::9000->80/tcp   nginx-test-2
79e2d2824b97   dockerproxy.cn/library/nginx   "/docker-entrypoint.…"   5 hours ago     Up About an hour   0.0.0.0:8000->80/tcp, :::8000->80/tcp   nginx-test
# 他会自动在DOCKER链中生成一条新的,不用管它。
[root@server ~]# iptables -nL  DOCKER
Chain DOCKER (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  0.0.0.0/0            172.17.0.2           tcp dpt:80
ACCEPT     tcp  --  0.0.0.0/0            172.17.0.3           tcp dpt:80
# 我们将放行的机器剔除
root@server ~]# iptables  -D DOCKER-USER -s 192.168.56.128 -p tcp --dport 80 -j ACCEPT
[root@server ~]# iptables -nL  DOCKER-USER
Chain DOCKER-USER (1 references)
target     prot opt source               destination         
LOG        tcp  --  0.0.0.0/0            0.0.0.0/0            limit: avg 4/min burst 5 LOG flags 0 level 7 prefix "Iptables-DROP: "
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
# 测试访问两个端口,发现都无法访问,这就很可能会影响我们其他容器的业务

[root@client ~]# curl  192.168.56.132:9000
curl: (7) Failed to connect to 192.168.56.132 port 9000: Connection timed out

[root@client ~]# curl  192.168.56.132:8000
curl: (7) Failed to connect to 192.168.56.132 port 8000: Connection timed out

解决方法:

解决方法一:

策略指定目的地址,我们每创建一个容器他都会生成一个他自己的地址,在加防火墙的时候增加上目的地址可以解决此问题。
例如:我现在9000端口的容器就想要被所有人访问,8000端口只让192.168.56.128一台可以访问,可以这样做(就是比较麻烦,首先得获取每个容器的ip地址)

# 先清理掉之前的规则
[root@server ~]# iptables  -D DOCKER-USER -p tcp --dport 80 -j DROP
# 增加一条带有目的地址的策略
[root@server ~]# iptables  -A DOCKER-USER -d 172.17.0.2 -p tcp --dport 80 -j DROP
[root@server ~]# iptables -nL  DOCKER-USER
Chain DOCKER-USER (1 references)
target     prot opt source               destination         
LOG        tcp  --  0.0.0.0/0            0.0.0.0/0            limit: avg 4/min burst 5 LOG flags 0 level 7 prefix "Iptables-DROP: "
DROP       tcp  --  0.0.0.0/0            172.17.0.2           tcp dpt:80

# 测试,达成了我们的目的
[root@client ~]# curl  192.168.56.132:9000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

[root@client ~]# curl  192.168.56.132:8000
curl: (7) Failed to connect to 192.168.56.132 port 8000: Connection timed out

#然后放行我们想要放行的地址即可
[root@server ~]# iptables  -I DOCKER-USER -s 192.168.56.128 -d  172.17.0.2 -p tcp --dport 80 -j ACCEPT
[root@server ~]# iptables -nL  DOCKER-USER
Chain DOCKER-USER (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  192.168.56.128       172.17.0.2           tcp dpt:80
LOG        tcp  --  0.0.0.0/0            0.0.0.0/0            limit: avg 4/min burst 5 LOG flags 0 level 7 prefix "Iptables-DROP: "
DROP       tcp  --  0.0.0.0/0            172.17.0.2           tcp dpt:80

#测试访问
[root@client ~]# curl  192.168.56.132:9000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
[root@client ~]# curl  192.168.56.132:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
解决方法二:

在raw表中增加策略

上一篇:四款语音转文字免费的软件深度体验分享


下一篇:钉钉日常报销单与金蝶云星空集成技术详解