mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 07:40:56 +08:00 
			
		
		
		
	自动尝试安装nftables
This commit is contained in:
		@@ -1,6 +1,5 @@
 | 
			
		||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
//go:build linux
 | 
			
		||||
// +build linux
 | 
			
		||||
 | 
			
		||||
package firewalls
 | 
			
		||||
 | 
			
		||||
@@ -55,19 +54,13 @@ func init() {
 | 
			
		||||
 | 
			
		||||
// DDoSProtectionManager DDoS防护
 | 
			
		||||
type DDoSProtectionManager struct {
 | 
			
		||||
	nftPath string
 | 
			
		||||
 | 
			
		||||
	lastAllowIPList []string
 | 
			
		||||
	lastConfig      []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDDoSProtectionManager 获取新对象
 | 
			
		||||
func NewDDoSProtectionManager() *DDoSProtectionManager {
 | 
			
		||||
	nftPath, _ := exec.LookPath("nft")
 | 
			
		||||
 | 
			
		||||
	return &DDoSProtectionManager{
 | 
			
		||||
		nftPath: nftPath,
 | 
			
		||||
	}
 | 
			
		||||
	return &DDoSProtectionManager{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Apply 应用配置
 | 
			
		||||
@@ -93,7 +86,7 @@ func (this *DDoSProtectionManager) Apply(config *ddosconfigs.ProtectionConfig) e
 | 
			
		||||
	}
 | 
			
		||||
	remotelogs.Println("FIREWALL", "change DDoS protection config")
 | 
			
		||||
 | 
			
		||||
	if len(this.nftPath) == 0 {
 | 
			
		||||
	if len(this.nftExe()) == 0 {
 | 
			
		||||
		return errors.New("can not find nft command")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -156,6 +149,11 @@ func (this *DDoSProtectionManager) Apply(config *ddosconfigs.ProtectionConfig) e
 | 
			
		||||
 | 
			
		||||
// 添加TCP规则
 | 
			
		||||
func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig) error {
 | 
			
		||||
	var nftExe = this.nftExe()
 | 
			
		||||
	if len(nftExe) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检查nft版本不能小于0.9
 | 
			
		||||
	if len(nftablesInstance.version) > 0 && stringutil.VersionCompare("0.9", nftablesInstance.version) > 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
@@ -265,7 +263,7 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
 | 
			
		||||
		// 添加新规则
 | 
			
		||||
		for _, port := range ports {
 | 
			
		||||
			if maxConnections > 0 {
 | 
			
		||||
				var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "count", "over", types.String(maxConnections), "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnections", types.String(maxConnections)}))
 | 
			
		||||
				var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "count", "over", types.String(maxConnections), "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnections", types.String(maxConnections)}))
 | 
			
		||||
				cmd.WithStderr()
 | 
			
		||||
				err := cmd.Run()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
@@ -275,7 +273,7 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
 | 
			
		||||
 | 
			
		||||
			// TODO 让用户选择是drop还是reject
 | 
			
		||||
			if maxConnectionsPerIP > 0 {
 | 
			
		||||
				var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "meter", "meter-"+protocol+"-"+types.String(port)+"-max-connections", "{ "+protocol+" saddr ct count over "+types.String(maxConnectionsPerIP)+" }", "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnectionsPerIP", types.String(maxConnectionsPerIP)}))
 | 
			
		||||
				var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "meter", "meter-"+protocol+"-"+types.String(port)+"-max-connections", "{ "+protocol+" saddr ct count over "+types.String(maxConnectionsPerIP)+" }", "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnectionsPerIP", types.String(maxConnectionsPerIP)}))
 | 
			
		||||
				cmd.WithStderr()
 | 
			
		||||
				err := cmd.Run()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
