转载Linux下使用 ipset 封大量IP及ipset参数说明
Linux使用iptables封IP,是常用的应对网络攻击的方法,但要封禁成千上万个IP,如果添加成千上万条规则,对机器性能影响较大,使用ipset能解决这个问题。
iptables 包含几个表,每个表由链组成。默认的是 filter 表,最常用的也是 filter 表,另一个比较常用的是nat表,封IP就是在 filter 表的 INPUT 链添加规则。
在进行规则匹配时,是从规则列表中从头到尾一条一条进行匹配。
这像是在链表中搜索指定节点费力。ipset 提供了把这个 O(n) 的操作变成 O(1) 的方法:就是把要处理的 IP 放进一个集合,对这个集合设置一条 iptables 规则。像 iptable 一样,IP sets 是 Linux 内核中的东西,ipset 这个命令是对它进行操作的一个工具。
简单的流程
可以用这几条命令概括使用 ipset 和 iptables 进行 IP 封禁的流程
ipset create vader hash:ip
iptables -I INPUT -m set --match-set vader src -j DROP
ipset add vader 4.5.6.7
ipset add vader 1.2.3.4
ipset add vader ...
ipset list vader # 查看 vader 集合的内容
下面分别对各条命令进行描述。
创建一个集合
ipset create vader hash:ip
这条命令创建了名为 vader 的集合,以 hash 方式存储,存储内容是 IP 地址。
添加 iptables 规则
iptables -I INPUT -m set --match-set vader src -j DROP
如果源地址(src)属于 vader 这个集合,就进行 DROP 操作。这条命令中,vader 是作为黑名单的,如果要把某个集合作为白名单,添加一个 ‘!’ 符号就可以。
iptables -I INPUT -m set ! --match-set yoda src -j DROP
到现在虽然创建了集合,添加了过滤规则,但是现在集合还是空的,需要往集合里加内容。
找出“坏” IP
找出要封禁的 IP,这是封禁过程中重要的步骤,不过不是这里的重点。简要说明一下两种方法思路。
netstat -ntu | tail -n +3 | awk '{print $5}' | sort | uniq -c | sort -nr
直接通过 netstat 的信息,把与本地相关的各种状态的 IP 都计数,排序列出来。
或者从 nginx 或者其他 web server 的日志里找请求数太多的 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr
后半部分,排序,去重,再按次数进行逆向排序的操作,跟上面命令是一样的。
找出“坏” IP,往之前创建的集合里添加就可以了。
ipset add vader 4.5.6.7
有多少“坏” IP,就添加多少 IP,因为针对这些封禁的 IP 只需要一条 iptables 规则,而这些 IP 是以 hash 方式存储,所以封禁大量的 IP 也不会影响性能,这也是 ipset 存在的最大目的。
You shall not pass!
ipset 更多的用法
存储类型
前面例子中的 vader 这个集合是以 hash 方式存储 IP 地址,也就是以 IP 地址为 hash 的键。除了 IP 地址,还可以是网络段,端口号(支持指定 TCP/UDP 协议),mac 地址,网络接口名称,或者上述各种类型的组合。
比如指定 hash:ip,port就是 IP 地址和端口号共同作为 hash 的键。查看 ipset 的帮助文档可以看到它支持的所有类型。
下面以两个例子说明。
HASH:NET
ipset create r2d2 hash:net
ipset add r2d2 1.2.3.0/24
ipset add r2d2 1.2.3.0/30 nomatch
ipset add r2d2 6.7.8.9
ipset test r2d2 1.2.3.2
hash:net 指定了可以往 r2d2 这个集合里添加 IP 段或 IP 地址。
第三条命令里的 nomatch 的作用简单来说是把 1.2.3.0/30 从 1.2.3.0/24 这一范围相对更大的段里“剥离”了出来,也就是说执行完 ipset add r2d2 1.2.3.0/24 只后1.2.3.0/24 这一段 IP 是属于 r2d2 集合的,执行了 ipset add r2d2 1.2.3.0/30 nomatch 之后,1.2.3.0/24 里 1.2.3.0/30 这部分,就不属于 r2d2 集合了。执行 ipset test r2d2 1.2.3.2 就会得到结果 1.2.3.2 is NOT in set r2d2.
HASH:IP,PORT
ipset create c-3po hash:ip,port
ipset add c-3po 3.4.5.6,80
ipset add c-3po 5.6.7.8,udp:53
ipset add c-3po 1.2.3.4,80-86
第二条命令添加的是 IP 地址为 3.4.5.6,端口号是 80 的项。没有注明协议,默认就是 TCP,下面一条命令则是指明了是 UDP 的 53 端口。最后一条命令指明了一个 IP 地址和一个端口号范围,这也是合法的命令。
自动过期,解封
ipset 支持 timeout 参数,这就意味着,如果一个集合是作为黑名单使用,通过 timeout 参数,就可以到期自动从黑名单里删除内容。
ipset create obiwan hash:ip timeout 300
ipset add obiwan 1.2.3.4
ipset add obiwan 6.6.6.6 timeout 60
上面第一条命令创建了名为 obiwan 的集合,后面多加了 timeout 参数,值为 300,往集合里添加条目的默认 timeout 时间就是 300。第三条命令在向集合添加 IP 时指定了一个不同于默认值的 timeout 值 60,那么这一条就会在 60 秒后自动删除。
隔几秒执行一次 ipset list obiwan 可以看到这个集合里条目的 timeout 一直在随着时间变化,标志着它们在多少秒之后会被删除。
如果要重新为某个条目指定 timeout 参数,要使用 -exit 这一选项。
ipset -exist add obiwan 1.2.3.4 timeout 100
这样 1.2.3.4 这一条数据的 timeout 值就变成了 100,如果这里设置 300,那么它的 timeout,也就是存活时间又重新变成 300。
如果在创建集合是没有指定 timeout,那么之后添加条目也就不支持 timeout 参数,执行 add 会收到报错。想要默认条目不会过期(自动删除),又需要添加某些条目时加上 timeout 参数,可以在创建集合时指定 timeout 为 0。
ipset create luke hash:ip
ipset add luke 5.5.5.5 timeout 100
得到报错信息 kernel error received: Unknown error -1
hashsize, maxelem 这两个参数分别指定了创建集合时初始的 hash 大小,和最大存储的条目数量。
ipset create yoda hash:ip,port hashsize 4096 maxelem 1000000
ipset add yoda 3.4.5.6,3306
这样创建了名为 yoda 的集合,初始 hash 大小是 4096,如果满了,这个 hash 会自动扩容为之前的两倍。最大能存储的数量是 100000 个。
如果没有指定,hashsize 的默认值是 1024,maxelem 的默认值是 65536。
另外几条常用命令
ipset del yoda x.x.x.x # 从 yoda 集合中删除内容
ipset list yoda # 查看 yoda 集合内容
ipset list # 查看所有集合的内容
ipset flush yoda # 清空 yoda 集合
ipset flush # 清空所有集合
ipset destroy yoda # 销毁 yoda 集合
ipset destroy # 销毁所有集合
ipset save yoda # 输出 yoda 集合内容到标准输出
ipset save # 输出所有集合内容到标准输出
ipset restore # 根据输入内容恢复集合内容
还有……
如果创建集合是指定的存储内容包含 ip, 例如 hash:ip 或 hash:ip,port ,在添加条目时,可以填 IP 段,但是仍然是以单独一个个 IP 的方式来存。
上面所有的例子都是用 hash 的方式进行存储,实际上 ipset 还可以以 bitmap 或者 link 方式存储,用这两种方式创建的集合大小,是固定的。
ipset参数如下:
命令
这些选项明确地指定了执行的活动。只有一个命令可以在命令行中规定除非其他的命令在下面。对于所有的长版本命令和选项名称,必须使用足够大的空间以确保ipset可以把他们和其他的选项区分开。
-N, --创建 集合名称 类型 类型具体选项
创建一个用集合名称命名并且指定类型的集合。类型具体选项必须是系统规定的。
-X, --删除 [集合名称]
删除指定的集合,如果没有指定或者指定all就删除全部集合。在删除集合之前,所有基于集合的绑定和默认绑定都会被移除。
如果集合已经被使用,则什么都不做。
-F, --清空 [集合名称]
删除指定集合中的所有规则, 如果没有指定或者指定了all就清空所有的集合。绑定不会受到清空操作的影响。
-E, --重命名 旧集合名 新集合名
重命名集合,新集合名的标识必须是目前不存在。
-W, --交换 源集合名 目的集合名
交换两个集合的内容,或者说交换两个集合的名称。这两个集合必须是存在的而且是具有相同的类型才能交换。
-L, --列出 [集合名名称]
列出指定集合的规则和绑定,如果没有指定或者指定为all就列出所有的集合。-n选项,数字选项可以用来限定名称查找和生产数字输出,当-s ,分类选项已经使用,规则将分类排列(如果给出的集合类型支持这个选项)。
-S, --保存 [集合名称]
保存指定名称的集合,如果没有指定或者指定为all,则保持所有集合:指定恢复可以读取的标准输出格式。
-R, --复原
复原已保存的会话.已被保存的会话可以是标准输入提供的。
当产生一个会话文件的时候请注意支持的命令(创建集合,添加元素,绑定)必须遵循严格的规范:首先创建集合添加所有属于它的集合等等,最后你可以列出所有的绑定命令.此外,这是一个复原选项,所以复原的集合必须是不存在的。
-A, --添加 集合名称 IP
往集合中添加一个ip。
-D, --删除 集合名称IP
从集合中删除一个ip
-T, --测试 集合名称 IP
测试一个ip是不是在集合中,要是ip在集合中返回0,如果ip不在集合中则返回非0.
-T, --测试 集合名称 IP 绑定的目的集合
测试ip是否附属指定集合的绑定点。如果成功返回0,否则返回非0.关键字default可以用来测试集合的默认绑定。
-B, --绑定 集合名称 IP 绑定的目的集合
绑定集合里的ip和目的集合
-U, --解除绑定 集合名称 IP
删除集合中指定ip的绑定。
-H, --帮助 [设置类型]
找出指定设置类型的帮助信息。
在-B –U和-T命令,你可以使用默认的:default,去绑定,解除绑定或者测试默认绑定去代替ip.在-U命令中你可以使用默认的:all去删除绑定集合的所有元素。
其他选项
接下来的选项可以被指定
-b, --binding setname
这个选择为-B绑定选项指定值。这是一个强制性的命令. 你在-T中可以用来测试绑定。
-s, --分类
分类标准输出.当监听集合,规则列表分类的时候。
-n, --数字
数字输出。当监听集合,绑定,ip地址和端口好需要输出的时候使用数字格式. 默认的系统试着去显示这些信息用主机名,网络名和服务,这会引起dns查找。
-q, --安静
禁止在标准输出和标准错误上有认可输出但是ipset还是会返回可能的错误。