mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-06 01:50:26 +08:00
增加IP动作
This commit is contained in:
@@ -20,7 +20,7 @@ func Notify(event string) {
|
||||
locker.Lock()
|
||||
callbacks, _ := eventsMap[event]
|
||||
locker.Unlock()
|
||||
|
||||
|
||||
for _, callback := range callbacks {
|
||||
callback()
|
||||
}
|
||||
|
||||
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,
|
||||
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