目录
tcp扩展模块
multiport扩展模块
iprange扩展模块
connlimit模块
limit扩展模块
udp扩展模块
icmp扩展模块
state扩展模块
限制每分钟接收10个ICMP数据报文
允许10个数据报文快速通过,然后限制每分钟接收1个个ICMP数据报文
限制网络传输的带宽不可以超过500k/s
tcp扩展模块
-p tcp -m tcp --sport
用于匹配tcp协议报文的源端口,可以使用冒号指定一个连续的端口范围
-p protocol,
- -m
:match,指匹配的模块,很多人可能以为是module的缩写,其实是match的缩写,
- --sport
: source port);
-p tcp -m tcp - --dport
用于匹配tcp协议报文的目标端口,可以使用冒号指定一个连续的端口范围(
--dport 80:88
这里是我们正常访问80端口的页面
这面的规则是拒绝了来自192.168.159.1无法使用tcp访问本机(192.168.159.200)的80:88端口
iptables -t filter -I INPUT -s 192.168.159.1 -m tcp -p tcp --dport 80:88 -j REJECT
我们可以尝试使用22端口ssh来测试一下:
可以看到我们还是访问成功了,这是为什么呢,就是因为我们设置的规则它是开区间的,80:88,但是并不包括80,所以我们应该设置为79:88。
修改完成后再测试:
查看iptables表我们也可以发现这条拒绝规则收到了数据
此外,tcp扩展模块还有-tcp-flags选项,它可以根据TCP头部的“标识位”来匹配。
注:这里只能封连续的端口,无法封分散的端口,下面的multiport就可以解决这个问题
multiport扩展模块
-p tcp -m multiport --sports
用于匹配报文的源端口,可以指定离散的多个端口号,端口之间用”逗号”隔开;
-p udp -m multiport --dports
用于匹配报文的目标端口,可以指定离散的多个端口号,端口之间用”逗号”隔开:
iptables -t filter -I INPUT -s 192.168.159.1 -m multiport -p tcp --dport 80,22 -j REJECT
这里拒绝了来自192.168.159.1 目标80 和 22 端口的流量
我们刚敲完这条命令就会发现由于拒绝了22端口,我们的远程连接直接断开了
那么再测试一下80端口:
这里很明显也拒绝成功了!
iprange扩展模块
使用iprange扩展模块可以指定”一段连续的IP地址范围”,用于匹配报文的源地址或者目标地址。iprange扩展模块中有两个扩展匹配条件可以使用:
– --src-range
(匹配源地址范围)
– --dst-range
(匹配目标地址范围)
iptables -t filter -I INPUT -m iprange --src-range 192.168.159.201-192.168.159.222 -j DROP
上面这条规则的意思就是封堵了192.168.159.201-222ip
那么我们可以尝试使用192,168,159.201尝试ping一下本机
可以看到一直卡在这里,说明规则确实生效了,DROP掉了我们的icmp包
connlimit模块
使用connlimit扩展模块,可以限制每个IP地址同时链接到server端的链接数量
注意:我们不用指定IP,其默认就是针对”每个客户端IP”,即对单IP的并发连接数限制。(可以限制并发连接,即最多只能有两个进程)
限制22端口(ssh默认端口)连接数量上限不能超过2个;
iptables -t filter -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 2 -j REJECT
表示连接数量只要不超过两个就允许连接,至于超过两个并不一定不允许连接,这得看默认策略是ACCEPT还是DROP或REJECT,又或者有其它规则对它进行限制。
可以配合--connlimit-mask
来限制网段:
iptables -t filter -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 2 --connlimit-mask 24 -j REJECT
网址由32位二进制组成,最大可写成:255.255.255.255,而mask就是掩码(网络知识,请自行了解),24表示24个1,即255.255.255.0。
我们在192.168.159.200上面配置了限制只能连接两个的规则
连接测试一下:
limit扩展模块
limit模块是限速用的,用于限制“单位时间内流入的数据包的数量”。
每6位秒放行一下ping包(因为1分钟是60秒,所以1分钟10个包,就相当于每6秒1个包):
iptables -t filter -I INPUT -p icmp -m limit --limit 10/minite -j ACCEPT
//默认规则需要修改为DROP
然后需要将默认规则修改为DROP,否则会走默认的ACCEPT规则
iptables -P INPUT DROP
注:修改万模默认规则远程连接可能会断开,不过没有关系,用别的主机测试即可
--limit
后面的单位除了minite,还可以是second、hour、day
--limit-burst
: burst是爆发、迸发的意思,在这里是指最多允许一次性有几个包通过,要理解burst,先看以下的“令牌桶算法”。
注:可以防御DDos攻击
令牌桶算法: 有一个木桶,木桶里面放了5块令牌,而且这个木桶最多也只能放下5块令牌,所有报文如果想要出关入关,都必须要持有木桶中的令牌才行,这个木桶有一个神奇的功能,就是每隔6秒钟会生成一块新的令牌,如果此时,木桶中的令牌不足5块,那么新生成的令牌就存放在木桶中,如果木桶中已经存在5块令牌,新生成的令牌就无处安放了,只能溢出木桶(令牌被丢弃),如果此时有5个报文想要入关,那么这5个报文就去木桶里找令牌,正好一人一个,于是他们5个手持令牌,快乐的入关了,此时木桶空了,再有报文想要入关,已经没有对应的令牌可以使用了,但是,过了6秒钟,新的令牌生成了,此刻,正好来了一个报文想要入关,于是,这个报文拿起这个令牌,就入关了,在这个报文之后,如果很长一段时间内没有新的报文想要入关,木桶中的令牌又会慢慢的积攒了起来,直到达到5个令牌,并且一直保持着5个令牌,直到有人需要使用这些令牌,这就是令牌桶算法的大致逻辑。
看完了“令牌桶算法”,其实--limit
就相当于指定“多长时间生成一个新令牌”,而--limit-burst
则用于指定木桶中最多存放多少块令牌。
我们在192.168.159.200上面配置了上面的那一条规则,然后尝试使用192.168.159.201ping200看看效果:
可以看到从第六个包开始下面的每一个包都是间隔了6秒发送的
udp扩展模块
udp扩展模块中能用的匹配条件比较少,只有两个,就是--sport
与--dport
,即匹配报文的源端口与目标端口。
例如放行samba服务的137和138端口:
iptables -t filter -I INPUT -p udp -m udp --dport 137 -j ACCEPT
iptables -t filter -I INPUT -p udp -m udp --dport 138 -j ACCEPT
udp扩展中的--sport
与--dport
与tcp一样,同样支持指定一个连续的端口范围:
iptables -t filter -I INPUT -p udp --dport 137:157 -j ACCEPT
另外与tcp一样,udp也能使用--multiport
指定多个不连续的端口。
icmp扩展模块
ping是使用icmp协议的,假设要禁止所有icmp协议的报文进入本机(根据前面所说,我们可以省略用-m icmp
来指定使用icmp模块,因为不指定它会默认使用-p
指定的协议对应的模块):
iptables -t filter -I INPUT -p icmp -j REJECT
上述命令能产生两个效果:
– 1、别人ping本机时,无法ping通,因为数据报文无法进入;
– 2、本机ping别人时,虽然数据包可以出去,但别人的响应包也是icmp协议,无法进来(即“有去无回”)。
所以这样设置会导致,不止别人ping不通本机,本机也ping不通别人。
很明显上边的规则不是我们想要的,我们想要的一般都是允许本机ping别人,不允许别人ping本机:
iptables -t filter -I INPUT -p icmp --icmp-type 8/0(type为8,code为0) -j REJECT
--icmp-type 8/0
用于匹配报文type为8,code为0时才会被匹配到,至于会是type和code,这是icmp协议的知识。
其实上边的命令还可以省略code(即把“8/0”写成“8”即可,省略掉“/0”,原因是type=8的报文中只有code=0一种,所以我们不写默认就是code=0,不会有其它值):
iptables -t filter -I INPUT -p icmp --icmp-type 8 -j REJECT
除了能用type/code来匹配icmp报文,还可以使用icmp的描述名称来匹配:
iptables -t filter -I INPUT -p icmp --icmp-type "echo-request" -j REJECT
--icmp-type "echo-request"
的效果与icmp --icmp-type 8/0
或icmp --icmp-type 8
的效果完全一样(你可能发现了,icmp协议的描述“echo-request”其实是“echo request”,只不过我们用于作为匹配条件时,要把空格换成横杠)。
例如:
在192.168.159.200上进行配置
iptables -t filter -I INPUT -p icmp --icmp-type 8/0(type为8,code为0) -j REJECT
这里拒绝了给本机里面走的8/0,所以别人无法ping你
配置完成后
本主机ping别人:
别人ping本主机:
注:这里可以使用Wireshark抓包查看
200->201的icmp包:
后面就正常的ping
201->200的icmp包
这里就会拒绝这个包
state扩展模块
在TCP/IP协议簇中,UDP和ICMP是没有所谓的连接的,但是对于state模块来说,tcp报文、udp报文、icmp报文都是有连接状态的,我们可以这样认为,对于state模块而言,只要两台机器在”你来我往”的通信,就算建立起了连接。
而”连接”中的报文可以分为5种状态,报文状态可以为NEW(连接的第一个包)、ESTABLISHED(表示连接已经建立)、RELATED(相关联的连接,当前连接是一个新请求,但是附属于某个已经存在的连接)、INVALID(没有办法识别的包的状态)、UNTRACKED(报文未追踪)。
放行RELATED和ESTABLISHED状态的报文:
iptables -t filter -I INPUT -m state --state RELATED, ESTABLISHED -j ACCEPT
iptables -t filter -A INPUT -j REJECT
//这两条命令也可以防止nmap的端口扫描
当我们通过http的url访问某个网站的网页时,客户端向服务端的80端口发起请求,服务端再通过80端口响应我们的请求,于是,作为客户端,我们似乎应该理所应当的放行80端口,以便服务端回应我们的报文可以进入客户端主机,于是,我们在客户端放行了80端口,同理,当我们通过ssh工具远程连接到某台服务器时,客户端向服务端的22号端口发起请求,服务端再通过22号端口响应我们的请求,于是我们理所应当的放行了所有22号端口,以便远程主机的响应请求能够通过防火墙,但是,作为客户端,如果我们并没有主动向80端口发起请求,也没有主动向22号端口发起请求,那么其他主机通过80端口或者22号端口向我们发送数据时,我们可以接收到吗?应该是可以的,因为我们为了收到http与ssh的响应报文,已经放行了80端口与22号端口,所以,不管是”响应”我们的报文,还是”主动发送”给我们的报文,应该都是可以通过这两个端口的,那么仔细想想,这样是不是不太安全呢?如果某些与你敌对的人,利用这些端口”主动”连接到你的主机,你肯定会不爽的吧,一般都是我们主动请求80端口,80端口回应我们,但是一般不会出现80端口主动请求我们的情况吧。
你心里可能会这样想:我知道哪些主机是安全的,我只要针对这些安全的主机放行对应的端口就行了,其他IP一律拒绝,比如,我知道IP为123的主机是安全的,所以,我对123主机开放了22号端口,以便123主机能够通过22号端口响应我们的ssh请求,
那么,如果你需要管理的主机越来越多呢?你是不是每次都要为新的主机配置这些规则呢?如果有30台主机呢?如果有300台主机呢?80端口就更别提了,难道你每次访问一个新的网址,都要对这个网址添加信任吗?这显然不太合理。
你心里可能又会想:针对对应的端口,我用–tcp-flags去匹配tcp报文的标志位,把外来的”第一次握手”的请求拒绝,是不是也可以呢?那么如果对方使用的是UDP协议或者ICMP协议呢?似乎总是有一些不完美的地方。
那么我们仔细的思考一下,造成上述问题的”根源”在哪里,我们为了让”提供服务方”能够正常的”响应”我们的请求,于是在主机上开放了对应的端口,开放这些端口的同时,也出现了问题,别人利用这些开放的端口,”主动”的攻击我们,他们发送过来的报文并不是为了响应我们,而是为了主动攻击我们,好了,我们似乎找到了问题所在?
问题就是:怎样判断这些报文是为了回应我们之前发出的报文,还是主动向我们发送的报文呢?
我们可以通过iptables的state扩展模块解决上述问题,但是我们需要先了解一些state模块的相关概念,然后再回过头来解决上述问题。
从字面上理解,state可以译为状态,但是我们也可以用一个高大上的词去解释它,state模块可以让iptables实现”连接追踪”机制。
那么,既然是”连接追踪”,则必然要有”连接”。
咱们就来聊聊什么是连接吧,一说到连接,你可能会下意识的想到tcp连接,但是,对于state模块而言的”连接”并不能与tcp的”连接”画等号,在TCP/IP协议簇中,UDP和ICMP是没有所谓的连接的,
但是对于state模块来说,tcp报文、udp报文、icmp报文都是有连接状态的,我们可以这样认为,对于state模块而言,只要两台机器在”你来我往”的通信,就算建立起了连接,如下图所示
而报文在这个所谓的链接中是什么状态的呢?这是我们后面讨论的话题。
对于state模块的连接而言,”连接”其中的报文可以分为5种状态,报文状态可以为NEW、ESTABLISHED、RELATED、INVALID、UNTRACKED
那么上述报文的状态都代表什么含义呢?我们先来大概的了解一下概念,然后再结合示例说明。
注意:如下报文状态都是对于state模块来说的。
-
NEW:连接中的第一个包,状态就是NEW,我们可以理解为新连接的第一个包的状态为NEW。
-
ESTABLISHED:我们可以把NEW状态包后面的包的状态理解为ESTABLISHED,表示连接已建立。
或许用图说话更容易被人理解
-
RELATED:从字面上理解RELATED译为关系,但是这样仍然不容易理解,我们举个例子。
比如FTP服务,FTP服务端会建立两个进程,一个命令进程(20),一个数据进程(21)。
命令进程负责服务端与客户端之间的命令传输(我们可以把这个传输过程理解成state中所谓的一个”连接”,暂称为”命令连接”)。
数据进程负责服务端与客户端之间的数据传输 ( 我们把这个过程暂称为”数据连接” )。
但是具体传输哪些数据,是由命令去控制的,所以,”数据连接”中的报文与”命令连接”是有”关系”的。
那么,”数据连接”中的报文可能就是RELATED状态,因为这些报文与”命令连接”中的报文有关系。
(注:如果想要对ftp进行连接追踪,需要单独加载对应的内核模块nf_conntrack_ftp,如果想要自动加载,可以配置/etc/sysconfig/iptables-config文件)
INVALID:如果一个包没有办法被识别,或者这个包没有任何状态,那么这个包的状态就是INVALID,我们可以主动屏蔽状态为INVALID的报文。
UNTRACKED:报文的状态为untracked时,表示报文未被追踪,当报文的状态为Untracked时通常表示无法找到相关的连接。
好了,我们已经大致了解了state模块中所定义的5种状态,那么现在,我们回过头想想刚才的问题。
刚才问题的根源就是:怎样判断报文是否是为了回应之前发出的报文。
刚才举例中的问题即可使用state扩展模块解决,我们只要放行状态为ESTABLISHED的报文即可,因为如果报文的状态为ESTABLISHED,那么报文肯定是之前发出的报文的回应,如果你还不放心,可以将状态为RELATED或ESTABLISHED的报文都放行,这样,就表示只有回应我们的报文能够通过防火墙,如果是别人主动发送过来的新的报文,则无法通过防火墙,示例如下。
iptables -F
iptables -t filter -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
//接受回复的ESTABLISHED的连接
iptables -t filter -A INPUT -j REJECT
//直接连接的NEW会被拒绝掉
实验验证:
(1)在192.168.159.200上进行配置
[root@centos111 ~]# iptables -F
[root@centos111 ~]# iptables -t filter -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
[root@centos111 ~]# iptables -t filter -A INPUT -j REJECT
(2)在200上尝试远程登录192.168.159.201
[root@centos111 ~]# ssh root@192.168.159.201
root@192.168.159.201's password:
Last failed login: Sun Nov 19 09:55:47 CST 2023 from 192.168.159.200 on ssh:notty
There was 1 failed login attempt since the last successful login.
Last login: Sun Nov 19 09:02:50 2023 from 192.168.159.1
[root@centos222 ~]#
//这里成功的连接了
(3)在201上尝试远程连接200
[root@centos222 ~]# ssh root@192.168.159.200
ssh: connect to host 192.168.159.200 port 22: Connection refused
验证成功!
下面介绍几个实用的案例:
限制每分钟接收10个ICMP数据报文
1.先添加一条规则,限制1分钟内只收10个icmp协议的请求报文。
iptables -t filter -I INPUT -p icmp -m limit --limit 10/minute -j ACCEPT
2.然后添加第二条规则,超出10个报文后,其余的报文全部丢弃。
iptables -t filter -A INPUT -p icmp -j DROP
3.查看添加的防火墙规则
[root@centos222 ~]# iptables -t filter -nvxL INPUT --line-numbers
Chain INPUT (policy ACCEPT 171 packets, 10464 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 10/min burst 5
2 0 0 DROP icmp -- * * 0.0.0.0/0 0.0.0.0/0
查看效果,可以看到前5个数据包都可以正常接收,那是因为limit默认初始包就是5个,因此没有任何问题,从第六个数据包开始后,每隔6秒才会正常处理一个数据包,因为1分钟限制10次数据包的接收,那就表示6秒收一个,效果如下图所示。
也可以理解为是一开始可以快速通过5个数据报文,后面的数据报文是每分钟通过10个。
允许10个数据报文快速通过,然后限制每分钟接收1个个ICMP数据报文
实现思路和1)中一模一样,只不过就是调整一下初始包的数量。
1.先添加一条规则,限制1分钟内只收10个icmp协议的请求报文。
iptables -t filter -I INPUT -p icmp -m limit --limit 1/m --limit-burst 10 -j ACCEPT
2.然后添加第二条规则,超出10个报文后,其余的报文全部丢弃。
iptables -t filter -A INPUT -p icmp -j DROP
效果如下,首先发送了10个数据包正常接收,剩下的数据包每隔1分钟接收1个。
限制网络传输的带宽不可以超过500k/s
限速如何通过limit模块来实现呢?其实也很简单,我们可以计算出1个数据包的大小,然后用500k的大小除以数据包的大小,得出500k能换算出多少个数据包,例如是300个,那么最后设置1秒钟接收的数据包的数量为300个,就可以实现每秒的传输速率带宽在500k左右。
一个数据包的大小大概在1500字节。
公式:(限制的带宽速率*1000)/(单个数据包的大小)
500k的限速,每秒可限制的数据包数量大概为(500*1000/150 = 300个)300个。
[root@centos111 ~]# iptables -t filter -I INPUT -p tcp -m limit --limit 300/s -j ACCEPT
[root@centos111 ~]# iptables -t filter -A INPUT -p tcp -j DROP
速率带宽多多少少是由落差的,如果就想限制在500k以内,可以根据测试的结果来调整每秒数据包的效果,如下图所示,拉取文件的速率为327k/s,此时数据包的数量我是设置的120个。
我们尝试在配置规则完成后的主机上测试传输一个东西:
首先可以使用find命令找一个比较大的文件:
find / -type f -size +100M
然后我使用scp 将这个文件传入到一个目录下,查看它的传输速度:
scp /usr/lib64/firefox/libxul.so root@192.168.159.200:/tmp
但是这里并没有生效,只是传输的很慢而已,大概两分钟传输完了
我们试着清理掉防火墙规则后再测试一下:
这里可以看到1秒中传输完了,这说明我们前面的限速还是有作用的,只是没有达到预期的那么慢
到这里iptables的基础知识和一些常用的模块的基本使用就介绍完毕了