目录
1、背景
iptables是linux中一个比较好用的防火墙工具,可以通过它对网络数据包进行管理和过滤,iptables中的四表五链在网上能查到很多文章解释,这里不做过多解释。ipset是一个用于管理IP地址集合的工具,能和iptables组合起来一起使用,方便维护和管理各种防火墙规则。本文会给出一个通过go库操作iptables中filter表的INPUT链和ipset来实现允许和拒绝进入本机ipv4网络数据包的示例。
2、go库下载
【1】iptables下载
go get -u github.com/coreos/go-iptables
【2】ipset下载
go get -u github.com/vishvananda/netlink
3、go库和命令行对比
操作方式 | 优点 | 缺点 |
---|---|---|
命令行操作 | 简单方便 | 平台架构版本兼容性差,复杂场景灵活性差 |
go库操作 | 平台兼容性强,复杂场景灵活性高 | 需要花时间验证库的正确性 |
4、代码示例
【1】定义iptables规则和ipset集合名称常量
// ipset类型
const (
HASH_IP = "hash:ip"
HASH_IP_PORT = "hash:ip,port"
HASH_NET = "hash:net"
HASH_NET_PORT = "hash:net,port"
)
// ipset名称
const (
WL_IP = "wl_ip"
WL_IP_PORT = "wl_ip_port"
WL_NET = "wl_net"
WL_NET_PORT = "wl_net_port"
BL_IP = "bl_ip"
BL_IP_PORT = "bl_ip_port"
BL_NET = "bl_net"
BL_NET_PORT = "bl_net_port"
)
var ipSetNameToTypeMp = map[string]string{
WL_IP: HASH_IP,
WL_IP_PORT: HASH_IP_PORT,
WL_NET: HASH_NET,
WL_NET_PORT: HASH_NET_PORT,
BL_IP: HASH_IP,
BL_IP_PORT: HASH_IP_PORT,
BL_NET: HASH_NET,
BL_NET_PORT: HASH_NET_PORT,
}
// iptables规则
var iptRules = [][]string{
{"-m", "state", "--state", "ESTABLISHED,RELATED", "-j", "ACCEPT"}, //已建连接允许
{"-i", "lo", "-j", "ACCEPT"}, //本地回环包运行
{"-m", "set", "--match-set", WL_IP, "src", "-j", "ACCEPT"}, //对集合条目里的源ip允许
{"-m", "set", "--match-set", WL_IP_PORT, "src,dst", "-j", "ACCEPT"}, //对集合条目里的源ip和目的port允许
{"-m", "set", "--match-set", WL_NET, "src", "-j", "ACCEPT"}, //对集合条目里的源ip允许
{"-m", "set", "--match-set", WL_NET_PORT, "src,dst", "-j", "ACCEPT"}, //对集合条目里的源ip和目的port允许
{"-m", "set", "--match-set", BL_IP, "src", "-j", "ACCEPT"}, //对集合条目里的源ip拒绝
{"-m", "set", "--match-set", BL_IP_PORT, "src,dst", "-j", "ACCEPT"}, //对集合条目里的源ip和目的port拒绝
{"-m", "set", "--match-set", BL_NET, "src", "-j", "ACCEPT"}, //对集合条目里的源ip拒绝
{"-m", "set", "--match-set", BL_NET_PORT, "src,dst", "-j", "ACCEPT"}, //对集合条目里的源ip和目的port拒绝
}
一般都会对本机的已建连接和lo网口的数据包放行,两条规则分别为:
1、iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
2、iptables -A INPUT -i lo -j ACCEPT
默认在filter表上增加规则,也可以增加-t指定其它表
【2】创建ipset集合
//创建ipset集合
for setName, setType := range ipSetNameToTypeMp {
if err := netlink.IpsetCreate(setName, setType, netlink.IpsetCreateOptions{Replace: true}); err != nil {
logger.Error("ipset create error", zap.String("set_name", setName), zap.Error(err))
continue
}
}
netlink.IpsetCreateOptions中可以定义其它自定义参数,比如:超时时间、ipv4/ipv6、最大个数、是否覆盖已存在的集合等。
【3】创建iptables规则
//创建iptables规则
ipt, err := iptables.New()
if err != nil {
logger.Error("iptables new error", zap.Error(err))
return
}
for _, iptRule := range iptRules {
if err = ipt.AppendUnique("filter", "INPUT", iptRule...); err != nil {
logger.Error("iptables append unique error", zap.Error(err))
continue
}
}
AppendUnique函数是往指定链中追加规则,如果规则存在就不会去做操作。
【4】添加条目
//增加ipset条目
var port = uint16(100)
if err = netlink.IpsetAdd(WL_IP, &netlink.IPSetEntry{IP: net.ParseIP("1.1.1.1")}); err != nil { //对源ip为1.1.1.1的包加白
logger.Error("ipset add error", zap.Error(err))
}
if err = netlink.IpsetAdd(WL_IP_PORT, &netlink.IPSetEntry{IP: net.ParseIP("2.2.2.2"), Port: &port}); err != nil { //对源ip为2.2.2.2,目的端口为100的包加白
logger.Error("ipset add error", zap.Error(err))
}
if err = netlink.IpsetAdd(WL_NET, &netlink.IPSetEntry{IP: net.ParseIP("3.3.3.3"), CIDR: 24}); err != nil { //对源ip在3.3.3.3/24范围内的包加白
logger.Error("ipset add error", zap.Error(err))
}
if err = netlink.IpsetAdd(WL_NET_PORT, &netlink.IPSetEntry{IP: net.ParseIP("4.4.4.4"), CIDR: 24, Port: &port}); err != nil { //对源在4.4.4.4/24范围内,目的端口为100的包加白
logger.Error("ipset add error", zap.Error(err))
}
if err = netlink.IpsetAdd(BL_IP, &netlink.IPSetEntry{IP: net.ParseIP("5.5.5.5")}); err != nil { //对源ip为1.1.1.1的包加黑
logger.Error("ipset add error", zap.Error(err))
}
if err = netlink.IpsetAdd(BL_IP_PORT, &netlink.IPSetEntry{IP: net.ParseIP("6.6.6.6"), Port: &port}); err != nil { //对源ip为2.2.2.2,目的端口为100的包加黑
logger.Error("ipset add error", zap.Error(err))
}
if err = netlink.IpsetAdd(BL_NET, &netlink.IPSetEntry{IP: net.ParseIP("7.7.7.7"), CIDR: 24}); err != nil { //对源ip在3.3.3.3/24范围内的包加黑
logger.Error("ipset add error", zap.Error(err))
}
if err = netlink.IpsetAdd(BL_NET_PORT, &netlink.IPSetEntry{IP: net.ParseIP("8.8.8.8"), CIDR: 24, Port: &port}); err != nil { //对源在4.4.4.4/24范围内,目的端口为100的包加黑
logger.Error("ipset add error", zap.Error(err))
}
根据不同的集合类型给netlink.IPSetEntry设置不同的参数。
【5】查看iptables
[root@xxx ~]# iptables -nL INPUT
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set wl_ip src
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set wl_ip_port src,dst
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set wl_net src
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set wl_net_port src,dst
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set bl_ip src
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set bl_ip_port src,dst
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set bl_net src
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set bl_net_port src,dst
[root@xxx ~]# iptables -S | grep INPUT
-P INPUT ACCEPT
-N SPA_HIDE_INPUT
-N SPA_WL_INPUT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m set --match-set wl_ip src -j ACCEPT
-A INPUT -m set --match-set wl_ip_port src,dst -j ACCEPT
-A INPUT -m set --match-set wl_net src -j ACCEPT
-A INPUT -m set --match-set wl_net_port src,dst -j ACCEPT
-A INPUT -m set --match-set bl_ip src -j ACCEPT
-A INPUT -m set --match-set bl_ip_port src,dst -j ACCEPT
-A INPUT -m set --match-set bl_net src -j ACCEPT
-A INPUT -m set --match-set bl_net_port src,dst -j ACCEPT
【6】查看ipset
[root@xxx ~]# ipset list
Name: bl_ip
Type: hash:ip
Revision: 0
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16544
References: 1
Members:
5.5.5.5
Name: bl_ip_port
Type: hash:ip,port
Revision: 1
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16560
References: 1
Members:
6.6.6.6,tcp:100
Name: bl_net
Type: hash:net
Revision: 0
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16816
References: 1
Members:
7.7.7.0/24
Name: bl_net_port
Type: hash:net,port
Revision: 1
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16816
References: 1
Members:
8.8.8.0/24,tcp:100
Name: wl_ip
Type: hash:ip
Revision: 0
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16544
References: 1
Members:
1.1.1.1
Name: wl_ip_port
Type: hash:ip,port
Revision: 1
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16560
References: 1
Members:
2.2.2.2,tcp:100
Name: wl_net
Type: hash:net
Revision: 0
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16816
References: 1
Members:
3.3.3.0/24
Name: wl_net_port
Type: hash:net,port
Revision: 1
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16816
References: 1
Members:
4.4.4.0/24,tcp:100
5、总结
上面给出了一部分go的iptables和ipset库的基础用法,在真实的业务场景中是可以做出很多优化,比如:黑白名单各自定义一条自定义链再追加到INPUT链去维护,这样黑白名单相互之间不会互相影响。更多功能可以根据库里的API应用到适合的场景。