Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion agent/app/dto/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ type SettingInfo struct {

FileRecycleBin string `json:"fileRecycleBin"`
LocalSSHConnShow string `json:"localSSHConnShow"`

FirewallPortWhiteList string `json:"firewallPortWhiteList"`
}

type SettingUpdate struct {
Expand All @@ -33,7 +35,7 @@ type SettingUpdate struct {
}

type AgentSettingUpdate struct {
Key string `json:"key" validate:"required,oneof=SystemIP DockerSockPath FileRecycleBin"`
Key string `json:"key" validate:"required,oneof=SystemIP DockerSockPath FileRecycleBin FirewallPortWhiteList"`
Value string `json:"value"`
}

Expand Down
28 changes: 6 additions & 22 deletions agent/app/service/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -579,30 +579,14 @@ func (u *FirewallService) cleanUnUsedData(client firewall.FirewallClient) {
}

func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) error {
if !global.IsMaster {
if err := client.Port(fireClient.FireInfo{Port: global.CONF.Base.Port, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
} else {
var portSetting model.Setting
_ = global.CoreDB.Where("key = ?", "ServerPort").First(&portSetting).Error
if len(portSetting.Value) != 0 {
if err := client.Port(fireClient.FireInfo{Port: portSetting.Value, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
}
}
if err := client.Port(fireClient.FireInfo{Port: loadSSHPort(), Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
if err := client.Port(fireClient.FireInfo{Port: "80", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
if err := client.Port(fireClient.FireInfo{Port: "443", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
portWhiteList, err := loadFirewallPortWhiteList()
if err != nil {
return err
}
if err := client.Port(fireClient.FireInfo{Port: "443", Protocol: "udp", Strategy: "accept"}, "add"); err != nil {
return err
for _, item := range portWhiteList {
if err := client.Port(fireClient.FireInfo{Port: item.Port, Protocol: item.Protocol, Strategy: "accept"}, "add"); err != nil {
return err
}
}

return client.Reload()
Expand Down
152 changes: 152 additions & 0 deletions agent/app/service/firewall_setting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
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 loadFirewallPortWhiteList() ([]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 parseFullFirewallPortWhiteList(value)
}

func parseFullFirewallPortWhiteList(value string) ([]firewallPortWhitelist, error) {
portWhiteList, err := parseFirewallPortWhiteList(value)
if err != nil {
return nil, err
}
panelPort := LoadPanelPort()
if panelPort == "" {
return nil, fmt.Errorf("find 1panel service port failed")
}
portWhiteList = append(portWhiteList, firewallPortWhitelist{Port: panelPort, Protocol: "tcp"})
portWhiteList = append(portWhiteList, firewallPortWhitelist{Port: loadSSHPort(), Protocol: "tcp"})
return normalizeFirewallPortWhiteList(portWhiteList), 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
}
portWhiteList, err := loadFirewallPortWhiteList()
if err != nil {
return err
}
if client.Name() != "iptables" {
oldPortWhiteList, err := parseFullFirewallPortWhiteList(oldValue)
if err != nil {
return err
}
return syncFirewallClientPortWhiteList(client, oldPortWhiteList, portWhiteList)
}
isInit, _ := iptables.LoadInitStatus("iptables", "base")
if !isInit {
return nil
}
return applyFirewallPortWhiteListRules(portWhiteList, true)
}

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
}
79 changes: 70 additions & 9 deletions agent/app/service/iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,12 @@ func (s *IptablesService) Operate(req dto.IptablesOp) error {
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {
return err
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.BasicBeforeFileName); err != nil {
return err
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.BasicAfterFileName); err != nil {
return err
}
_ = settingRepo.Update("IptablesStatus", constant.StatusEnable)
return nil
case "bind-base-without-init":
Expand Down Expand Up @@ -365,25 +371,80 @@ func initPreRules() error {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT", "-m", "comment", "--comment", "ESTABLISHED Whitelist"); err != nil {
return err
}
panelPort := LoadPanelPort()
if len(panelPort) == 0 {
return errors.New("find 1panel service port failed")
portWhiteList, err := loadFirewallPortWhiteList()
if err != nil {
return err
}
if err := applyFirewallPortWhiteListRules(portWhiteList, false); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p", "tcp", "-j", "DROP"); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p", "udp", "-j", "DROP"); err != nil {
return err
}
return nil
}

func applyFirewallPortWhiteListRules(portWhiteList []firewallPortWhitelist, withSave bool) error {
if err := syncFirewallPortWhiteListRules(portWhiteList); err != nil {
return err
}
ports := []string{"80", "443", panelPort, loadSSHPort()}
for _, item := range ports {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, "-p", "tcp", "-m", "tcp", "--dport", item, "-j", "ACCEPT"); err != nil {
for _, item := range portWhiteList {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, "-p", item.Protocol, "-m", item.Protocol, "--dport", item.Port, "-j", "ACCEPT"); err != nil {
return err
}
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p", "udp", "-m", "udp", "--dport", "443", "-j", "ACCEPT"); err != nil {
if !withSave {
return nil
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.BasicBeforeFileName); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p", "tcp", "-j", "DROP"); err != nil {
return iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.BasicAfterFileName)
}

func syncFirewallPortWhiteListRules(portWhiteList []firewallPortWhitelist) error {
tcpWhitelist := make(map[string]struct{})
udpWhitelist := make(map[string]struct{})
for _, item := range portWhiteList {
if item.Protocol == "udp" {
udpWhitelist[item.Port] = struct{}{}
continue
}
tcpWhitelist[item.Port] = struct{}{}
}

if err := cleanExtraFirewallPortRules(iptables.Chain1PanelBasicBefore, "tcp", tcpWhitelist); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, "-p", "udp", "-j", "DROP"); err != nil {
if err := cleanExtraFirewallPortRules(iptables.Chain1PanelBasicBefore, "udp", udpWhitelist); err != nil {
return err
}
return cleanExtraFirewallPortRules(iptables.Chain1PanelBasicAfter, "udp", map[string]struct{}{})
}

func cleanExtraFirewallPortRules(chain, protocol string, whitelist map[string]struct{}) error {
rules, err := iptables.ReadFilterRulesByChain(chain)
if err != nil {
return err
}
kept := make(map[string]struct{})
for _, rule := range rules {
if rule.Strategy != "accept" || rule.Protocol != protocol || rule.DstPort == "" || rule.SrcIP != "" || rule.DstIP != "" || rule.SrcPort != "" {
continue
}
if _, ok := whitelist[rule.DstPort]; ok {
if _, seen := kept[rule.DstPort]; !seen {
kept[rule.DstPort] = struct{}{}
continue
}
}
if err := iptables.DeleteRule(iptables.FilterTab, chain, "-p", protocol, "-m", protocol, "--dport", rule.DstPort, "-j", "ACCEPT"); err != nil {
return err
}
}
return nil
}

Expand Down
17 changes: 16 additions & 1 deletion agent/app/service/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,22 @@ func (u *SettingService) GetWebsiteDir() string {
}

func (u *SettingService) Update(key, value string) error {
return settingRepo.UpdateOrCreate(key, value)
oldValue := constant.FirewallPortWhiteListValue
if key == constant.FirewallPortWhiteList {
if _, err := parseFirewallPortWhiteList(value); err != nil {
return err
}
if val, err := settingRepo.GetValueByKey(key); err == nil {
oldValue = val
}
}
if err := settingRepo.UpdateOrCreate(key, value); err != nil {
return err
}
if key == constant.FirewallPortWhiteList {
return syncFirewallPortWhiteListAfterUpdate(oldValue)
}
return nil
}

func (u *SettingService) UpdateTerminalAI(req dto.TerminalAIInfo) error {
Expand Down
3 changes: 3 additions & 0 deletions agent/constant/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ const (

SystemRestart = "systemRestart"

FirewallPortWhiteList = "FirewallPortWhiteList"
FirewallPortWhiteListValue = "80/tcp,443/tcp,443/udp"

TypeWebsite = "website"
TypePhp = "php"
TypeSSL = "ssl"
Expand Down
1 change: 1 addition & 0 deletions agent/init/migration/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func InitAgentDB() {
migrations.AddFileShareTable,
migrations.AddFileHistoryTable,
migrations.MigrateLegoV5,
migrations.InitFirewallPortWhiteList,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
Expand Down
17 changes: 17 additions & 0 deletions agent/init/migration/migrations/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,23 @@ var InitIptablesStatus = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "IptablesOutputStatus", Value: constant.StatusDisable}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: constant.FirewallPortWhiteList, Value: constant.FirewallPortWhiteListValue}).Error; err != nil {
return err
}
return nil
},
}

var InitFirewallPortWhiteList = &gormigrate.Migration{
ID: "20260601-init-firewall-port-whitelist",
Migrate: func(tx *gorm.DB) error {
var setting model.Setting
if err := tx.Where("key = ?", constant.FirewallPortWhiteList).First(&setting).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return tx.Create(&model.Setting{Key: constant.FirewallPortWhiteList, Value: constant.FirewallPortWhiteListValue}).Error
}
return err
}
return nil
},
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/api/interface/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export namespace Setting {

fileRecycleBin: string;
localSSHConnShow: string;
firewallPortWhiteList: string;
}
export interface SettingInfo {
systemVersion: string;
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3769,6 +3769,11 @@ const message = {
bindHelper: 'Bind - Firewall rules will only take effect when the status is bound. Confirm?',
unbindHelper:
'Unbind - When unbound, all added firewall rules will become invalid. Proceed with caution. Confirm?',
whiteList: 'Allowlist',
portWhiteList: 'Port allowlist',
portWhiteListAlter:
'Ports in the allowlist are opened automatically when the firewall is initialized or started.',
portWhiteListHelper: 'Protocol can be specified, e.g. 80/tcp or 443/udp. Defaults to tcp when omitted.',
defaultStrategy: 'Default policy for current chain {0} is {1}',
defaultStrategy2:
'Default policy for current chain {0} is {1}, current status is unbound. Added firewall rules will take effect after binding!',
Expand Down
Loading
Loading