mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-23 16:00:52 +00:00
Port forwarding no longer shares the filter client. FilterClient keeps only
filter capabilities, and forwarding gets its own adapter, service and boot
replay:
- utils/firewall/forwarding holds the provider adapters. firewalld uses native
forward-port, ufw and iptables share the NAT implementation moved out of
client/iptables/forward.go.
- service/forwarding.go owns base info, search, operate, enable and replay.
The API keeps its routes and dispatches on name/type/operate.
- init/firewall replays forwarding through that service instead of loading NAT
rule files inline.
Also adds 1PANEL_FORWARD to the IptablesOp name enum: the frontend already
sends {"name":"1PANEL_FORWARD","operate":"init-forward"} and the validator
rejected it with 400 before reaching the service. Besides that, the only
observable difference is that a forward-tab search no longer triggers the
port/address record cleanup goroutine on the side.
236 lines
5.7 KiB
Go
236 lines
5.7 KiB
Go
package service
|
|
|
|
import (
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
"github.com/1Panel-dev/1Panel/agent/global"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall"
|
|
forwardClient "github.com/1Panel-dev/1Panel/agent/utils/firewall/forwarding"
|
|
)
|
|
|
|
type IForwardingService interface {
|
|
LoadBaseInfo() (dto.FirewallBaseInfo, error)
|
|
SearchWithPage(search dto.ForwardRuleSearch) (int64, interface{}, error)
|
|
Operate(req dto.ForwardRuleOperate) error
|
|
Enable() error
|
|
Replay() error
|
|
}
|
|
|
|
type ForwardingService struct {
|
|
adapterFactory func() (forwardClient.Adapter, error)
|
|
filterFactory func() (firewall.FilterClient, error)
|
|
}
|
|
|
|
func NewIForwardingService() IForwardingService {
|
|
return &ForwardingService{
|
|
adapterFactory: newForwardingAdapter,
|
|
filterFactory: firewall.NewFirewallClient,
|
|
}
|
|
}
|
|
|
|
func newForwardingAdapter() (forwardClient.Adapter, error) {
|
|
client, err := firewall.NewFirewallClient()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return forwardClient.NewAdapter(client.Name())
|
|
}
|
|
|
|
func (s *ForwardingService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
|
|
baseInfo := dto.FirewallBaseInfo{Version: "-", Name: "-"}
|
|
adapter, err := s.adapterFactory()
|
|
if err != nil {
|
|
global.LOG.Errorf("load forwarding failed, err: %v", err)
|
|
return baseInfo, nil
|
|
}
|
|
filter, err := s.filterFactory()
|
|
if err != nil {
|
|
global.LOG.Errorf("load firewall status failed, err: %v", err)
|
|
return baseInfo, nil
|
|
}
|
|
baseInfo.IsExist = true
|
|
baseInfo.Name = adapter.Name()
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
go func() {
|
|
defer wg.Done()
|
|
baseInfo.PingStatus = firewall.LoadPingStatus()
|
|
baseInfo.Version, _ = filter.Version()
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
baseInfo.IsActive, _ = filter.Status()
|
|
baseInfo.IsInit, baseInfo.IsBind = adapter.InitStatus()
|
|
}()
|
|
wg.Wait()
|
|
return baseInfo, nil
|
|
}
|
|
|
|
func (s *ForwardingService) SearchWithPage(req dto.ForwardRuleSearch) (int64, interface{}, error) {
|
|
adapter, err := s.adapterFactory()
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
rules, err := adapter.List()
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
if req.Strategy != "" {
|
|
return 0, nil, nil
|
|
}
|
|
|
|
var filtered []forwardClient.Rule
|
|
for _, rule := range rules {
|
|
if req.Info != "" && !strings.Contains(rule.Port, req.Info) &&
|
|
!strings.Contains(rule.TargetPort, req.Info) && !strings.Contains(rule.TargetIP, req.Info) {
|
|
continue
|
|
}
|
|
filtered = append(filtered, rule)
|
|
}
|
|
total := len(filtered)
|
|
start, end := (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
|
if start > total {
|
|
return int64(total), make([]dto.ForwardRule, 0), nil
|
|
}
|
|
if end > total {
|
|
end = total
|
|
}
|
|
pageRules := filtered[start:end]
|
|
var items []dto.ForwardRule
|
|
if pageRules != nil {
|
|
items = make([]dto.ForwardRule, 0, len(pageRules))
|
|
}
|
|
for _, rule := range pageRules {
|
|
items = append(items, dto.ForwardRule{
|
|
Num: rule.Num,
|
|
Protocol: rule.Protocol,
|
|
Port: rule.Port,
|
|
TargetIP: rule.TargetIP,
|
|
TargetPort: rule.TargetPort,
|
|
Interface: rule.Interface,
|
|
})
|
|
}
|
|
return int64(total), items, nil
|
|
}
|
|
|
|
func (s *ForwardingService) Operate(req dto.ForwardRuleOperate) error {
|
|
adapter, err := s.adapterFactory()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rules, _ := adapter.List()
|
|
kept := rules[:0]
|
|
for _, rule := range rules {
|
|
shouldKeep := true
|
|
for i := range req.Rules {
|
|
reqRule := &req.Rules[i]
|
|
if reqRule.TargetIP == "" {
|
|
reqRule.TargetIP = "127.0.0.1"
|
|
}
|
|
if reqRule.Operation == "remove" && requestMatchesForwardRule(*reqRule, rule) {
|
|
shouldKeep = false
|
|
break
|
|
}
|
|
}
|
|
if shouldKeep {
|
|
kept = append(kept, rule)
|
|
}
|
|
}
|
|
|
|
for _, rule := range kept {
|
|
for _, reqRule := range req.Rules {
|
|
if reqRule.Operation != "remove" && requestMatchesForwardRule(reqRule, rule) {
|
|
return buserr.New("ErrRecordExist")
|
|
}
|
|
}
|
|
}
|
|
|
|
sort.SliceStable(req.Rules, func(i, j int) bool {
|
|
if req.Rules[i].Operation == "remove" && req.Rules[j].Operation != "remove" {
|
|
return true
|
|
}
|
|
if req.Rules[i].Operation != "remove" && req.Rules[j].Operation == "remove" {
|
|
return false
|
|
}
|
|
n1, _ := strconv.Atoi(req.Rules[i].Num)
|
|
n2, _ := strconv.Atoi(req.Rules[j].Num)
|
|
return n1 > n2
|
|
})
|
|
|
|
for _, rule := range req.Rules {
|
|
for _, protocol := range strings.Split(rule.Protocol, "/") {
|
|
targetIP := rule.TargetIP
|
|
if targetIP == "" {
|
|
targetIP = "127.0.0.1"
|
|
}
|
|
err := adapter.Operate(forwardClient.Rule{
|
|
Num: rule.Num,
|
|
Protocol: protocol,
|
|
Port: rule.Port,
|
|
TargetIP: targetIP,
|
|
TargetPort: rule.TargetPort,
|
|
Interface: rule.Interface,
|
|
}, rule.Operation)
|
|
if err == nil {
|
|
continue
|
|
}
|
|
if req.ForceDelete {
|
|
global.LOG.Error(err)
|
|
continue
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func requestMatchesForwardRule(req dto.ForwardRuleOperation, rule forwardClient.Rule) bool {
|
|
for _, protocol := range strings.Split(req.Protocol, "/") {
|
|
if req.Port == rule.Port && req.TargetPort == rule.TargetPort && req.TargetIP == rule.TargetIP &&
|
|
protocol == rule.Protocol && req.Interface == rule.Interface {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *ForwardingService) Enable() error {
|
|
adapter, err := s.adapterFactory()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := adapter.Enable(); err != nil {
|
|
return err
|
|
}
|
|
if adapter.Name() != "firewalld" {
|
|
_ = settingRepo.Update("IptablesForwardStatus", constant.StatusEnable)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *ForwardingService) Replay() error {
|
|
adapter, err := s.adapterFactory()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := adapter.Replay(); err != nil {
|
|
return err
|
|
}
|
|
if adapter.Name() == "firewalld" {
|
|
return nil
|
|
}
|
|
status, _ := settingRepo.GetValueByKey("IptablesForwardStatus")
|
|
if status == constant.StatusEnable {
|
|
return adapter.Enable()
|
|
}
|
|
return nil
|
|
}
|