一、封端口和常用参数
1. 先来个最简单的例子
配置前要清空:
iptables -F # 清空默认规则
iptables -X # 清空自定义链的规则
iptables -Z # 清空统计与计数
[root@localhost ~]# iptables -F
[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
# 拒绝所有人访问22端口(不要真的执行啊!)
# 添加规则(指定表、链、规则)
# 涉及端口必须指定 协议和端口
iptables -t filter -I INPUT -p tcp --dport 22 -j DROP
# 网络中端口有两种:源端口和目标端口
# 删掉规则(-F 肯定慎用)
iptables -t filter -D INPUT 1
# 1 表示删第一条,查看行数:
iptables -nL --line-number
建议顺手配个定时任务,隔几分钟把防火墙关掉,免得自己进不去。
2. 三段参数总结
iptables 的命令我习惯拆成三段看:前面是表、中间是条件、尾端是动作。
中间这一段是匹配条件:

尾端这一段是匹配上之后干什么:

考虑到安全性,一般用 DROP。这里要注意 DROP 和 REJECT 在客户端那头的表现完全不一样:DROP 什么都不回,客户端只会一直等到超时或者自己重试;REJECT 会把拒绝信息返回给客户端,客户端立刻收到"连接被拒绝"。
对链本身进行操作的是另外几个参数,记住 -A -I -D 都是对链动手就很好理解了:

补一句:-P 是修改链的默认规则,跟 -A/-I/-D 不是一回事。
顺带把常用的"层"也理一下,写规则的时候心里要清楚自己在拦哪一层:

二、filter 表案例 1
1. 屏蔽指定 IP
clone 一台机器出来:128 当操作机,129 当测试机。在 128 上操作,在 129 上看效果。
# 在129测试ping
[root@localhost ~]# ping 192.168.73.128
PING 192.168.73.128 (192.168.73.128) 56(84) 字节的数据。
64 字节,来自 192.168.73.128: icmp_seq=1 ttl=64 时间=0.363 毫秒
64 字节,来自 192.168.73.128: icmp_seq=2 ttl=64 时间=1.46 毫秒
64 字节,来自 192.168.73.128: icmp_seq=3 ttl=64 时间=1.14 毫秒
64 字节,来自 192.168.73.128: icmp_seq=4 ttl=64 时间=1.53 毫秒
# 屏蔽129的IP(对于128,129是原IP)
# 端口需要加协议,只有IP不用
iptables -t filter -I INPUT -s 192.168.73.129 -j DROP
root@localhost ~]# iptables -t filter -I INPUT -s 192.168.73.129 -j DROP
[root@localhost ~]# iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP all -- 192.168.73.129 0.0.0.0/0
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
# 在129 ping没有显示毫秒而且卡住,失败了
[root@localhost ~]# ping 192.168.73.128
PING 192.168.73.128 (192.168.73.128) 56(84) 字节的数据。
# 删除规则(ping成功了)
iptables -D INPUT 1
# 如果是reject会提示
[root@localhost ~]# ping 192.168.73.128
PING 192.168.73.128 (192.168.73.128) 56(84) 字节的数据。
来自 192.168.73.128 icmp_seq=1 目标端口不可达
来自 192.168.73.128 icmp_seq=2 目标端口不可达
来自 192.168.73.128 icmp_seq=3 目标端口不可达
# 如果是屏蔽指定网段--CIDR表示法(屏蔽了256个)
# 注意这里会把自己也踢出去,因为ssh走的是双向通信
# win(客户端)的IP发起ssh连接,服务器INPUT放行才能连
iptables -t filter -I INPUT -s 192.168.73.0/24 -j DROP
# win和虚拟机能连上,肯定在一个内网,如果屏蔽了win所在网段,就断ssh
所以如果是屏蔽网段,要先放行 ssh 连接的 IP。如果是屏蔽指定 IP,通常是从访问日志、错误日志、请求次数太多的记录里把那个 IP 揪出来的。
2. 屏蔽网段和端口
禁止 192.168.73.0/24 访问 8848 端口:
# 使用 netcat (nc) 工具,在 8848 端口上启动一个 TCP 服务器
#用来持续监听并接收网络连接
nc -kl 8848
# tcp通信:连接远程主机
telnet 192.168.73.129 8848
[root@localhost ~]# telnet 192.168.73.129 8848
Trying 192.168.73.129... # 尝试连接
Connected to 192.168.73.129. # 连接成功
Escape character is '^]'. # Ctrl + ] 退出连接(没连上就是Ctrl + C)
# 此时输入,对方能收到
# 之后输入quit才能退出
telnet> exit
?Invalid command
telnet> quit
Connection closed.
[root@localhost ~]#
# 在129屏蔽128的端口
iptables -t filter -I INPUT -s 192.168.73.0/24 -p tcp --dport 8848 -j DROP
# 再开启8848,隔壁telnet发送数据,发现无法连接/超时
3. 类似白名单的方式(只允许指定网段访问)
如果只屏蔽一个网段,其他网段还是能访问;如果只想让某些网段访问,就只能反过来配允许规则,也就是白名单:
# 1. 利用 ! 先拒绝后加叹号
iptables -I INPUT -s 192.168.73.0/24 -j DROP
# 在中段参数前加叹号:拒绝不在该网段的IP
iptables -I INPUT ! -s 192.168.73.0/24 -j DROP
# 局限性就是,如果允许的网段多了,也需要写好多
# 2. 调整默认规则(ACCEPT变为DROP),然后添加允许规则
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# OUTPUT 通常保持 ACCEPT(本机对外访问不受限)
这么写有点类似于 nginx 里的:
allow 网段
deny all
4. 一次指定多个端口(逗号和冒号)
可以写多条规则,也可以用 -m 指定功能模块 multiport,端口之间用逗号分隔:
-m multiport -p tcp --dport 80,443
iptables -I INPUT -m multiport -p tcp --dport 80,443 -j DROP
# 如果端口有规律,可以用 冒号(1~1024)
# 注意先允许22,而且22在前面,先匹配放行
iptables -I INPUT -p tcp --dport 22 -j ACCEPT
iptables -I INPUT -m multiport -p tcp --dport 1:1024 -j DROP
5. 禁 ping:限制 ICMP 类型
服务器不想让外网 ping,怕被扫,那就把 ping 禁掉。两条路:改内核参数,或者加 iptables 规则。
用 -p 指定 icmp 协议,--icmp-type 指定 icmp 类型为 8(和 ping 有关);也可以不指定类型,直接禁掉所有 icmp。
防火墙规则禁止:
# 指定icmp类型
iptables -I INPUT -p icmp --icmp-type 8 -j DROP
# 禁止所有icmp
iptables -I INPUT -p icmp -j DROP
# 效果和之前一样不演示了
内核参数禁止:
# 先删除之前的icmp防火墙规则
iptables -D INPUT 1
# 内核参数写入
vim /etc/sysctl.conf
[root@localhost ~]# vim /etc/sysctl.conf
net.ipv4.icmp_echo_ignore_all = 1
# 加载 /etc/sysctl.conf 文件下的所有参数,使配置生效
# 看到输出,测试ping确实不通
sysctl -p
[root@localhost ~]# sysctl -p
net.ipv4.icmp_echo_ignore_all = 1
# 恢复直接改为 0 即可、ping通
[root@localhost ~]# sysctl -p
net.ipv4.icmp_echo_ignore_all = 0
补充:sysctl -a 看所有的内核参数。
想让改动临时生效(上面那种是永久的):
# 所有的内核参数在 /proc/sys
[root@localhost ~]# ll /proc/sys
总计 0
dr-xr-xr-x. 1 root root 0 8月29日 20:04 abi
dr-xr-xr-x. 1 root root 0 8月29日 12:30 crypto
dr-xr-xr-x. 1 root root 0 8月29日 20:04 debug
dr-xr-xr-x. 1 root root 0 8月29日 20:04 dev
dr-xr-xr-x. 1 root root 0 8月29日 12:30 fs
dr-xr-xr-x. 1 root root 0 8月29日 12:30 kernel
dr-xr-xr-x. 1 root root 0 8月29日 12:30 net
dr-xr-xr-x. 1 root root 0 8月29日 20:04 user
dr-xr-xr-x. 1 root root 0 8月29日 12:30 vm
# -d 只查看目录本身的信息
[root@localhost ~]# ll -d /proc/sys
dr-xr-xr-x. 1 root root 0 8月29日 12:30 /proc/sys
# 配置临时的,直接在目录后把参数的 . 换成 / 即可
# 先看一下
ll -d /proc/sys/net/ipv4/icmp_echo_ignore_all
[root@localhost ~]# ll -d /proc/sys/net/ipv4/icmp_echo_ignore_all
-rw-r--r--. 1 root root 0 8月29日 20:07 /proc/sys/net/ipv4/icmp_echo_ignore_all
cat /proc/sys/net/ipv4/icmp_echo_ignore_all
0
# 0 是默认值,改为 1 禁止icmp(临时,重启恢复)
echo 1 >/proc/sys/net/ipv4/icmp_echo_ignore_all
# 覆盖写入,改回
echo 0 >/proc/sys/net/ipv4/icmp_echo_ignore_all
改 /etc/sysctl.conf 里的参数,本质上改的就是 /proc/sys 下深层目录里的那些文件。另外 > 是覆盖,>> 是追加,别写错。