IPSET
本项目提供与Linux IPSET技术兼容的服务器隔离策略自动部署和更新方案
快速开始
本项目为Golang开发的插件
原理
IPTables是Linux服务器上进行网络隔离的核心技术,内核在处理网络请求时会对IPTables中的策略进行逐条解析,因此当策略较多时效率较低;而是用IPSet技术可以将策略中的五元组(协议,源地址,源端口,目的地址,目的端口)合并到有限的集合中,可以大大减少IPTables策略条目从而提高效率。测试结果显示IPSet方式效率将比IPTables提高100倍。
使用到的IPTables参数如下:
-A INPUT|OUTPUT 指定IPTables链,对应网络数据流相对于当前网卡的方向是流入或流出
-p tcp|udp|icmp 指定网络数据流使用的协议
-m state|set 指定加载的模块:set对应IPSet模块,具体的介绍在后面;state模块用于限定连接状态,配置此项可以避免对每个ACCEPT的策略同时加入INPUT链和OUTPUT链
-j ACCEPT|REJECT 指定对网络数据流放行或拒绝
目前网络隔离使用默认拒绝+白名单的方式配置,在配置IPSet表时,根据其方向、协议、是否放行及五元组类型可以分为24个list:set类型的集合,每个list:set集合可以容纳65536个其他类型集合(目前为hash:ip hash:ip,port),从而容纳海量策略:
in_tcp_acc_si out_tcp_acc_di
in_tcp_rej_si out_tcp_rej_di
in_udp_acc_si out_udp_acc_di
in_udp_rej_si out_udp_rej_di
in_tcp_acc_dp out_tcp_acc_dp
in_tcp_rej_dp out_tcp_rej_dp
in_udp_acc_dp out_udp_acc_dp
in_udp_rej_dp out_udp_rej_dp
in_tcp_acc_sidp out_tcp_acc_didp
in_tcp_rej_sidp out_tcp_rej_didp
in_udp_acc_sidp out_udp_acc_didp
in_udp_rej_sidp out_udp_rej_didp
IPSet策略设计
使用到的IPSet类型
- hash:ip 用于单独指定(si=源地址)或(di=目标地址),这两种在本方案中都会使用
- hash:ip,port 用于指定(sisp=源地址-源端口)、(disp=目的地址-源端口)、(sidp=源地址-目的端口)、(didp=目的地址-目的端口),后两种在本方案中会使用
- bitmap:port 用于单独指定端口(sp=源端口)或(dp=目的端口),后一种在本方案中会使用
- list:set 用于容纳前述三种集合类型,且不能包含本身
目前存在的hash:ip hash:ip,port容量设置为1048576,bitmap:port容量设置为65536,外层list:set容量设置为65536,这样任意一个list:set集合可以容纳1048576*65536=600亿 个元素,整体结构如下:
IPTables
in_tcp_acc_si
set1
set2
....
setn
in_tcp_rej_si
set1
set2
....
setn
in_udp_acc_si
in_udp_rej_si
in_tcp_acc_dp
in_tcp_rej_dp
in_udp_acc_dp
in_udp_rej_dp
in_tcp_acc_sidp
in_tcp_rej_sidp
in_udp_acc_sidp
in_udp_rej_sidp
out_tcp_acc_di
out_tcp_rej_di
out_udp_acc_di
out_udp_rej_di
out_tcp_acc_dp
out_tcp_rej_dp
out_udp_acc_dp
out_udp_rej_dp
out_tcp_acc_didp
out_tcp_rej_didp
out_udp_acc_didp
out_udp_rej_didp
IPSet使用注意事项
1.hash类型集合在iptables命令执行时大小确定,后期如果对该集合增加较多元素会发生哈希冲突,影响策略匹配。因此建立集合时需要设定一个较大容量
2.ipset的集合在执行iptables后进行更新操作时,会在这段时间执行默认操作(reject)
3.list:set不能包含list:set类型,只能包含其他类型
IPTables策略设计
按如下形式固定IPTables策略,可避免IPTables本身策略的备份与恢复操作(IPTables进行restore操作时会全量还原重置,而IPSet进行restore操作时是增量进行的):
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT DROP [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set in_tcp_acc_si src
-A INPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set in_tcp_acc_dp dst
-A INPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set in_tcp_acc_sidp src,dst
-A INPUT -p tcp -m state --state NEW,ESTABLISHED -j REJECT -m set --set in_tcp_rej_si src
-A INPUT -p tcp -m state --state NEW,ESTABLISHED -j REJECT -m set --set in_tcp_rej_dp dst
-A INPUT -p tcp -m state --state NEW,ESTABLISHED -j REJECT -m set --set in_tcp_rej_sidp src,dst
-A INPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set in_udp_acc_si src
-A INPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set in_udp_acc_dp dst
-A INPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set in_udp_acc_sidp src,dst
-A INPUT -p udp -m state --state NEW,ESTABLISHED -j REJECT -m set --set in_udp_rej_si src
-A INPUT -p udp -m state --state NEW,ESTABLISHED -j REJECT -m set --set in_udp_rej_dp dst
-A INPUT -p udp -m state --state NEW,ESTABLISHED -j REJECT -m set --set in_udp_rej_sidp src,dst
-A INPUT -m state --state NEW -j REJECT
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -p icmp -j ACCEPT
-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set out_tcp_acc_di src
-A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set out_tcp_acc_dp dst
-A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set out_tcp_acc_didp src,dst
-A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j REJECT -m set --set out_tcp_rej_di src
-A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j REJECT -m set --set out_tcp_rej_dp dst
-A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j REJECT -m set --set out_tcp_rej_didp src,dst
-A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set out_udp_acc_di src
-A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set out_udp_acc_dp dst
-A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT -m set --set out_udp_acc_didp src,dst
-A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j REJECT -m set --set out_udp_rej_di src
-A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j REJECT -m set --set out_udp_rej_dp dst
-A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j REJECT -m set --set out_udp_rej_didp src,dst
-A OUTPUT -j LOG --log-prefix "iptables:"
-A OUTPUT -m state --state NEW -j REJECT
COMMIT
现在来考虑IPSet指定的五元组的问题
- INPUT连接:考虑socket的accept函数,源端即远程主机,目的端即本机。这里只考虑新链接(NEW)
-
- 源地址 即远程主机地址,可以作为策略限制因子
-
- 源端口 即远程主机端口,一般为随机端口,不可作为策略限制因子
-
- 目的地址 即本机地址,由于是固定的,无需作为策略限制因子
-
- 目的端口 即本机端口,可以作为策略限制因子
- OUTPUT连接:考虑socket的connect函数,源端即本机,目的端即远程主机。这里只考虑新链接(NEW)
-
- 源地址 即本机地址,由于是固定的,无需作为策略限制因子
-
- 源端口 即本机端口,一般为随机端口,不可作为策略限制因子
-
- 目的地址 即远程主机地址,可以作为策略限制因子
-
- 目的端口 即远程主机端口,可以作为策略限制因子
综上,对于INPUT连接,可以指定源地址(si)、目的端口(dp)、源地址+目的端口(sidp);而对于OUTPUT连接,可以指定目的地址(di)、目的端口(dp)、目的地址+目的端口(didp)
IPSet方案相比IPTables方案的优点
- IPSet基于IPTables方案,是对IPTables技术的优化,将线性策略查找优化为集合元素查找,提高了通信速度
- IPSet方案在进行下发更新时,只需要对IPSet策略进行更新,无需对IPTables策略进行更新,由于在这种情况下IPTables条目极少,且无需还原操作,因此在更新的时间段对网络策略的影响较小,不会出现因为IPTables重置而导致短时间策略失效问题