iptables笔记
第一条,其他教程都不会写的,请注意:
所谓 “服务器内部(本地)、外部(外网)”,并不是指请求来自于本机还是其他外部计算机。
准确的说,iptables规则并没有内部、外部访问这个概念。而是指:通道的两端,source(源端)和 destination(目的端),两端都是一个IP范围。两端之间不一定要经过网络设备,换句话说,在同一个服务器上可以既是source又是destination,在服务器本地使用telnet、curl、wget,包括其他程序(Java、Python、Golang)等在本机发起的请求,请求时作为client,采用了自动生成的端口(见下文)自己属于source,服务方属于destination,而服务方响应返回数据时,服务方属于source,自己则是destination。HTTP的请求和响应通常是一对儿(是否可以只请求、不响应?)。
要理解这个其实比较难,需要知道网络连接的实现原理。
服务器A(10.10.2.188)的连接:
[root@dev-ipsec ~]# netstat -antpl Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 14554/sshd tcp 0 0 10.10.2.188:59722 100.100.30.26:80 ESTABLISHED 15123/AliYunDun tcp 0 52 10.10.2.188:22 10.8.228.102:51528 ESTABLISHED 31289/sshd: root@pt tcp 0 0 10.10.2.188:51972 100.100.0.70:443 TIME_WAIT -
可以看到本地端口(Local Address),外部端口(Foreign Address),以及端口号。
可以看到,每一个网络连接,本地端口号和外部端口号其中一个是自动生成的
(仔细分析和测试可知,数据接收端的端口号是固定的,而客户端-数据发送端的端口号是自动生成的)。
例如(10.9.8.187):
tcp 0 0 10.9.8.187:36646 10.9.64.28:3306 ESTABLISHED 3060/java tcp 0 0 10.9.8.187:60966 10.10.64.206:6379 ESTABLISHED 3861/node /www/app/ tcp 0 0 10.9.8.187:443 125.112.146.203:58827 ESTABLISHED 11759/nginx: worker tcp 1 0 10.9.8.187:52652 10.9.8.187:8002 CLOSE_WAIT 21693/java tcp 0 0 10.9.8.187:8443 10.9.1.61:39134 TIME_WAIT -
Foreign Address的3306是MySQL,6379是Redis,都是服务端。
但是Local Address的443、8443也是服务端。
只是有个比较奇怪:
“10.9.8.187:52652 10.9.8.187:8002 CLOSE_WAIT 21693/java”
187:8002按道理来说,是本地服务,怎么却是 “Foreign Address”?
再看一个,服务器B(10.8.228.102)的连接:
执行: wget http://10.9.8.187:8002/login wget http://10.9.8.187:80/login 然后,立即查看: [root@gateway ~]# netstat -antpl Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 10.8.228.102:80 0.0.0.0:* LISTEN 24283/python2.7 tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1354/sshd tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1471/master tcp 0 64 10.8.228.102:22 172.16.8.22:51314 ESTABLISHED 4352/sshd tcp 0 0 10.8.228.102:34308 10.9.8.187:8002 TIME_WAIT - tcp 0 0 10.8.228.102:80 10.8.228.102:44480 TIME_WAIT -
可以看到,wget后面跟的地址,第一次是Foreign Address,第二次却是Local Address。
再次尝试3次,又变成了:
tcp 0 0 10.8.228.102:44546 10.8.228.102:80 TIME_WAIT - tcp 0 0 10.8.228.102:44550 10.8.228.102:80 TIME_WAIT - tcp 0 0 10.8.228.102:44548 10.8.228.102:80 TIME_WAIT -
由此可见,Foreign Address和Local Address是可以反转的,对应的就是请求和响应,是不同的socket连接。请求时,自己是client,对方是server,响应时对方是client,自己的server。server的端口是固定的,client的端口是自动生成的。
原理:
server端不需要新的端口,所有的连接都是同一个端口,但client端,每个连接都有不同的端口。
但每个连接都是不同的sockfd。服务器在accept后,创建的新的sockfd,这个sockfd和listenfd是共用同一个port的。
在客户端client 的sockfd进行connect时,内核会自动随机给其sockfd分配一个port。
利用tcp类型的socket来建立跨主机的通信:
在server端,accept之后的所生成的socket,它们的端口都是和监听端口一样的。
(比如监听socket的端口是80,那么这些新生成的连接socket的端口也是80)
至于为何没有新绑定端口,那时因为,在client端的socket已经选择了不同的端口。
这样可行的原因在于:一个连接是由四个变量确定的,srcip,srcport,dstip,dstport。
这四个变量中只要有一个不同,就可要表示一个不同的连接了。
这种唯一性由client每次connect时候绑定的新的端口而保证了。
我们知道,服务器本地IP,有两重含义:
内部(本地、目的地)仅指 本地特殊IP(127.0.0.1、0.0.0.0,包括映射到127.0.0.1的localhost)
本地(内部)网络IP(例如192.xx、172.xx、10.xx等)
还有一个重要结论:使用telnet、curl、wget,包括其他程序(Java、Python、Golang)等在本机发起的请求,其client ip(srcip)都是当前的网络IP。(我不确定这句话是否正确和完整,但从实验来看是这样的),这对于理解netstat、iptables至关重要。一个灵魂拷问:从服务器内部访问服务器内部地址?它的请求srcip和dstip分别是什么?例如,在本机执行 “curl 本地服务”。答案:请求srcip是本地网络IP(例如192.168.31.12),dstip取决于你写的IP(有三种写法:127.0.0.1,192.168.31.12,0.0.0.0)这三种写法,对于iptables来说并不等价!!也就是说:
curl http://127.0.0.1:8080/test curl http://192.168.31.12:8080/test curl http://0.0.0.0:8080/test
这三种写法,对于iptables来说,是三个不同的dstip,如果你发现三者其中一两个telnet、curl不通,很可能是iptables规则限制了。
一个有问题的配置规则解读:(见注释)
[root@prodweb1 logs]# cat myipt.rule # Generated by iptables-save v1.4.21 on Fri Mar 17 11:48:53 2023 *filter :INPUT DROP [17543:1063956] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [49333353:17734980469] -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT # 允许外部访问,仅本地的443端口,此配置非常危险 -A INPUT -p udp -m udp --sport 53 -j ACCEPT # 允许外部访问,仅来源端口为80、53、443的请求 -A INPUT -p tcp -m tcp --sport 80 -j ACCEPT -A INPUT -p tcp -m tcp --sport 443 -j ACCEPT -A INPUT -s 10.0.0.0/8 -j ACCEPT # -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT -A INPUT -s 172.16.0.0/16 -p tcp -m tcp --dport 22 -j ACCEPT # 允许来自于172.16.0.0/16的外部访问,仅本地的22端口 -A INPUT -s 10.0.0.0/8 -p tcp -m tcp --dport 22 -j ACCEPT # 允许来自于10.0.0.0/8的外部访问,仅本地的22端口 -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT -A INPUT -s 172.16.0.0/16 -p tcp -m tcp --dport 80 -j ACCEPT # 允许来自于172.16.0.0/16的外部访问,仅本地的80端口 -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT # 允许外部访问,仅本地的80端口 -A OUTPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT -A OUTPUT -s 192.168.1.0/24 -p icmp -m icmp --icmp-type 0 -j ACCEPT -A OUTPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT -A OUTPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT -A OUTPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT COMMIT # Completed on Fri Mar 17 11:48:53 2023
对应的生效信息为:
[root@prodweb1 smartmanger]#iptables -L -n Chain INPUT (policy DROP) target prot opt source destination ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 ACCEPT all -- 10.0.0.0/8 0.0.0.0/0 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 state ESTABLISHED ACCEPT tcp -- 172.16.0.0/16 0.0.0.0/0 tcp dpt:22 ACCEPT tcp -- 10.0.0.0/8 0.0.0.0/0 tcp dpt:22 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmptype 8 ACCEPT tcp -- 172.16.0.0/16 0.0.0.0/0 tcp dpt:22 ACCEPT tcp -- 10.0.0.0/8 0.0.0.0/0 tcp dpt:22 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmptype 8 ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp spt:53 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:80 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp spt:443 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmptype 8 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmptype 8 ACCEPT tcp -- 172.16.0.0/16 0.0.0.0/0 tcp dpt:22 ACCEPT tcp -- 172.16.0.0/16 0.0.0.0/0 tcp dpt:80 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmptype 0 ACCEPT icmp -- 192.168.1.0/24 0.0.0.0/0 icmptype 0 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmptype 0 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmptype 0 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmptype 0
这个配置会导致nginx的本地转发失效:
server { listen 80; server_name login.paramland.com; location / { proxy_pass http://127.0.0.1:8002; }
参见:https://blog.csdn.net/weixin_39144798/article/details/89215656
因为,这个配置就相当于“ curl http://127.0.0.1:8002”,根据上文的分析,其srcip
关于请求,srcip是 192.168.31.12,dstport是8002,而对iptables来说,配置规则是:
[root@prodweb1 smartmanger]#iptables -L -n Chain INPUT (policy DROP) target prot opt source destination ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
它的“tcp dpt:443” 不含包8002,所以会拒绝。
解决方法:
iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT 或者 iptables -I INPUT -s 127.0.0.1 -j ACCEPT
这个规则的含义是,对于本地发起的且dstip为127.0.0.1的请求,直接放行。
注意,不能用 iptables -A INPUT -s 192.168.31.12(或者0.0.0.0) -d 127.0.0.1 -j ACCEPT
查看加的规则:
# iptables -A INPUT -s 192.168.31.12 -d 127.0.0.1 -j ACCEPT # iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT # iptables -A INPUT -s 0.0.0.0 -d 127.0.0.1 -j ACCEPT # iptables -L INPUT --line-numbers Chain INPUT (policy DROP) num target prot opt source destination 1 ACCEPT tcp -- anywhere anywhere tcp dpt:https 2 ACCEPT all -- 10.0.0.0/8 anywhere 3 ACCEPT icmp -- anywhere anywhere state ESTABLISHED 4 ACCEPT tcp -- 172.16.0.0/16 anywhere tcp dpt:ssh 5 ACCEPT tcp -- 10.0.0.0/8 anywhere tcp dpt:ssh 6 ACCEPT icmp -- anywhere anywhere icmp echo-request 7 ACCEPT tcp -- 172.16.0.0/16 anywhere tcp dpt:ssh 8 ACCEPT tcp -- 10.0.0.0/8 anywhere tcp dpt:ssh 9 ACCEPT icmp -- anywhere anywhere icmp echo-request 10 ACCEPT udp -- anywhere anywhere udp spt:domain 11 ACCEPT tcp -- anywhere anywhere tcp spt:http 12 ACCEPT tcp -- anywhere anywhere tcp spt:https 13 ACCEPT icmp -- anywhere anywhere icmp echo-request 14 ACCEPT icmp -- anywhere anywhere icmp echo-request 15 ACCEPT tcp -- 172.16.0.0/16 anywhere tcp dpt:ssh 16 ACCEPT tcp -- 172.16.0.0/16 anywhere tcp dpt:http 17 ACCEPT tcp -- anywhere anywhere tcp dpt:http 18 ACCEPT all -- prodweb1 localhost # 对应iptables -A INPUT -s 192.168.31.12 19 ACCEPT all -- localhost localhost # 对应iptables -A INPUT -s 127.0.0.1 20 ACCEPT all -- default localhost # 对应iptables -A INPUT -s 0.0.0.0
换句话说,iptables规则里面,要指本地IP,必须用127.0.0.1而不是网络IP(我也不知道为什么,但测试过了换成本地网络IP或者0.0.0.0都不会生效)。
iptables删除规则命令:
iptables -D INPUT 3 #删除INPUT的第三条已添加规则,这里3代表第几行规则
关闭命令(CentOS):(关闭后规则不会丢失)
systemctl stop iptables
删除、清空规则:(永久有效)
iptables -F
备份、还原规则:
# iptables-save > /usr/myipt.rule #备份规则至/usr/myipt.rule # iptables-restore < /usr/myipt.rule #从/usr/myipt.rule恢复规则
永久保存:
iptables-save > /etc/sysconfig/iptables