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.
176 lines
5.0 KiB
Go
176 lines
5.0 KiB
Go
package service
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall"
|
|
fireClient "github.com/1Panel-dev/1Panel/agent/utils/firewall/client"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables"
|
|
)
|
|
|
|
type firewallPortWhitelist struct {
|
|
Port string
|
|
Protocol string
|
|
}
|
|
|
|
func loadConfiguredFirewallPortWhiteList() ([]firewallPortWhitelist, error) {
|
|
value, err := settingRepo.GetValueByKey(constant.FirewallPortWhiteList)
|
|
if err != nil {
|
|
value = constant.FirewallPortWhiteListValue
|
|
if err := settingRepo.UpdateOrCreate(constant.FirewallPortWhiteList, value); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return parseFirewallPortWhiteList(value)
|
|
}
|
|
|
|
func loadFirewallPortWhiteList() ([]firewallPortWhitelist, error) {
|
|
portWhiteList, err := loadConfiguredFirewallPortWhiteList()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
requiredPorts, err := loadRequiredFirewallPortWhiteList()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return normalizeFirewallPortWhiteList(append(portWhiteList, requiredPorts...)), nil
|
|
}
|
|
|
|
func loadRequiredFirewallPortWhiteList() ([]firewallPortWhitelist, error) {
|
|
panelPort := LoadPanelPort()
|
|
if panelPort == "" {
|
|
return nil, fmt.Errorf("find 1panel service port failed")
|
|
}
|
|
return normalizeFirewallPortWhiteList([]firewallPortWhitelist{
|
|
{Port: panelPort, Protocol: "tcp"},
|
|
{Port: loadSSHPort(), Protocol: "tcp"},
|
|
}), nil
|
|
}
|
|
|
|
func parseFirewallPortWhiteList(value string) ([]firewallPortWhitelist, error) {
|
|
items := strings.FieldsFunc(value, func(r rune) bool {
|
|
return r == ',' || r == '\n' || r == ';' || r == ' '
|
|
})
|
|
ports := make([]firewallPortWhitelist, 0, len(items))
|
|
exists := make(map[string]struct{})
|
|
for _, item := range items {
|
|
item = strings.TrimSpace(item)
|
|
if item == "" {
|
|
continue
|
|
}
|
|
port, protocol, ok := strings.Cut(item, "/")
|
|
if !ok {
|
|
protocol = "tcp"
|
|
}
|
|
port = strings.TrimSpace(port)
|
|
protocol = strings.ToLower(strings.TrimSpace(protocol))
|
|
if protocol != "tcp" && protocol != "udp" {
|
|
return nil, fmt.Errorf("invalid firewall port whitelist protocol: %s", item)
|
|
}
|
|
portNum, err := strconv.Atoi(port)
|
|
if err != nil || portNum < 1 || portNum > 65535 {
|
|
return nil, fmt.Errorf("invalid firewall port whitelist: %s", item)
|
|
}
|
|
key := fmt.Sprintf("%d/%s", portNum, protocol)
|
|
if _, ok := exists[key]; ok {
|
|
continue
|
|
}
|
|
exists[key] = struct{}{}
|
|
ports = append(ports, firewallPortWhitelist{Port: strconv.Itoa(portNum), Protocol: protocol})
|
|
}
|
|
return ports, nil
|
|
}
|
|
|
|
func normalizeFirewallPortWhiteList(portWhiteList []firewallPortWhitelist) []firewallPortWhitelist {
|
|
ports := make([]firewallPortWhitelist, 0, len(portWhiteList))
|
|
exists := make(map[string]struct{})
|
|
for _, item := range portWhiteList {
|
|
if item.Port == "" {
|
|
continue
|
|
}
|
|
key := fmt.Sprintf("%s/%s", item.Port, item.Protocol)
|
|
if _, ok := exists[key]; ok {
|
|
continue
|
|
}
|
|
exists[key] = struct{}{}
|
|
ports = append(ports, item)
|
|
}
|
|
return ports
|
|
}
|
|
|
|
func syncFirewallPortWhiteListAfterUpdate(oldValue string) error {
|
|
client, err := firewall.NewFirewallClient()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if client.Name() == "iptables" {
|
|
isInit, _ := iptables.LoadInitStatus("iptables", "base")
|
|
if !isInit {
|
|
return nil
|
|
}
|
|
oldPortWhiteList, err := parseFirewallPortWhiteList(oldValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return syncIptablesFirewallPortWhiteList(true, oldPortWhiteList)
|
|
}
|
|
|
|
isActive, _ := client.Status()
|
|
if !isActive {
|
|
return nil
|
|
}
|
|
portWhiteList, err := loadFirewallPortWhiteList()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
oldPortWhiteList, err := parseFirewallPortWhiteList(oldValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
requiredPorts, err := loadRequiredFirewallPortWhiteList()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
oldPortWhiteList = normalizeFirewallPortWhiteList(append(oldPortWhiteList, requiredPorts...))
|
|
return syncFirewallClientPortWhiteList(client, oldPortWhiteList, portWhiteList)
|
|
}
|
|
|
|
func syncFirewallClientPortWhiteList(client firewall.FilterClient, oldPortWhiteList, portWhiteList []firewallPortWhitelist) error {
|
|
oldPorts := firewallPortWhiteListMap(oldPortWhiteList)
|
|
newPorts := firewallPortWhiteListMap(portWhiteList)
|
|
for _, item := range oldPortWhiteList {
|
|
key := firewallPortWhiteListKey(item)
|
|
if _, ok := newPorts[key]; ok {
|
|
continue
|
|
}
|
|
if err := client.Port(fireClient.FireInfo{Port: item.Port, Protocol: item.Protocol, Strategy: "accept"}, "remove"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, item := range portWhiteList {
|
|
key := firewallPortWhiteListKey(item)
|
|
if _, ok := oldPorts[key]; ok {
|
|
continue
|
|
}
|
|
if err := client.Port(fireClient.FireInfo{Port: item.Port, Protocol: item.Protocol, Strategy: "accept"}, "add"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return client.Reload()
|
|
}
|
|
|
|
func firewallPortWhiteListMap(portWhiteList []firewallPortWhitelist) map[string]struct{} {
|
|
ports := make(map[string]struct{})
|
|
for _, item := range portWhiteList {
|
|
ports[firewallPortWhiteListKey(item)] = struct{}{}
|
|
}
|
|
return ports
|
|
}
|
|
|
|
func firewallPortWhiteListKey(item firewallPortWhitelist) string {
|
|
return item.Port + "/" + item.Protocol
|
|
}
|