@@ -287,14 +285,14 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
 | 
			
		||||
			// TODO 让用户选择是drop还是reject
 | 
			
		||||
			if newConnectionsMinutelyRate > 0 {
 | 
			
		||||
				if newConnectionsMinutelyRateBlockTimeout > 0 {
 | 
			
		||||
					var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsMinutelyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", types.String(newConnectionsMinutelyRate), types.String(newConnectionsMinutelyRateBlockTimeout)}))
 | 
			
		||||
					var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsMinutelyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", types.String(newConnectionsMinutelyRate), types.String(newConnectionsMinutelyRateBlockTimeout)}))
 | 
			
		||||
					cmd.WithStderr()
 | 
			
		||||
					err := cmd.Run()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", "0"}))
 | 
			
		||||
					var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", "0"}))
 | 
			
		||||
					cmd.WithStderr()
 | 
			
		||||
					err := cmd.Run()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
@@ -307,14 +305,14 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
 | 
			
		||||
			// TODO 让用户选择是drop还是reject
 | 
			
		||||
			if newConnectionsSecondlyRate > 0 {
 | 
			
		||||
				if newConnectionsSecondlyRateBlockTimeout > 0 {
 | 
			
		||||
					var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsSecondlyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", types.String(newConnectionsSecondlyRate), types.String(newConnectionsSecondlyRateBlockTimeout)}))
 | 
			
		||||
					var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsSecondlyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", types.String(newConnectionsSecondlyRate), types.String(newConnectionsSecondlyRateBlockTimeout)}))
 | 
			
		||||
					cmd.WithStderr()
 | 
			
		||||
					err := cmd.Run()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", "0"}))
 | 
			
		||||
					var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", "0"}))
 | 
			
		||||
					cmd.WithStderr()
 | 
			
		||||
					err := cmd.Run()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
@@ -551,3 +549,8 @@ func (this *DDoSProtectionManager) updateAllowIPList(allIPList []string) error {
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *DDoSProtectionManager) nftExe() string {
 | 
			
		||||
	path, _ := exec.LookPath("nft")
 | 
			
		||||
	return path
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										108
									
								
								internal/firewalls/nftables/installer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								internal/firewalls/nftables/installer.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
			
		||||
 | 
			
		||||
package nftables
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/events"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/goman"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
 | 
			
		||||
	executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
 | 
			
		||||
	"github.com/iwind/TeaGo/logs"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	events.On(events.EventReload, func() {
 | 
			
		||||
		// linux only
 | 
			
		||||
		if runtime.GOOS != "linux" {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nodeConfig, err := nodeconfigs.SharedNodeConfig()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if nodeConfig == nil || !nodeConfig.AutoInstallNftables {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if os.Getgid() == 0 { // root user only
 | 
			
		||||
			_, err := exec.LookPath("nft")
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			goman.New(func() {
 | 
			
		||||
				err := NewInstaller().Install()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					// 不需要传到API节点
 | 
			
		||||
					logs.Println("[NFTABLES]install nftables failed: " + err.Error())
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Installer struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewInstaller() *Installer {
 | 
			
		||||
	return &Installer{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Installer) Install() error {
 | 
			
		||||
	// linux only
 | 
			
		||||
	if runtime.GOOS != "linux" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检查是否已经存在
 | 
			
		||||
	_, err := exec.LookPath("nft")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var cmd *executils.Cmd
 | 
			
		||||
 | 
			
		||||
	// check dnf
 | 
			
		||||
	dnfExe, err := exec.LookPath("dnf")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		cmd = executils.NewCmd(dnfExe, "-y", "install", "nftables")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check apt
 | 
			
		||||
	if cmd == nil {
 | 
			
		||||
		aptExe, err := exec.LookPath("apt")
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			cmd = executils.NewCmd(aptExe, "install", "nftables")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check yum
 | 
			
		||||
	if cmd == nil {
 | 
			
		||||
		yumExe, err := exec.LookPath("yum")
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			cmd = executils.NewCmd(yumExe, "-y", "install", "nftables")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cmd == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.WithTimeout(10 * time.Minute)
 | 
			
		||||
	cmd.WithStderr()
 | 
			
		||||
	err = cmd.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New(err.Error() + ": " + cmd.Stderr())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	remotelogs.Println("NFTABLES", "installed nftables with command '"+cmd.String()+"' successfully")
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user