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.FirewallClient, 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 }