【Golang】用go操作iptables和ipset设置黑白名单

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应用到适合的场景。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值