2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 17:17:13 +08:00
Files
panel/pkg/firewall/firewall.go
耗子 194287554e refactor: migrate to chi framework (#165)
* refactor: 重构部分完成

* fix: 添加.gitkeep

* fix: build

* fix: lint

* fix: lint

* chore(deps): Update module github.com/go-playground/validator/v10 to v10.22.1 (#162)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update module gorm.io/gorm to v1.25.12 (#161)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update module golang.org/x/net to v0.29.0 (#159)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* workflow: 更新工作流

* workflow: test new download

* feat: merge frontend project

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: update to ubuntu-24.04

* workflow: rename build-*

* workflow: 修改fetch-depth

* chore(deps): Update dependency eslint to v9 (#164)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(frontend): update dependences

* chore(frontend): fix lint

* chore(frontend): fix lint

* workflow: add govulncheck

* workflow: disable nilaway

* feat: 使用新的压缩解压库

* fix: 测试

* fix: 测试

* fix: 测试

* feat: 添加ntp包

* chore(deps): Lock file maintenance (#168)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update module github.com/go-resty/resty/v2 to v2.15.0 (#167)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update dependency @iconify/json to v2.2.249 (#169)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* feat: 添加限流器

* feat: 调整登录限流

* feat: 证书

* fix: lint

* feat: 证书dns

* feat: 证书acme账号

* fix: 修改UserID导致的一系列问题

* feat: 低配版任务队列

* feat: 队列完成

* fix: lint

* fix: lint

* fix: swagger和前端路由

* fix: 去掉ntp测试

* feat: 完成插件接口

* feat: 完成cron

* feat: 完成safe

* chore(deps): Update dependency vue to v3.5.6 (#170)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update dependency @vueuse/core to v11.1.0 (#171)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update dependency vite to v5.4.6 (#173)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update unocss monorepo to v0.62.4 (#172)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore: update renovate config

* feat: 新的firewall客户端

* fix: lint

* feat: firewall完成

* feat: ssh完成

* feat: 容器完成1/2

* feat: 容器完成

* feat: 文件完成

* feat: systemctl及设置

* fix: windows编译

* fix: session not work

* fix: migrate not work

* feat: 前端路由

* feat: 初步支持cli

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-18 01:43:14 +08:00

219 lines
5.4 KiB
Go

package firewall
import (
"errors"
"fmt"
"regexp"
"strings"
"sync"
"github.com/spf13/cast"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
)
type Firewall struct {
forwardListRegex *regexp.Regexp
richRuleRegex *regexp.Regexp
}
func NewFirewall() *Firewall {
firewall := &Firewall{
forwardListRegex: regexp.MustCompile(`^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$`),
richRuleRegex: regexp.MustCompile(`^rule family="([^"]+)" (?:source address="([^"]+)" )?(?:port port="([^"]+)" )?(?:protocol="([^"]+)" )?(accept|drop|reject)$`),
}
return firewall
}
func (r *Firewall) Status() (bool, error) {
return systemctl.Status("firewalld")
}
func (r *Firewall) Version() (string, error) {
return shell.Execf("firewall-cmd --version")
}
func (r *Firewall) ListRule() ([]FireInfo, error) {
var wg sync.WaitGroup
var data []FireInfo
wg.Add(2)
go func() {
defer wg.Done()
out, err := shell.Execf("firewall-cmd --zone=public --list-ports")
if err != nil {
return
}
ports := strings.Split(out, " ")
for _, port := range ports {
if len(port) == 0 {
continue
}
var itemPort FireInfo
if strings.Contains(port, "/") {
itemPort.Port = cast.ToUint(strings.Split(port, "/")[0])
itemPort.Protocol = strings.Split(port, "/")[1]
}
itemPort.Strategy = "accept"
data = append(data, itemPort)
}
}()
go func() {
defer wg.Done()
rich, err := r.ListRichRule()
if err != nil {
return
}
data = append(data, rich...)
}()
wg.Wait()
return data, nil
}
func (r *Firewall) ListForward() ([]FireInfo, error) {
out, err := shell.Execf("firewall-cmd --zone=public --list-forward-ports")
if err != nil {
return nil, err
}
var data []FireInfo
for _, line := range strings.Split(out, "\n") {
line = strings.TrimFunc(line, func(r rune) bool {
return r <= 32
})
if r.forwardListRegex.MatchString(line) {
match := r.forwardListRegex.FindStringSubmatch(line)
if len(match) < 4 {
continue
}
if len(match[4]) == 0 {
match[4] = "127.0.0.1"
}
data = append(data, FireInfo{
Port: cast.ToUint(match[1]),
Protocol: match[2],
TargetIP: match[4],
TargetPort: match[3],
})
}
}
return data, nil
}
func (r *Firewall) ListRichRule() ([]FireInfo, error) {
out, err := shell.Execf("firewall-cmd --zone=public --list-rich-rules")
if err != nil {
return nil, err
}
var data []FireInfo
rules := strings.Split(out, "\n")
for _, rule := range rules {
if len(rule) == 0 {
continue
}
if itemRule, err := r.parseRichRule(rule); err == nil {
data = append(data, *itemRule)
}
}
return data, nil
}
func (r *Firewall) Port(port FireInfo, operation string) error {
stdout, err := shell.Execf("firewall-cmd --zone=public --%s-port=%d/%s --permanent", operation, port.Port, port.Protocol)
if err != nil {
return fmt.Errorf("%s port %d/%s failed, err: %s", operation, port.Port, port.Protocol, stdout)
}
return systemctl.Reload("firewalld")
}
func (r *Firewall) RichRules(rule FireInfo, operation string) error {
families := strings.Split(rule.Family, "/") // ipv4 ipv6
for _, family := range families {
var ruleStr strings.Builder
ruleStr.WriteString(fmt.Sprintf(`rule family="%s" `, family))
if len(rule.Address) != 0 {
ruleStr.WriteString(fmt.Sprintf(`source address="%s" `, rule.Address))
}
if rule.Port != 0 {
ruleStr.WriteString(fmt.Sprintf(`port port="%d" `, rule.Port))
}
if len(rule.Protocol) != 0 {
ruleStr.WriteString(fmt.Sprintf(`protocol="%s" `, rule.Protocol))
}
ruleStr.WriteString(rule.Strategy)
out, err := shell.Execf("firewall-cmd --zone=public --%s-rich-rule '%s' --permanent", operation, ruleStr.String())
if err != nil {
return fmt.Errorf("%s rich rules (%s) failed, err: %s", operation, ruleStr.String(), out)
}
}
return systemctl.Reload("firewalld")
}
func (r *Firewall) PortForward(info Forward, operation string) error {
if err := r.enableForward(); err != nil {
return err
}
var ruleStr strings.Builder
ruleStr.WriteString(fmt.Sprintf("firewall-cmd --zone=public --%s-forward-port=port=%d:proto=%s:", operation, info.Port, info.Protocol))
if info.TargetIP != "" && info.TargetIP != "127.0.0.1" && info.TargetIP != "localhost" {
ruleStr.WriteString(fmt.Sprintf("toaddr=%s:toport=%d", info.TargetIP, info.TargetPort))
} else {
ruleStr.WriteString(fmt.Sprintf("toport=%d", info.TargetPort))
}
ruleStr.WriteString(" --permanent")
out, err := shell.Execf(ruleStr.String()) // nolint: govet
if err != nil {
return fmt.Errorf("%s port forward failed, err: %s", operation, out)
}
return systemctl.Reload("firewalld")
}
func (r *Firewall) parseRichRule(line string) (*FireInfo, error) {
itemRule := new(FireInfo)
if r.richRuleRegex.MatchString(line) {
match := r.richRuleRegex.FindStringSubmatch(line)
if len(match) < 6 {
return nil, errors.New("invalid rich rule")
}
itemRule.Family = match[1]
itemRule.Address = match[2]
itemRule.Port = cast.ToUint(match[3])
itemRule.Protocol = match[4]
itemRule.Strategy = match[5]
}
return itemRule, nil
}
func (r *Firewall) enableForward() error {
out, err := shell.Execf("firewall-cmd --zone=public --query-masquerade")
if err != nil {
if out == "no" {
out, err = shell.Execf("firewall-cmd --zone=public --add-masquerade --permanent")
if err != nil {
return fmt.Errorf("%s: %s", err, out)
}
return systemctl.Reload("firewalld")
}
return fmt.Errorf("%s: %s", err, out)
}
return nil
}