mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-02 14:00:25 +08:00
227 lines
5.3 KiB
Go
227 lines
5.3 KiB
Go
// Copyright 2022 GoEdge goedge.cdn@gmail.com. All rights reserved.
|
|
|
|
package firewalls
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
|
"github.com/iwind/TeaGo/types"
|
|
)
|
|
|
|
type firewalldCmd struct {
|
|
cmd *executils.Cmd
|
|
denyIP string
|
|
}
|
|
|
|
type Firewalld struct {
|
|
BaseFirewall
|
|
|
|
isReady bool
|
|
exe string
|
|
cmdQueue chan *firewalldCmd
|
|
}
|
|
|
|
func NewFirewalld() *Firewalld {
|
|
var firewalld = &Firewalld{
|
|
cmdQueue: make(chan *firewalldCmd, 4096),
|
|
}
|
|
|
|
path, err := executils.LookPath("firewall-cmd")
|
|
if err == nil && len(path) > 0 {
|
|
var cmd = executils.NewTimeoutCmd(3*time.Second, path, "--state")
|
|
err := cmd.Run()
|
|
if err == nil {
|
|
firewalld.exe = path
|
|
// TODO check firewalld status with 'firewall-cmd --state' (running or not running),
|
|
// but we should recover the state when firewalld state changes, maybe check it every minutes
|
|
|
|
firewalld.isReady = true
|
|
firewalld.init()
|
|
}
|
|
}
|
|
|
|
return firewalld
|
|
}
|
|
|
|
func (this *Firewalld) init() {
|
|
goman.New(func() {
|
|
for c := range this.cmdQueue {
|
|
var cmd = c.cmd
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
if strings.HasPrefix(err.Error(), "Warning:") {
|
|
continue
|
|
}
|
|
remotelogs.Warn("FIREWALL", "run command failed '"+cmd.String()+"': "+err.Error())
|
|
} else {
|
|
// 关闭连接
|
|
if len(c.denyIP) > 0 {
|
|
conns.SharedMap.CloseIPConns(c.denyIP)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// Name 名称
|
|
func (this *Firewalld) Name() string {
|
|
return "firewalld"
|
|
}
|
|
|
|
func (this *Firewalld) IsReady() bool {
|
|
return this.isReady
|
|
}
|
|
|
|
// IsMock 是否为模拟
|
|
func (this *Firewalld) IsMock() bool {
|
|
return false
|
|
}
|
|
|
|
func (this *Firewalld) AllowPort(port int, protocol string) error {
|
|
if !this.isReady {
|
|
return nil
|
|
}
|
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--add-port="+types.String(port)+"/"+protocol)
|
|
this.pushCmd(cmd, "")
|
|
return nil
|
|
}
|
|
|
|
func (this *Firewalld) AllowPortRangesPermanently(portRanges [][2]int, protocol string) error {
|
|
for _, portRange := range portRanges {
|
|
var port = this.PortRangeString(portRange, protocol)
|
|
|
|
{
|
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--add-port="+port, "--permanent")
|
|
this.pushCmd(cmd, "")
|
|
}
|
|
|
|
{
|
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--add-port="+port)
|
|
this.pushCmd(cmd, "")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (this *Firewalld) RemovePort(port int, protocol string) error {
|
|
if !this.isReady {
|
|
return nil
|
|
}
|
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--remove-port="+types.String(port)+"/"+protocol)
|
|
this.pushCmd(cmd, "")
|
|
return nil
|
|
}
|
|
|
|
func (this *Firewalld) RemovePortRangePermanently(portRange [2]int, protocol string) error {
|
|
var port = this.PortRangeString(portRange, protocol)
|
|
|
|
{
|
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--remove-port="+port, "--permanent")
|
|
this.pushCmd(cmd, "")
|
|
}
|
|
|
|
{
|
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--remove-port="+port)
|
|
this.pushCmd(cmd, "")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (this *Firewalld) PortRangeString(portRange [2]int, protocol string) string {
|
|
if portRange[0] == portRange[1] {
|
|
return types.String(portRange[0]) + "/" + protocol
|
|
} else {
|
|
return types.String(portRange[0]) + "-" + types.String(portRange[1]) + "/" + protocol
|
|
}
|
|
}
|
|
|
|
func (this *Firewalld) RejectSourceIP(ip string, timeoutSeconds int) error {
|
|
if !this.isReady {
|
|
return nil
|
|
}
|
|
|
|
// 避免短时间内重复添加
|
|
if this.checkLatestIP(ip) {
|
|
return nil
|
|
}
|
|
|
|
var family = "ipv4"
|
|
if strings.Contains(ip, ":") {
|
|
family = "ipv6"
|
|
}
|
|
var args = []string{"--add-rich-rule=rule family='" + family + "' source address='" + ip + "' reject"}
|
|
if timeoutSeconds > 0 {
|
|
args = append(args, "--timeout="+types.String(timeoutSeconds)+"s")
|
|
}
|
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, args...)
|
|
this.pushCmd(cmd, ip)
|
|
return nil
|
|
}
|
|
|
|
func (this *Firewalld) DropSourceIP(ip string, timeoutSeconds int, async bool) error {
|
|
if !this.isReady {
|
|
return nil
|
|
}
|
|
|
|
// 避免短时间内重复添加
|
|
if async && this.checkLatestIP(ip) {
|
|
return nil
|
|
}
|
|
|
|
var family = "ipv4"
|
|
if strings.Contains(ip, ":") {
|
|
family = "ipv6"
|
|
}
|
|
var args = []string{"--add-rich-rule=rule family='" + family + "' source address='" + ip + "' drop"}
|
|
if timeoutSeconds > 0 {
|
|
args = append(args, "--timeout="+types.String(timeoutSeconds)+"s")
|
|
}
|
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, args...)
|
|
if async {
|
|
this.pushCmd(cmd, ip)
|
|
return nil
|
|
}
|
|
|
|
// 关闭连接
|
|
defer conns.SharedMap.CloseIPConns(ip)
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
return fmt.Errorf("run command failed '%s': %w", cmd.String(), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (this *Firewalld) RemoveSourceIP(ip string) error {
|
|
if !this.isReady {
|
|
return nil
|
|
}
|
|
|
|
var family = "ipv4"
|
|
if strings.Contains(ip, ":") {
|
|
family = "ipv6"
|
|
}
|
|
for _, action := range []string{"reject", "drop"} {
|
|
var args = []string{"--remove-rich-rule=rule family='" + family + "' source address='" + ip + "' " + action}
|
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, args...)
|
|
this.pushCmd(cmd, "")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (this *Firewalld) pushCmd(cmd *executils.Cmd, denyIP string) {
|
|
select {
|
|
case this.cmdQueue <- &firewalldCmd{cmd: cmd, denyIP: denyIP}:
|
|
default:
|
|
// we discard the command
|
|
}
|
|
}
|