mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 16:00:25 +08:00 
			
		
		
		
	增加IP动作
This commit is contained in:
		
							
								
								
									
										25
									
								
								internal/iplibrary/action_base.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								internal/iplibrary/action_base.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type BaseAction struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *BaseAction) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *BaseAction) convertParams(params maps.Map, ptr interface{}) error {
 | 
			
		||||
	data, err := json.Marshal(params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = json.Unmarshal(data, ptr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								internal/iplibrary/action_errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								internal/iplibrary/action_errors.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
// 是否是致命错误
 | 
			
		||||
type FataError struct {
 | 
			
		||||
	err string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *FataError) Error() string {
 | 
			
		||||
	return this.err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFataError(err string) error {
 | 
			
		||||
	return &FataError{err: err}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IsFatalError(err error) bool {
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := err.(*FataError)
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										147
									
								
								internal/iplibrary/action_firewalld.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								internal/iplibrary/action_firewalld.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Firewalld动作管理
 | 
			
		||||
// 常用命令:
 | 
			
		||||
//  - 查询列表: firewall-cmd --list-all
 | 
			
		||||
//  - 添加IP:firewall-cmd --add-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s
 | 
			
		||||
//  - 删除IP:firewall-cmd --remove-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s
 | 
			
		||||
type FirewalldAction struct {
 | 
			
		||||
	BaseAction
 | 
			
		||||
 | 
			
		||||
	config *firewallconfigs.FirewallActionFirewalldConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFirewalldAction() *FirewalldAction {
 | 
			
		||||
	return &FirewalldAction{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *FirewalldAction) Init(config *firewallconfigs.FirewallActionConfig) error {
 | 
			
		||||
	this.config = &firewallconfigs.FirewallActionFirewalldConfig{}
 | 
			
		||||
	err := this.convertParams(config.Params, this.config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *FirewalldAction) AddItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("addItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *FirewalldAction) DeleteItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("deleteItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *FirewalldAction) runAction(action string, listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	if item.Type == "all" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(item.IpTo) == 0 {
 | 
			
		||||
		return this.runActionSingleIP(action, listType, item)
 | 
			
		||||
	}
 | 
			
		||||
	cidrList, err := iPv4RangeToCIDRRange(item.IpFrom, item.IpTo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// 不合法的范围不予处理即可
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if len(cidrList) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	for _, cidr := range cidrList {
 | 
			
		||||
		item.IpFrom = cidr
 | 
			
		||||
		item.IpTo = ""
 | 
			
		||||
		err := this.runActionSingleIP(action, listType, item)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *FirewalldAction) runActionSingleIP(action string, listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	timestamp := time.Now().Unix()
 | 
			
		||||
 | 
			
		||||
	if item.ExpiredAt > 0 && timestamp > item.ExpiredAt {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	path := this.config.Path
 | 
			
		||||
	var err error
 | 
			
		||||
	if len(path) == 0 {
 | 
			
		||||
		path, err = exec.LookPath("firewall-cmd")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(path) == 0 {
 | 
			
		||||
		return errors.New("can not find 'firewall-cmd'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	opt := ""
 | 
			
		||||
	switch action {
 | 
			
		||||
	case "addItem":
 | 
			
		||||
		opt = "--add-rich-rule"
 | 
			
		||||
	case "deleteItem":
 | 
			
		||||
		opt = "--remove-rich-rule"
 | 
			
		||||
	default:
 | 
			
		||||
		return errors.New("invalid action '" + action + "'")
 | 
			
		||||
	}
 | 
			
		||||
	opt += "=rule family='"
 | 
			
		||||
	switch item.Type {
 | 
			
		||||
	case "ipv4":
 | 
			
		||||
		opt += "ipv4"
 | 
			
		||||
	case "ipv6":
 | 
			
		||||
		opt += "ipv6"
 | 
			
		||||
	default:
 | 
			
		||||
		// 我们忽略不能识别的Family
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	opt += "' source address='"
 | 
			
		||||
	if len(item.IpFrom) == 0 {
 | 
			
		||||
		return errors.New("invalid ip from")
 | 
			
		||||
	}
 | 
			
		||||
	opt += item.IpFrom + "' "
 | 
			
		||||
 | 
			
		||||
	switch listType {
 | 
			
		||||
	case IPListTypeWhite:
 | 
			
		||||
		opt += " accept"
 | 
			
		||||
	case IPListTypeBlack:
 | 
			
		||||
		opt += " reject"
 | 
			
		||||
	default:
 | 
			
		||||
		// 我们忽略不能识别的列表类型
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	args := []string{opt}
 | 
			
		||||
	if item.ExpiredAt > timestamp {
 | 
			
		||||
		args = append(args, "--timeout="+fmt.Sprintf("%d", item.ExpiredAt-timestamp)+"s")
 | 
			
		||||
	} else {
 | 
			
		||||
		// TODO 思考是否需要permanent,不然--reload之后会丢失
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if runtime.GOOS == "darwin" {
 | 
			
		||||
		// MAC OS直接返回
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	cmd := exec.Command(path, args...)
 | 
			
		||||
	stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
	cmd.Stderr = stderr
 | 
			
		||||
	err = cmd.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New(err.Error() + ", output: " + string(stderr.Bytes()))
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								internal/iplibrary/action_firewalld_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								internal/iplibrary/action_firewalld_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestFirewalldAction_AddItem(t *testing.T) {
 | 
			
		||||
	{
 | 
			
		||||
		action := NewFirewalldAction()
 | 
			
		||||
		action.config = &firewallconfigs.FirewallActionFirewalldConfig{
 | 
			
		||||
			Path: "/usr/bin/firewalld",
 | 
			
		||||
		}
 | 
			
		||||
		err := action.AddItem(IPListTypeWhite, &pb.IPItem{
 | 
			
		||||
			Type:      "ipv4",
 | 
			
		||||
			Id:        1,
 | 
			
		||||
			IpFrom:    "192.168.1.100",
 | 
			
		||||
			ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		t.Log("ok")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		action := NewFirewalldAction()
 | 
			
		||||
		action.config = &firewallconfigs.FirewallActionFirewalldConfig{
 | 
			
		||||
			Path: "/usr/bin/firewalld",
 | 
			
		||||
		}
 | 
			
		||||
		err := action.AddItem(IPListTypeBlack, &pb.IPItem{
 | 
			
		||||
			Type:      "ipv4",
 | 
			
		||||
			Id:        1,
 | 
			
		||||
			IpFrom:    "192.168.1.101",
 | 
			
		||||
			ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		t.Log("ok")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFirewalldAction_DeleteItem(t *testing.T) {
 | 
			
		||||
	action := NewFirewalldAction()
 | 
			
		||||
	action.config = &firewallconfigs.FirewallActionFirewalldConfig{
 | 
			
		||||
		Path: "/usr/bin/firewalld",
 | 
			
		||||
	}
 | 
			
		||||
	err := action.DeleteItem(IPListTypeWhite, &pb.IPItem{
 | 
			
		||||
		Type:      "ipv4",
 | 
			
		||||
		Id:        1,
 | 
			
		||||
		IpFrom:    "192.168.1.100",
 | 
			
		||||
		ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFirewalldAction_MultipleItem(t *testing.T) {
 | 
			
		||||
	action := NewFirewalldAction()
 | 
			
		||||
	action.config = &firewallconfigs.FirewallActionFirewalldConfig{
 | 
			
		||||
		Path: "/usr/bin/firewalld",
 | 
			
		||||
	}
 | 
			
		||||
	err := action.AddItem(IPListTypeBlack, &pb.IPItem{
 | 
			
		||||
		Type:      "ipv4",
 | 
			
		||||
		Id:        1,
 | 
			
		||||
		IpFrom:    "192.168.1.30",
 | 
			
		||||
		IpTo:      "192.168.1.200",
 | 
			
		||||
		ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										81
									
								
								internal/iplibrary/action_http_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								internal/iplibrary/action_http_api.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var httpAPIClient = &http.Client{
 | 
			
		||||
	Timeout: 5 * time.Second,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type HTTPAPIAction struct {
 | 
			
		||||
	BaseAction
 | 
			
		||||
 | 
			
		||||
	config *firewallconfigs.FirewallActionHTTPAPIConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewHTTPAPIAction() *HTTPAPIAction {
 | 
			
		||||
	return &HTTPAPIAction{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *HTTPAPIAction) Init(config *firewallconfigs.FirewallActionConfig) error {
 | 
			
		||||
	this.config = &firewallconfigs.FirewallActionHTTPAPIConfig{}
 | 
			
		||||
	err := this.convertParams(config.Params, this.config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(this.config.URL) == 0 {
 | 
			
		||||
		return NewFataError("'url' should not be empty")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *HTTPAPIAction) AddItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("addItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *HTTPAPIAction) DeleteItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("deleteItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *HTTPAPIAction) runAction(action string, listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	if item == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO 增加节点ID等信息
 | 
			
		||||
	m := maps.Map{
 | 
			
		||||
		"action":   action,
 | 
			
		||||
		"listType": listType,
 | 
			
		||||
		"item": maps.Map{
 | 
			
		||||
			"type":      item.Type,
 | 
			
		||||
			"ipFrom":    item.IpFrom,
 | 
			
		||||
			"ipTo":      item.IpTo,
 | 
			
		||||
			"expiredAt": item.ExpiredAt,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	mJSON, err := json.Marshal(m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	req, err := http.NewRequest(http.MethodPost, this.config.URL, bytes.NewReader(mJSON))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("User-Agent", "GoEdge-Node/"+teaconst.Version)
 | 
			
		||||
	resp, err := httpAPIClient.Do(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_ = resp.Body.Close()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								internal/iplibrary/action_http_api_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								internal/iplibrary/action_http_api_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestHTTPAPIAction_AddItem(t *testing.T) {
 | 
			
		||||
	action := NewHTTPAPIAction()
 | 
			
		||||
	action.config = &firewallconfigs.FirewallActionHTTPAPIConfig{
 | 
			
		||||
		URL:            "http://127.0.0.1:2345/post",
 | 
			
		||||
		TimeoutSeconds: 0,
 | 
			
		||||
	}
 | 
			
		||||
	err := action.AddItem(IPListTypeBlack, &pb.IPItem{
 | 
			
		||||
		Type:   "ipv4",
 | 
			
		||||
		Id:     1,
 | 
			
		||||
		IpFrom: "192.168.1.100",
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHTTPAPIAction_DeleteItem(t *testing.T) {
 | 
			
		||||
	action := NewHTTPAPIAction()
 | 
			
		||||
	action.config = &firewallconfigs.FirewallActionHTTPAPIConfig{
 | 
			
		||||
		URL:            "http://127.0.0.1:2345/post",
 | 
			
		||||
		TimeoutSeconds: 0,
 | 
			
		||||
	}
 | 
			
		||||
	err := action.DeleteItem(IPListTypeBlack, &pb.IPItem{
 | 
			
		||||
		Type:   "ipv4",
 | 
			
		||||
		Id:     1,
 | 
			
		||||
		IpFrom: "192.168.1.100",
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								internal/iplibrary/action_interface.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								internal/iplibrary/action_interface.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ActionInterface interface {
 | 
			
		||||
	// 初始化
 | 
			
		||||
	Init(config *firewallconfigs.FirewallActionConfig) error
 | 
			
		||||
 | 
			
		||||
	// 添加
 | 
			
		||||
	AddItem(listType IPListType, item *pb.IPItem) error
 | 
			
		||||
 | 
			
		||||
	// 删除
 | 
			
		||||
	DeleteItem(listType IPListType, item *pb.IPItem) error
 | 
			
		||||
 | 
			
		||||
	// 关闭
 | 
			
		||||
	Close() error
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										276
									
								
								internal/iplibrary/action_ipset.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								internal/iplibrary/action_ipset.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,276 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IPSet动作
 | 
			
		||||
// 相关命令:
 | 
			
		||||
//   - 利用Firewalld管理set:
 | 
			
		||||
//       - 添加:firewall-cmd --permanent --new-ipset=edge_ip_list --type=hash:ip --option="timeout=0"
 | 
			
		||||
//       - 删除:firewall-cmd --permanent --delete-ipset=edge_ip_list
 | 
			
		||||
//       - 重载:firewall-cmd --reload
 | 
			
		||||
//       - firewalld+ipset: firewall-cmd --permanent --add-rich-rule="rule source ipset='edge_ip_list' reject"
 | 
			
		||||
//   - 利用IPTables管理set:
 | 
			
		||||
//       - 添加:iptables -A INPUT -m set --match-set edge_ip_list src -j REJECT
 | 
			
		||||
//   - 添加Item:ipset add edge_ip_list 192.168.2.32 timeout 30
 | 
			
		||||
//   - 删除Item: ipset del edge_ip_list 192.168.2.32
 | 
			
		||||
//   - 创建set:ipset create edge_ip_list hash:ip timeout 0
 | 
			
		||||
type IPSetAction struct {
 | 
			
		||||
	BaseAction
 | 
			
		||||
 | 
			
		||||
	config *firewallconfigs.FirewallActionIPSetConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewIPSetAction() *IPSetAction {
 | 
			
		||||
	return &IPSetAction{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) error {
 | 
			
		||||
	this.config = &firewallconfigs.FirewallActionIPSetConfig{}
 | 
			
		||||
	err := this.convertParams(config.Params, this.config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(this.config.WhiteName) == 0 {
 | 
			
		||||
		return NewFataError("white list name should not be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if len(this.config.BlackName) == 0 {
 | 
			
		||||
		return NewFataError("black list name should not be empty")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 创建ipset
 | 
			
		||||
	{
 | 
			
		||||
		path, err := exec.LookPath("ipset")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		{
 | 
			
		||||
			cmd := exec.Command(path, "create", this.config.WhiteName, "hash:ip", "timeout", "0")
 | 
			
		||||
			stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
			cmd.Stderr = stderr
 | 
			
		||||
			err := cmd.Run()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				output := stderr.Bytes()
 | 
			
		||||
				if !bytes.Contains(output, []byte("already exists")) {
 | 
			
		||||
					return errors.New("create ipset '" + this.config.WhiteName + "': " + err.Error() + ", output: " + string(output))
 | 
			
		||||
				} else {
 | 
			
		||||
					err = nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		{
 | 
			
		||||
			cmd := exec.Command(path, "create", this.config.BlackName, "hash:ip", "timeout", "0")
 | 
			
		||||
			stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
			cmd.Stderr = stderr
 | 
			
		||||
			err := cmd.Run()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				output := stderr.Bytes()
 | 
			
		||||
				if !bytes.Contains(output, []byte("already exists")) {
 | 
			
		||||
					return errors.New("create ipset '" + this.config.BlackName + "': " + err.Error() + ", output: " + string(output))
 | 
			
		||||
				} else {
 | 
			
		||||
					err = nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// firewalld
 | 
			
		||||
	if this.config.AutoAddToFirewalld {
 | 
			
		||||
		path, err := exec.LookPath("firewall-cmd")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			cmd := exec.Command(path, "--permanent", "--new-ipset="+this.config.WhiteName, "--type=hash:ip", "--option=timeout=0", "--option=maxelem=1000000")
 | 
			
		||||
			stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
			cmd.Stderr = stderr
 | 
			
		||||
			err := cmd.Run()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				output := stderr.Bytes()
 | 
			
		||||
				if bytes.Contains(output, []byte("NAME_CONFLICT")) {
 | 
			
		||||
					err = nil
 | 
			
		||||
				} else {
 | 
			
		||||
					return errors.New("firewall-cmd add ipset '" + this.config.WhiteName + "': " + err.Error() + ", output: " + string(output))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			cmd := exec.Command(path, "--permanent", "--add-rich-rule=rule source ipset='"+this.config.WhiteName+"' accept")
 | 
			
		||||
			stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
			cmd.Stderr = stderr
 | 
			
		||||
			err := cmd.Run()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				output := stderr.Bytes()
 | 
			
		||||
				return errors.New("firewall-cmd add rich rule '" + this.config.WhiteName + "': " + err.Error() + ", output: " + string(output))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			cmd := exec.Command(path, "--permanent", "--new-ipset="+this.config.BlackName, "--type=hash:ip", "--option=timeout=0", "--option=maxelem=1000000")
 | 
			
		||||
			stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
			cmd.Stderr = stderr
 | 
			
		||||
			err := cmd.Run()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				output := stderr.Bytes()
 | 
			
		||||
				if bytes.Contains(output, []byte("NAME_CONFLICT")) {
 | 
			
		||||
					err = nil
 | 
			
		||||
				} else {
 | 
			
		||||
					return errors.New("firewall-cmd add ipset '" + this.config.BlackName + "': " + err.Error() + ", output: " + string(output))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			cmd := exec.Command(path, "--permanent", "--add-rich-rule=rule source ipset='"+this.config.BlackName+"' reject")
 | 
			
		||||
			stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
			cmd.Stderr = stderr
 | 
			
		||||
			err := cmd.Run()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				output := stderr.Bytes()
 | 
			
		||||
				return errors.New("firewall-cmd add rich rule '" + this.config.WhiteName + "': " + err.Error() + ", output: " + string(output))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// reload
 | 
			
		||||
		{
 | 
			
		||||
			cmd := exec.Command(path, "--reload")
 | 
			
		||||
			stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
			cmd.Stderr = stderr
 | 
			
		||||
			err := cmd.Run()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				output := stderr.Bytes()
 | 
			
		||||
				return errors.New("firewall-cmd reload: " + err.Error() + ", output: " + string(output))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// iptables
 | 
			
		||||
	if this.config.AutoAddToIPTables {
 | 
			
		||||
		path, err := exec.LookPath("iptables")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			cmd := exec.Command(path, "-A", "INPUT", "-m", "set", "--match-set", this.config.WhiteName, "src", "-j", "ACCEPT")
 | 
			
		||||
			stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
			cmd.Stderr = stderr
 | 
			
		||||
			err := cmd.Run()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				output := stderr.Bytes()
 | 
			
		||||
				return errors.New("iptables add rule: " + err.Error() + ", output: " + string(output))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			cmd := exec.Command(path, "-A", "INPUT", "-m", "set", "--match-set", this.config.BlackName, "src", "-j", "REJECT")
 | 
			
		||||
			stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
			cmd.Stderr = stderr
 | 
			
		||||
			err := cmd.Run()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				output := stderr.Bytes()
 | 
			
		||||
				return errors.New("iptables add rule: " + err.Error() + ", output: " + string(output))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPSetAction) AddItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("addItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPSetAction) DeleteItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("deleteItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPSetAction) runAction(action string, listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	if item.Type == "all" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if len(item.IpTo) == 0 {
 | 
			
		||||
		return this.runActionSingleIP(action, listType, item)
 | 
			
		||||
	}
 | 
			
		||||
	cidrList, err := iPv4RangeToCIDRRange(item.IpFrom, item.IpTo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// 不合法的范围不予处理即可
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if len(cidrList) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	for _, cidr := range cidrList {
 | 
			
		||||
		item.IpFrom = cidr
 | 
			
		||||
		item.IpTo = ""
 | 
			
		||||
		err := this.runActionSingleIP(action, listType, item)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPSetAction) runActionSingleIP(action string, listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	if item.Type == "all" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	listName := ""
 | 
			
		||||
	switch listType {
 | 
			
		||||
	case IPListTypeWhite:
 | 
			
		||||
		listName = this.config.WhiteName
 | 
			
		||||
	case IPListTypeBlack:
 | 
			
		||||
		listName = this.config.BlackName
 | 
			
		||||
	default:
 | 
			
		||||
		// 不支持的类型
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if len(listName) == 0 {
 | 
			
		||||
		return errors.New("empty list name for '" + listType + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	path := this.config.Path
 | 
			
		||||
	var err error
 | 
			
		||||
	if len(path) == 0 {
 | 
			
		||||
		path, err = exec.LookPath("ipset")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ipset add edge_ip_list 192.168.2.32 timeout 30
 | 
			
		||||
	args := []string{}
 | 
			
		||||
	switch action {
 | 
			
		||||
	case "addItem":
 | 
			
		||||
		args = append(args, "add")
 | 
			
		||||
	case "deleteItem":
 | 
			
		||||
		args = append(args, "del")
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, listName, item.IpFrom)
 | 
			
		||||
	timestamp := time.Now().Unix()
 | 
			
		||||
	if item.ExpiredAt > timestamp {
 | 
			
		||||
		args = append(args, "timeout", strconv.FormatInt(item.ExpiredAt-timestamp, 10))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//logs.Println(args)
 | 
			
		||||
 | 
			
		||||
	if runtime.GOOS == "darwin" {
 | 
			
		||||
		// MAC OS直接返回
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd := exec.Command(path, args...)
 | 
			
		||||
	return cmd.Run()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										81
									
								
								internal/iplibrary/action_ipset_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								internal/iplibrary/action_ipset_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestIPSetAction_Init(t *testing.T) {
 | 
			
		||||
	action := NewIPSetAction()
 | 
			
		||||
	err := action.Init(&firewallconfigs.FirewallActionConfig{
 | 
			
		||||
		Params: maps.Map{
 | 
			
		||||
			"path":      "/usr/bin/iptables",
 | 
			
		||||
			"whiteName": "white-list",
 | 
			
		||||
			"blackName": "black-list",
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIPSetAction_AddItem(t *testing.T) {
 | 
			
		||||
	action := NewIPSetAction()
 | 
			
		||||
	action.config = &firewallconfigs.FirewallActionIPSetConfig{
 | 
			
		||||
		Path:      "/usr/bin/iptables",
 | 
			
		||||
		WhiteName: "white-list",
 | 
			
		||||
		BlackName: "black-list",
 | 
			
		||||
	}
 | 
			
		||||
	{
 | 
			
		||||
		err := action.AddItem(IPListTypeWhite, &pb.IPItem{
 | 
			
		||||
			Type:      "ipv4",
 | 
			
		||||
			Id:        1,
 | 
			
		||||
			IpFrom:    "192.168.1.100",
 | 
			
		||||
			ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		t.Log("ok")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		err := action.AddItem(IPListTypeBlack, &pb.IPItem{
 | 
			
		||||
			Type:      "ipv4",
 | 
			
		||||
			Id:        1,
 | 
			
		||||
			IpFrom:    "192.168.1.100",
 | 
			
		||||
			ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		t.Log("ok")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIPSetAction_DeleteItem(t *testing.T) {
 | 
			
		||||
	action := NewIPSetAction()
 | 
			
		||||
	err := action.Init(&firewallconfigs.FirewallActionConfig{
 | 
			
		||||
		Params: maps.Map{
 | 
			
		||||
			"path":      "/usr/bin/firewalld",
 | 
			
		||||
			"whiteName": "white-list",
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	err = action.DeleteItem(IPListTypeWhite, &pb.IPItem{
 | 
			
		||||
		Type:      "ipv4",
 | 
			
		||||
		Id:        1,
 | 
			
		||||
		IpFrom:    "192.168.1.100",
 | 
			
		||||
		ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										119
									
								
								internal/iplibrary/action_iptables.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								internal/iplibrary/action_iptables.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IPTables动作
 | 
			
		||||
// 相关命令:
 | 
			
		||||
//   iptables -A INPUT -s "192.168.2.32" -j ACCEPT
 | 
			
		||||
//   iptables -A INPUT -s "192.168.2.32" -j REJECT
 | 
			
		||||
//   iptables -D ...
 | 
			
		||||
type IPTablesAction struct {
 | 
			
		||||
	BaseAction
 | 
			
		||||
 | 
			
		||||
	config *firewallconfigs.FirewallActionIPTablesConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewIPTablesAction() *IPTablesAction {
 | 
			
		||||
	return &IPTablesAction{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPTablesAction) Init(config *firewallconfigs.FirewallActionConfig) error {
 | 
			
		||||
	this.config = &firewallconfigs.FirewallActionIPTablesConfig{}
 | 
			
		||||
	err := this.convertParams(config.Params, this.config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPTablesAction) AddItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("addItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPTablesAction) DeleteItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("deleteItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPTablesAction) runAction(action string, listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	if item.Type == "all" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if len(item.IpTo) == 0 {
 | 
			
		||||
		return this.runActionSingleIP(action, listType, item)
 | 
			
		||||
	}
 | 
			
		||||
	cidrList, err := iPv4RangeToCIDRRange(item.IpFrom, item.IpTo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// 不合法的范围不予处理即可
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if len(cidrList) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	for _, cidr := range cidrList {
 | 
			
		||||
		item.IpFrom = cidr
 | 
			
		||||
		item.IpTo = ""
 | 
			
		||||
		err := this.runActionSingleIP(action, listType, item)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IPTablesAction) runActionSingleIP(action string, listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	if item.Type == "all" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	path := this.config.Path
 | 
			
		||||
	var err error
 | 
			
		||||
	if len(path) == 0 {
 | 
			
		||||
		path, err = exec.LookPath("iptables")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	iptablesAction := ""
 | 
			
		||||
	switch action {
 | 
			
		||||
	case "addItem":
 | 
			
		||||
		iptablesAction = "-A"
 | 
			
		||||
	case "deleteItem":
 | 
			
		||||
		iptablesAction = "-D"
 | 
			
		||||
	default:
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	args := []string{iptablesAction, "INPUT", "-s", item.IpFrom, "-j"}
 | 
			
		||||
	switch listType {
 | 
			
		||||
	case IPListTypeWhite:
 | 
			
		||||
		args = append(args, "ACCEPT")
 | 
			
		||||
	case IPListTypeBlack:
 | 
			
		||||
		args = append(args, "REJECT")
 | 
			
		||||
	default:
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if runtime.GOOS == "darwin" {
 | 
			
		||||
		// MAC OS直接返回
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd := exec.Command(path, args...)
 | 
			
		||||
	stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
	cmd.Stderr = stderr
 | 
			
		||||
	err = cmd.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		output := stderr.Bytes()
 | 
			
		||||
		if bytes.Contains(output, []byte("No chain/target/match")) {
 | 
			
		||||
			err = nil
 | 
			
		||||
		} else {
 | 
			
		||||
			return errors.New(err.Error() + ", output: " + string(output))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								internal/iplibrary/action_iptables_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								internal/iplibrary/action_iptables_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestIPTablesAction_AddItem(t *testing.T) {
 | 
			
		||||
	action := NewIPTablesAction()
 | 
			
		||||
	action.config = &firewallconfigs.FirewallActionIPTablesConfig{
 | 
			
		||||
		Path: "/usr/bin/iptables",
 | 
			
		||||
	}
 | 
			
		||||
	{
 | 
			
		||||
		err := action.AddItem(IPListTypeWhite, &pb.IPItem{
 | 
			
		||||
			Type:      "ipv4",
 | 
			
		||||
			Id:        1,
 | 
			
		||||
			IpFrom:    "192.168.1.100",
 | 
			
		||||
			ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		t.Log("ok")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		err := action.AddItem(IPListTypeBlack, &pb.IPItem{
 | 
			
		||||
			Type:      "ipv4",
 | 
			
		||||
			Id:        1,
 | 
			
		||||
			IpFrom:    "192.168.1.100",
 | 
			
		||||
			ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		t.Log("ok")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIPTablesAction_DeleteItem(t *testing.T) {
 | 
			
		||||
	action := NewIPTablesAction()
 | 
			
		||||
	action.config = &firewallconfigs.FirewallActionIPTablesConfig{
 | 
			
		||||
		Path: "/usr/bin/firewalld",
 | 
			
		||||
	}
 | 
			
		||||
	err := action.DeleteItem(IPListTypeWhite, &pb.IPItem{
 | 
			
		||||
		Type:      "ipv4",
 | 
			
		||||
		Id:        1,
 | 
			
		||||
		IpFrom:    "192.168.1.100",
 | 
			
		||||
		ExpiredAt: time.Now().Unix() + 30,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										164
									
								
								internal/iplibrary/action_manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								internal/iplibrary/action_manager.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,164 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var SharedActionManager = NewActionManager()
 | 
			
		||||
 | 
			
		||||
// 动作管理器定义
 | 
			
		||||
type ActionManager struct {
 | 
			
		||||
	locker sync.Mutex
 | 
			
		||||
 | 
			
		||||
	eventMap    map[string][]ActionInterface                    // eventLevel => []instance
 | 
			
		||||
	configMap   map[int64]*firewallconfigs.FirewallActionConfig // id => config
 | 
			
		||||
	instanceMap map[int64]ActionInterface                       // id => instance
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取动作管理对象
 | 
			
		||||
func NewActionManager() *ActionManager {
 | 
			
		||||
	return &ActionManager{
 | 
			
		||||
		configMap:   map[int64]*firewallconfigs.FirewallActionConfig{},
 | 
			
		||||
		instanceMap: map[int64]ActionInterface{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 更新配置
 | 
			
		||||
func (this *ActionManager) UpdateActions(actions []*firewallconfigs.FirewallActionConfig) {
 | 
			
		||||
	this.locker.Lock()
 | 
			
		||||
	defer this.locker.Unlock()
 | 
			
		||||
 | 
			
		||||
	// 关闭不存在的
 | 
			
		||||
	newActionsMap := map[int64]*firewallconfigs.FirewallActionConfig{}
 | 
			
		||||
	for _, action := range actions {
 | 
			
		||||
		newActionsMap[action.Id] = action
 | 
			
		||||
	}
 | 
			
		||||
	for _, oldAction := range this.configMap {
 | 
			
		||||
		_, ok := newActionsMap[oldAction.Id]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			instance, ok := this.instanceMap[oldAction.Id]
 | 
			
		||||
			if ok {
 | 
			
		||||
				_ = instance.Close()
 | 
			
		||||
				delete(this.instanceMap, oldAction.Id)
 | 
			
		||||
				remotelogs.Println("IPLIBRARY/ACTION_MANAGER", "close action "+strconv.FormatInt(oldAction.Id, 10))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 添加新的或者更新老的
 | 
			
		||||
	for _, newAction := range newActionsMap {
 | 
			
		||||
		oldInstance, ok := this.instanceMap[newAction.Id]
 | 
			
		||||
		if ok {
 | 
			
		||||
			// 检查配置是否一致
 | 
			
		||||
			oldConfigJSON, err := json.Marshal(this.configMap[newAction.Id])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				remotelogs.Error("IPLIBRARY/ACTION_MANAGER", "action "+strconv.FormatInt(newAction.Id, 10) + ", type:" + newAction.Type+": "+err.Error())
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			newConfigJSON, err := json.Marshal(newAction)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				remotelogs.Error("IPLIBRARY/ACTION_MANAGER", "action "+strconv.FormatInt(newAction.Id, 10) + ", type:" + newAction.Type+": "+err.Error())
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if bytes.Compare(newConfigJSON, oldConfigJSON) != 0 {
 | 
			
		||||
				_ = oldInstance.Close()
 | 
			
		||||
 | 
			
		||||
				// 重新创建
 | 
			
		||||
				// 之所以要重新创建,是因为前后的动作类型可能有变化,完全重建可以避免不必要的麻烦
 | 
			
		||||
				newInstance, err := this.createInstance(newAction)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					remotelogs.Error("IPLIBRARY/ACTION_MANAGER", "reload action "+strconv.FormatInt(newAction.Id, 10) + ", type:" + newAction.Type+": "+err.Error())
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				remotelogs.Println("IPLIBRARY/ACTION_MANAGER", "reloaded "+strconv.FormatInt(newAction.Id, 10)+", type:"+newAction.Type)
 | 
			
		||||
				this.instanceMap[newAction.Id] = newInstance
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// 创建
 | 
			
		||||
			instance, err := this.createInstance(newAction)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				remotelogs.Error("IPLIBRARY/ACTION_MANAGER", "load new action "+strconv.FormatInt(newAction.Id, 10) + ", type:" + newAction.Type+": "+err.Error())
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			remotelogs.Println("IPLIBRARY/ACTION_MANAGER", "loaded action "+strconv.FormatInt(newAction.Id, 10)+", type:"+newAction.Type)
 | 
			
		||||
			this.instanceMap[newAction.Id] = instance
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 更新配置
 | 
			
		||||
	this.configMap = newActionsMap
 | 
			
		||||
	this.eventMap = map[string][]ActionInterface{}
 | 
			
		||||
	for _, action := range this.configMap {
 | 
			
		||||
		instance, ok := this.instanceMap[action.Id]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		instances, _ := this.eventMap[action.EventLevel]
 | 
			
		||||
		instances = append(instances, instance)
 | 
			
		||||
		this.eventMap[action.EventLevel] = instances
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 执行添加IP动作
 | 
			
		||||
func (this *ActionManager) AddItem(listType IPListType, item *pb.IPItem) {
 | 
			
		||||
	instances, ok := this.eventMap[item.EventLevel]
 | 
			
		||||
	if ok {
 | 
			
		||||
		for _, instance := range instances {
 | 
			
		||||
			err := instance.AddItem(listType, item)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				remotelogs.Error("IPLIBRARY/ACTION_MANAGER", "add item '"+fmt.Sprintf("%d", item.Id)+"': "+err.Error())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 执行删除IP动作
 | 
			
		||||
func (this *ActionManager) DeleteItem(listType IPListType, item *pb.IPItem) {
 | 
			
		||||
	instances, ok := this.eventMap[item.EventLevel]
 | 
			
		||||
	if ok {
 | 
			
		||||
		for _, instance := range instances {
 | 
			
		||||
			err := instance.DeleteItem(listType, item)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				remotelogs.Error("IPLIBRARY/ACTION_MANAGER", "delete item '"+fmt.Sprintf("%d", item.Id)+"': "+err.Error())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *ActionManager) createInstance(config *firewallconfigs.FirewallActionConfig) (ActionInterface, error) {
 | 
			
		||||
	var instance ActionInterface
 | 
			
		||||
	switch config.Type {
 | 
			
		||||
	case firewallconfigs.FirewallActionTypeIPSet:
 | 
			
		||||
		instance = NewIPSetAction()
 | 
			
		||||
	case firewallconfigs.FirewallActionTypeFirewalld:
 | 
			
		||||
		instance = NewFirewalldAction()
 | 
			
		||||
	case firewallconfigs.FirewallActionTypeIPTables:
 | 
			
		||||
		instance = NewIPTablesAction()
 | 
			
		||||
	case firewallconfigs.FirewallActionTypeScript:
 | 
			
		||||
		instance = NewScriptAction()
 | 
			
		||||
	case firewallconfigs.FirewallActionTypeHTTPAPI:
 | 
			
		||||
		instance = NewHTTPAPIAction()
 | 
			
		||||
	}
 | 
			
		||||
	if instance == nil {
 | 
			
		||||
		return nil, errors.New("can not create instance for type '" + config.Type + "'")
 | 
			
		||||
	}
 | 
			
		||||
	err := instance.Init(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// 如果是警告错误,我们只是提示
 | 
			
		||||
		if !IsFatalError(err) {
 | 
			
		||||
			remotelogs.Error("IPLIBRARY/ACTION_MANAGER/CREATE_INSTANCE", "init '"+config.Type+"' failed: "+err.Error())
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return instance, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								internal/iplibrary/action_manager_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								internal/iplibrary/action_manager_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestActionManager_UpdateActions(t *testing.T) {
 | 
			
		||||
	manager := NewActionManager()
 | 
			
		||||
	manager.UpdateActions([]*firewallconfigs.FirewallActionConfig{
 | 
			
		||||
		{
 | 
			
		||||
			Id:   1,
 | 
			
		||||
			Type: "ipset",
 | 
			
		||||
			Params: maps.Map{
 | 
			
		||||
				"whiteName": "edge-white-list",
 | 
			
		||||
				"blackName": "edge-black-list",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	t.Log("===config===")
 | 
			
		||||
	for _, c := range manager.configMap {
 | 
			
		||||
		t.Log(c.Id, c.Type)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("===instance===")
 | 
			
		||||
	for id, c := range manager.instanceMap {
 | 
			
		||||
		t.Log(id, c)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	manager.UpdateActions([]*firewallconfigs.FirewallActionConfig{
 | 
			
		||||
		{
 | 
			
		||||
			Id:   1,
 | 
			
		||||
			Type: "ipset",
 | 
			
		||||
			Params: maps.Map{
 | 
			
		||||
				"whiteName": "edge-white-list",
 | 
			
		||||
				"blackName": "edge-black-list",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Id:   2,
 | 
			
		||||
			Type: "iptables",
 | 
			
		||||
			Params: maps.Map{
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Log("===config===")
 | 
			
		||||
	for _, c := range manager.configMap {
 | 
			
		||||
		t.Log(c.Id, c.Type)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("===instance===")
 | 
			
		||||
	for id, c := range manager.instanceMap {
 | 
			
		||||
		t.Logf("%d: %#v", id, c)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								internal/iplibrary/action_script.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								internal/iplibrary/action_script.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 脚本命令动作
 | 
			
		||||
type ScriptAction struct {
 | 
			
		||||
	BaseAction
 | 
			
		||||
 | 
			
		||||
	config *firewallconfigs.FirewallActionScriptConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewScriptAction() *ScriptAction {
 | 
			
		||||
	return &ScriptAction{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *ScriptAction) Init(config *firewallconfigs.FirewallActionConfig) error {
 | 
			
		||||
	this.config = &firewallconfigs.FirewallActionScriptConfig{}
 | 
			
		||||
	err := this.convertParams(config.Params, this.config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(this.config.Path) == 0 {
 | 
			
		||||
		return NewFataError("'path' should not be empty")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *ScriptAction) AddItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("addItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *ScriptAction) DeleteItem(listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	return this.runAction("deleteItem", listType, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *ScriptAction) runAction(action string, listType IPListType, item *pb.IPItem) error {
 | 
			
		||||
	// TODO 智能支持 .sh 脚本文件
 | 
			
		||||
	cmd := exec.Command(this.config.Path)
 | 
			
		||||
	cmd.Env = []string{
 | 
			
		||||
		"ACTION=" + action,
 | 
			
		||||
		"TYPE=" + item.Type,
 | 
			
		||||
		"IP_FROM=" + item.IpFrom,
 | 
			
		||||
		"IP_TO=" + item.IpTo,
 | 
			
		||||
		"EXPIRED_AT=" + fmt.Sprintf("%d", item.ExpiredAt),
 | 
			
		||||
		"LIST_TYPE=" + listType,
 | 
			
		||||
	}
 | 
			
		||||
	if len(this.config.Cwd) > 0 {
 | 
			
		||||
		cmd.Dir = this.config.Cwd
 | 
			
		||||
	} else {
 | 
			
		||||
		cmd.Dir = filepath.Dir(this.config.Path)
 | 
			
		||||
	}
 | 
			
		||||
	stderr := bytes.NewBuffer([]byte{})
 | 
			
		||||
	cmd.Stderr = stderr
 | 
			
		||||
	err := cmd.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New(err.Error() + ", output: " + string(stderr.Bytes()))
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								internal/iplibrary/action_script_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								internal/iplibrary/action_script_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestScriptAction_AddItem(t *testing.T) {
 | 
			
		||||
	action := NewScriptAction()
 | 
			
		||||
	action.config = &firewallconfigs.FirewallActionScriptConfig{
 | 
			
		||||
		Path: "/tmp/ip-item.sh",
 | 
			
		||||
		Cwd:  "",
 | 
			
		||||
		Args: nil,
 | 
			
		||||
	}
 | 
			
		||||
	err := action.AddItem(IPListTypeBlack, &pb.IPItem{
 | 
			
		||||
		Type:      "ipv4",
 | 
			
		||||
		Id:        1,
 | 
			
		||||
		IpFrom:    "192.168.1.100",
 | 
			
		||||
		ExpiredAt: time.Now().Unix(),
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestScriptAction_DeleteItem(t *testing.T) {
 | 
			
		||||
	action := NewScriptAction()
 | 
			
		||||
	action.config = &firewallconfigs.FirewallActionScriptConfig{
 | 
			
		||||
		Path: "/tmp/ip-item.sh",
 | 
			
		||||
		Cwd:  "",
 | 
			
		||||
		Args: nil,
 | 
			
		||||
	}
 | 
			
		||||
	err := action.DeleteItem(IPListTypeBlack, &pb.IPItem{
 | 
			
		||||
		Type:      "ipv4",
 | 
			
		||||
		Id:        1,
 | 
			
		||||
		IpFrom:    "192.168.1.100",
 | 
			
		||||
		ExpiredAt: time.Now().Unix(),
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										85
									
								
								internal/iplibrary/action_utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								internal/iplibrary/action_utils.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Convert IPv4 range into CIDR
 | 
			
		||||
// 来自:https://gist.github.com/P-A-R-U-S/a090dd90c5104ce85a29c32669dac107
 | 
			
		||||
func iPv4RangeToCIDRRange(ipStart string, ipEnd string) (cidrs []string, err error) {
 | 
			
		||||
 | 
			
		||||
	cidr2mask := []uint32{
 | 
			
		||||
		0x00000000, 0x80000000, 0xC0000000,
 | 
			
		||||
		0xE0000000, 0xF0000000, 0xF8000000,
 | 
			
		||||
		0xFC000000, 0xFE000000, 0xFF000000,
 | 
			
		||||
		0xFF800000, 0xFFC00000, 0xFFE00000,
 | 
			
		||||
		0xFFF00000, 0xFFF80000, 0xFFFC0000,
 | 
			
		||||
		0xFFFE0000, 0xFFFF0000, 0xFFFF8000,
 | 
			
		||||
		0xFFFFC000, 0xFFFFE000, 0xFFFFF000,
 | 
			
		||||
		0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00,
 | 
			
		||||
		0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0,
 | 
			
		||||
		0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8,
 | 
			
		||||
		0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ipStartUint32 := iPv4ToUint32(ipStart)
 | 
			
		||||
	ipEndUint32 := iPv4ToUint32(ipEnd)
 | 
			
		||||
 | 
			
		||||
	if ipStartUint32 > ipEndUint32 {
 | 
			
		||||
		log.Fatalf("start IP:%s must be less than end IP:%s", ipStart, ipEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for ipEndUint32 >= ipStartUint32 {
 | 
			
		||||
		maxSize := 32
 | 
			
		||||
		for maxSize > 0 {
 | 
			
		||||
 | 
			
		||||
			maskedBase := ipStartUint32 & cidr2mask[maxSize-1]
 | 
			
		||||
 | 
			
		||||
			if maskedBase != ipStartUint32 {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			maxSize--
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		x := math.Log(float64(ipEndUint32-ipStartUint32+1)) / math.Log(2)
 | 
			
		||||
		maxDiff := 32 - int(math.Floor(x))
 | 
			
		||||
		if maxSize < maxDiff {
 | 
			
		||||
			maxSize = maxDiff
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cidrs = append(cidrs, uInt32ToIPv4(ipStartUint32)+"/"+strconv.Itoa(maxSize))
 | 
			
		||||
 | 
			
		||||
		ipStartUint32 += uint32(math.Exp2(float64(32 - maxSize)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cidrs, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//Convert IPv4 to uint32
 | 
			
		||||
func iPv4ToUint32(iPv4 string) uint32 {
 | 
			
		||||
 | 
			
		||||
	ipOctets := [4]uint64{}
 | 
			
		||||
 | 
			
		||||
	for i, v := range strings.SplitN(iPv4, ".", 4) {
 | 
			
		||||
		ipOctets[i], _ = strconv.ParseUint(v, 10, 32)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := (ipOctets[0] << 24) | (ipOctets[1] << 16) | (ipOctets[2] << 8) | ipOctets[3]
 | 
			
		||||
 | 
			
		||||
	return uint32(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//Convert uint32 to IP
 | 
			
		||||
func uInt32ToIPv4(iPuInt32 uint32) (iP string) {
 | 
			
		||||
	iP = fmt.Sprintf("%d.%d.%d.%d",
 | 
			
		||||
		iPuInt32>>24,
 | 
			
		||||
		(iPuInt32&0x00FFFFFF)>>16,
 | 
			
		||||
		(iPuInt32&0x0000FFFF)>>8,
 | 
			
		||||
		iPuInt32&0x000000FF)
 | 
			
		||||
	return iP
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								internal/iplibrary/action_utils_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								internal/iplibrary/action_utils_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
func TestIPv4RangeToCIDRRange(t *testing.T) {
 | 
			
		||||
	t.Log(iPv4RangeToCIDRRange("192.168.0.0", "192.168.255.255"))
 | 
			
		||||
}
 | 
			
		||||
@@ -12,11 +12,12 @@ const (
 | 
			
		||||
 | 
			
		||||
// IP条目
 | 
			
		||||
type IPItem struct {
 | 
			
		||||
	Type      string
 | 
			
		||||
	Id        int64
 | 
			
		||||
	IPFrom    uint64
 | 
			
		||||
	IPTo      uint64
 | 
			
		||||
	ExpiredAt int64
 | 
			
		||||
	Type       string `json:"type"`
 | 
			
		||||
	Id         int64  `json:"id"`
 | 
			
		||||
	IPFrom     uint64 `json:"ipFrom"`
 | 
			
		||||
	IPTo       uint64 `json:"ipTo"`
 | 
			
		||||
	ExpiredAt  int64  `json:"expiredAt"`
 | 
			
		||||
	EventLevel string `json:"eventLevel"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 检查是否包含某个IP
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								internal/iplibrary/list_type.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								internal/iplibrary/list_type.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
package iplibrary
 | 
			
		||||
 | 
			
		||||
type IPListType = string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	IPListTypeWhite IPListType = "white"
 | 
			
		||||
	IPListTypeBlack IPListType = "black"
 | 
			
		||||
)
 | 
			
		||||
@@ -120,15 +120,25 @@ func (this *IPListManager) fetch() (hasNext bool, err error) {
 | 
			
		||||
		}
 | 
			
		||||
		if item.IsDeleted {
 | 
			
		||||
			list.Delete(item.Id)
 | 
			
		||||
 | 
			
		||||
			// 操作事件
 | 
			
		||||
			SharedActionManager.DeleteItem(item.ListType, item)
 | 
			
		||||
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		list.Add(&IPItem{
 | 
			
		||||
			Id:         item.Id,
 | 
			
		||||
			Type:       item.Type,
 | 
			
		||||
			IPFrom:     utils.IP2Long(item.IpFrom),
 | 
			
		||||
			IPTo:       utils.IP2Long(item.IpTo),
 | 
			
		||||
			ExpiredAt:  item.ExpiredAt,
 | 
			
		||||
			EventLevel: item.EventLevel,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		// 事件操作
 | 
			
		||||
		SharedActionManager.DeleteItem(item.ListType, item)
 | 
			
		||||
		SharedActionManager.AddItem(item.ListType, item)
 | 
			
		||||
	}
 | 
			
		||||
	this.locker.Unlock()
 | 
			
		||||
	this.version = items[len(items)-1].Version
 | 
			
		||||
 
 | 
			
		||||
@@ -369,6 +369,7 @@ func (this *Node) syncConfig() error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sharedWAFManager.UpdatePolicies(nodeConfig.FindAllFirewallPolicies())
 | 
			
		||||
	iplibrary.SharedActionManager.UpdateActions(nodeConfig.FirewallActions)
 | 
			
		||||
	sharedNodeConfig = nodeConfig
 | 
			
		||||
 | 
			
		||||
	// 发送事件
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user