mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-13 00:00:36 +00:00
Replace database rule synchronization with native rule operations and file-based backup, import, and recovery. Update firewall management views, descriptions, whitelist handling, and translations. Include terminal reconnect fixes and xpack build tags for Darwin build targets. Remove temporary regression test files before committing.
689 lines
23 KiB
Go
689 lines
23 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
|
"github.com/1Panel-dev/1Panel/agent/app/task"
|
|
"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/cmd"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall"
|
|
dockerfirewall "github.com/1Panel-dev/1Panel/agent/utils/firewall/docker_guard"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall/filter"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall/forwarding"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall/iptables_helper"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall/lifecycle"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall/nftables_helper"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type IFirewallSettingService interface {
|
|
CreatePortWhitelist(context.Context, dto.FirewallPortWhitelistCreate) error
|
|
UpdatePortWhitelist(context.Context, dto.FirewallPortWhitelistUpdate) error
|
|
DeletePortWhitelist(context.Context, dto.FirewallPortWhitelistDelete) error
|
|
Load(context.Context) (dto.FirewallSettings, error)
|
|
Operate(context.Context, dto.FirewallBackendOperation) error
|
|
OperateFamily(dto.FirewallFamilyOperation) (dto.FilterChainOperationResponse, error)
|
|
OperateIPv6(dto.FirewallIPv6Operation) (dto.FilterChainOperationResponse, error)
|
|
}
|
|
|
|
type FirewallSettingService struct{}
|
|
|
|
var firewallWhitelistMu sync.Mutex
|
|
|
|
func (s *FirewallSettingService) CreatePortWhitelist(ctx context.Context, request dto.FirewallPortWhitelistCreate) (result error) {
|
|
firewallWhitelistMu.Lock()
|
|
firewallRuleMutationMu.Lock()
|
|
defer func() {
|
|
firewallRuleMutationMu.Unlock()
|
|
firewallWhitelistMu.Unlock()
|
|
if result == nil {
|
|
result = newFirewallService().SyncPortWhitelist(ctx)
|
|
}
|
|
}()
|
|
current, err := loadFirewallPortWhiteList()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
current = append(current, request.Rule)
|
|
current, err = firewall.ValidatePortWhitelist(current)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := validateFirewallWhitelistFamilies(current); err != nil {
|
|
return err
|
|
}
|
|
value, err := json.Marshal(current)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return settingRepo.UpdateOrCreate(constant.FirewallPortWhiteList, string(value))
|
|
}
|
|
|
|
func (s *FirewallSettingService) UpdatePortWhitelist(ctx context.Context, request dto.FirewallPortWhitelistUpdate) (result error) {
|
|
firewallWhitelistMu.Lock()
|
|
firewallRuleMutationMu.Lock()
|
|
defer func() {
|
|
firewallRuleMutationMu.Unlock()
|
|
firewallWhitelistMu.Unlock()
|
|
if result == nil {
|
|
result = newFirewallService().SyncPortWhitelist(ctx)
|
|
}
|
|
}()
|
|
current, err := loadFirewallPortWhiteList()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
index, err := findPortWhitelistRule(current, request.OldRule)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
current[index] = request.Rule
|
|
current, err = firewall.ValidatePortWhitelist(current)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := validateFirewallWhitelistFamilies(current); err != nil {
|
|
return err
|
|
}
|
|
value, err := json.Marshal(current)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return settingRepo.UpdateOrCreate(constant.FirewallPortWhiteList, string(value))
|
|
}
|
|
|
|
func (s *FirewallSettingService) DeletePortWhitelist(ctx context.Context, request dto.FirewallPortWhitelistDelete) error {
|
|
firewallWhitelistMu.Lock()
|
|
firewallRuleMutationMu.Lock()
|
|
defer func() {
|
|
firewallRuleMutationMu.Unlock()
|
|
firewallWhitelistMu.Unlock()
|
|
}()
|
|
current, err := loadFirewallPortWhiteList()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if request.Rule == nil {
|
|
return filter.ErrInvalidRule
|
|
}
|
|
index, err := findPortWhitelistRule(current, *request.Rule)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
current = slices.Delete(current, index, index+1)
|
|
current, err = firewall.ValidatePortWhitelist(current)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := validateFirewallWhitelistFamilies(current); err != nil {
|
|
return err
|
|
}
|
|
value, err := json.Marshal(current)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return settingRepo.UpdateOrCreate(constant.FirewallPortWhiteList, string(value))
|
|
}
|
|
|
|
func (s *FirewallSettingService) Load(ctx context.Context) (dto.FirewallSettings, error) {
|
|
families, err := loadFirewallFamilies()
|
|
if err != nil {
|
|
return dto.FirewallSettings{}, err
|
|
}
|
|
result := dto.FirewallSettings{PingStatus: firewall.LoadPingStatus(), IPv6Enabled: slices.Contains(families, constant.FirewallFamilyIPv6)}
|
|
|
|
installed := make(map[string]bool)
|
|
for _, name := range lifecycle.InstalledProviders() {
|
|
installed[name] = true
|
|
}
|
|
systemBackend, _ := settingRepo.GetValueByKey(constant.FirewallSystemBackendKey)
|
|
result.System.Selected = strings.TrimSpace(systemBackend)
|
|
if result.System.Selected == "" {
|
|
if client, err := lifecycle.NewClient(""); err == nil {
|
|
result.System.Selected = client.Name()
|
|
}
|
|
}
|
|
result.System.Current = result.System.Selected
|
|
for _, name := range []string{
|
|
constant.FirewallProviderFirewalld,
|
|
constant.FirewallProviderUFW,
|
|
constant.FirewallProviderIptables,
|
|
constant.FirewallProviderNftables,
|
|
} {
|
|
option := dto.FirewallBackendOption{Name: name, Installed: installed[name], Supported: true}
|
|
if option.Installed && name == result.System.Selected {
|
|
client, err := lifecycle.NewClient(name)
|
|
if err != nil {
|
|
option.Message = err.Error()
|
|
} else if name == constant.FirewallProviderIptables || name == constant.FirewallProviderNftables {
|
|
overview, err := loadSystemFirewallOverview(name, "base", families)
|
|
if err != nil {
|
|
option.Message = err.Error()
|
|
}
|
|
option.Initialized, option.Bound = overview.IsInit, overview.IsBind
|
|
option.IPv4, option.IPv6 = overview.IPv4, overview.IPv6
|
|
} else if option.Active, err = client.Status(); err != nil {
|
|
option.Message = err.Error()
|
|
}
|
|
}
|
|
if name == result.System.Selected && name == constant.FirewallProviderIptables {
|
|
if commands, err := lifecycle.ResolveIptablesCommands(); err == nil {
|
|
option.Implementation = commands.IPv4
|
|
}
|
|
}
|
|
result.System.Options = append(result.System.Options, option)
|
|
}
|
|
|
|
forwardingBackend, _ := settingRepo.GetValueByKey(constant.FirewallForwardingBackendKey)
|
|
result.Forwarding.Selected = strings.TrimSpace(forwardingBackend)
|
|
if result.Forwarding.Selected == "" {
|
|
result.Forwarding.Selected = constant.FirewallProviderIptables
|
|
}
|
|
result.Forwarding.Current = result.Forwarding.Selected
|
|
for _, name := range []string{constant.FirewallProviderIptables, constant.FirewallProviderNftables} {
|
|
option := dto.FirewallBackendOption{Name: name, Installed: installed[name], Supported: true}
|
|
if option.Installed && name == result.Forwarding.Selected {
|
|
manager, err := newForwardingAdapterFor(ctx, name)
|
|
if err != nil {
|
|
option.Message = err.Error()
|
|
} else {
|
|
status, statusErr := loadForwardingFirewallOverview(manager, families)
|
|
option.IPv4, option.IPv6 = status.IPv4, status.IPv6
|
|
if statusErr != nil {
|
|
option.Message = statusErr.Error()
|
|
} else {
|
|
option.Initialized, option.Bound = status.IsInit, status.IsBind
|
|
}
|
|
if result.IPv6Enabled && name == constant.FirewallProviderIptables && !option.IPv6.Available {
|
|
if commands, err := lifecycle.ResolveIptablesCommands(); err == nil && !commands.IPv6Available() {
|
|
option.IPv6.Reason = dockerfirewall.ReasonCommandMissing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if name == result.Forwarding.Selected && name == constant.FirewallProviderIptables {
|
|
if commands, err := lifecycle.ResolveIptablesCommands(); err == nil {
|
|
option.Implementation = commands.IPv4
|
|
}
|
|
}
|
|
result.Forwarding.Options = append(result.Forwarding.Options, option)
|
|
}
|
|
|
|
dockerInstalled := cmd.Which("docker")
|
|
dockerVersion := ""
|
|
if dockerInstalled {
|
|
dockerVersion = loadDockerEngineVersion(ctx)
|
|
}
|
|
dockerBackend, _ := settingRepo.GetValueByKey(constant.FirewallDockerBackendKey)
|
|
dockerBackend = strings.ToLower(strings.TrimSpace(dockerBackend))
|
|
if dockerBackend == constant.FirewallProviderIptables || dockerBackend == constant.FirewallProviderNftables {
|
|
result.Docker.Selected = dockerBackend
|
|
}
|
|
result.Docker.Current = result.Docker.Selected
|
|
for _, name := range []string{constant.FirewallProviderIptables, constant.FirewallProviderNftables} {
|
|
option := dto.FirewallBackendOption{
|
|
Name: name, Installed: installed[name], Supported: dockerInstalled,
|
|
Active: dockerInstalled && installed[name] && result.Docker.Selected == name,
|
|
}
|
|
if name == constant.FirewallProviderNftables && dockerInstalled && !dockerNftablesSupported(dockerVersion) {
|
|
option.Supported = false
|
|
option.SupportReason = "docker_version_unsupported"
|
|
option.Active = false
|
|
}
|
|
if option.Active {
|
|
guard := newDockerFirewallRuntime(ctx, name)
|
|
for _, family := range families {
|
|
status := guard.Status(family)
|
|
option.Initialized = option.Initialized || status.Initialized
|
|
option.Bound = option.Bound || status.Bound
|
|
info := dto.FirewallBackendFamilyStatus{
|
|
Available: status.Reason != dockerfirewall.ReasonCommandMissing,
|
|
Initialized: status.Initialized, Bound: status.Bound, Reason: status.Reason,
|
|
}
|
|
if family == constant.FirewallFamilyIPv4 {
|
|
option.IPv4 = info
|
|
} else {
|
|
option.IPv6 = info
|
|
}
|
|
}
|
|
}
|
|
result.Docker.Options = append(result.Docker.Options, option)
|
|
}
|
|
result.PortWhitelist, err = loadPortWhitelistSetting()
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
result.PanelPort = LoadPanelPort()
|
|
sshPort, sshErr := loadSSHWhitelistPortFrom(sshPath)
|
|
if sshErr != nil {
|
|
global.LOG.Warnf("load SSH port for firewall settings: %v", sshErr)
|
|
} else {
|
|
result.SSHPort = sshPort
|
|
}
|
|
return result, err
|
|
}
|
|
|
|
func (s *FirewallSettingService) Operate(ctx context.Context, request dto.FirewallBackendOperation) error {
|
|
if request.Operation == "cleanup" {
|
|
_, err := newFirewallService().Reset(ctx, dto.FirewallRuleReset{Subsystem: request.Subsystem, Provider: filter.Provider(request.Backend)})
|
|
return err
|
|
}
|
|
if err := lockFirewallLifecycleIdle(); err != nil {
|
|
return err
|
|
}
|
|
defer firewallLifecycleTaskMu.Unlock()
|
|
if request.Subsystem != "system" && request.Backend != constant.FirewallProviderIptables && request.Backend != constant.FirewallProviderNftables {
|
|
return fmt.Errorf("%s only supports iptables or nftables", request.Subsystem)
|
|
}
|
|
if request.Subsystem == "system" && (request.Backend != constant.FirewallProviderIptables && request.Backend != constant.FirewallProviderNftables) && request.Operation != "select" {
|
|
return fmt.Errorf("%s does not support initialization or cleanup", request.Backend)
|
|
}
|
|
switch request.Subsystem {
|
|
case "system":
|
|
if err := s.operateSystem(request); err != nil {
|
|
return err
|
|
}
|
|
if request.Operation == "initialize" {
|
|
service := newFirewallService()
|
|
whitelistErr := service.SyncPortWhitelist(ctx)
|
|
return whitelistErr
|
|
}
|
|
return nil
|
|
case "forwarding":
|
|
return s.operateForwarding(ctx, request)
|
|
case "docker":
|
|
return s.operateDocker(ctx, request)
|
|
default:
|
|
return fmt.Errorf("unsupported firewall subsystem %q", request.Subsystem)
|
|
}
|
|
}
|
|
|
|
func (s *FirewallSettingService) OperateFamily(request dto.FirewallFamilyOperation) (dto.FilterChainOperationResponse, error) {
|
|
if request.Family != constant.FirewallFamilyIPv4 && request.Family != constant.FirewallFamilyIPv6 {
|
|
return dto.FilterChainOperationResponse{}, filter.ErrInvalidScope
|
|
}
|
|
if request.Backend != constant.FirewallProviderIptables && request.Backend != constant.FirewallProviderNftables {
|
|
return dto.FilterChainOperationResponse{}, filter.ErrUnsupportedScope
|
|
}
|
|
if request.Operation != "initialize" && request.Operation != "repair" && request.Operation != "bind" {
|
|
return dto.FilterChainOperationResponse{}, filter.ErrRuleOperation
|
|
}
|
|
subsystem := ""
|
|
switch request.Subsystem {
|
|
case "system":
|
|
subsystem = firewallTaskHost
|
|
case "forwarding":
|
|
subsystem = firewallTaskForwarding
|
|
case "docker":
|
|
subsystem = firewallTaskDocker
|
|
default:
|
|
return dto.FilterChainOperationResponse{}, filter.ErrInvalidScope
|
|
}
|
|
if err := task.CheckScopeTaskIsExecuting(task.TaskScopeFirewall, 0); err != nil {
|
|
return dto.FilterChainOperationResponse{}, err
|
|
}
|
|
return queueFirewallRuleTask(subsystem, task.TaskExec, "", nil, func(t *task.Task) error {
|
|
if err := lockFirewallLifecycleIdle(); err != nil {
|
|
return err
|
|
}
|
|
defer firewallLifecycleTaskMu.Unlock()
|
|
t.Logf("backend=%s family=%s operation=%s", request.Backend, request.Family, request.Operation)
|
|
families, err := loadFirewallFamilies()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !slices.Contains(families, request.Family) {
|
|
return fmt.Errorf("IPv6 firewall support is disabled")
|
|
}
|
|
initialize := request.Operation != "bind"
|
|
switch request.Subsystem {
|
|
case "system":
|
|
firewallWhitelistMu.Lock()
|
|
defer firewallWhitelistMu.Unlock()
|
|
if err := newFirewallService().checkSelectedProvider(t.TaskCtx, filter.Provider(request.Backend)); err != nil {
|
|
return err
|
|
}
|
|
ports, err := loadFirewallPortWhiteList()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
required, err := firewall.RequiredPortWhitelist(ports)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
firewallRuleMutationMu.Lock()
|
|
if request.Backend == constant.FirewallProviderIptables {
|
|
err = iptables_helper.OperateFamily(request.Family, initialize, required)
|
|
} else {
|
|
err = nftables_helper.OperateFamily(filter.Family(request.Family), initialize, required)
|
|
}
|
|
firewallRuleMutationMu.Unlock()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if initialize {
|
|
if err := newFirewallService().applyPortWhitelist(t.TaskCtx, ports, nil, filter.Family(request.Family)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return settingRepo.UpdateOrCreate("IptablesStatus", constant.StatusEnable)
|
|
case "forwarding":
|
|
forwardingMutationMu.Lock()
|
|
defer forwardingMutationMu.Unlock()
|
|
manager, err := newForwardingAdapter(t.TaskCtx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if manager.Name() != request.Backend {
|
|
return filter.ErrProviderUnavailable
|
|
}
|
|
if err := manager.OperateFamily(request.Family, initialize); err != nil {
|
|
return err
|
|
}
|
|
rules, err := manager.List()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := persistForwardingRules(manager, rules); err != nil {
|
|
return err
|
|
}
|
|
return settingRepo.UpdateOrCreate(constant.FirewallForwardingInitializedKey, constant.StatusEnable)
|
|
default:
|
|
dockerPortGuardServiceMu.Lock()
|
|
defer dockerPortGuardServiceMu.Unlock()
|
|
runtime, backend, err := newDockerPortGuardService().runtimeForDocker(t.TaskCtx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if backend != request.Backend {
|
|
return filter.ErrProviderUnavailable
|
|
}
|
|
if err := runtime.OperateFamily(request.Family, initialize); err != nil {
|
|
return err
|
|
}
|
|
inventory, err := runtime.ListPolicies()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := persistDockerRules(backend, inventory); err != nil {
|
|
return err
|
|
}
|
|
return settingRepo.UpdateOrCreate(constant.FirewallDockerPortGuardStatusKey, constant.StatusEnable)
|
|
}
|
|
})
|
|
}
|
|
|
|
func (s *FirewallSettingService) OperateIPv6(request dto.FirewallIPv6Operation) (dto.FilterChainOperationResponse, error) {
|
|
if request.Status != constant.StatusEnable && request.Status != constant.StatusDisable {
|
|
return dto.FilterChainOperationResponse{}, filter.ErrInvalidRule
|
|
}
|
|
if err := task.CheckScopeTaskIsExecuting(task.TaskScopeFirewall, 0); err != nil {
|
|
return dto.FilterChainOperationResponse{}, err
|
|
}
|
|
return queueFirewallRuleTask(firewallTaskHost, task.TaskExec, "", nil, func(t *task.Task) error {
|
|
if err := lockFirewallLifecycleIdle(); err != nil {
|
|
return err
|
|
}
|
|
defer firewallLifecycleTaskMu.Unlock()
|
|
firewallWhitelistMu.Lock()
|
|
defer firewallWhitelistMu.Unlock()
|
|
firewallRuleMutationMu.Lock()
|
|
defer firewallRuleMutationMu.Unlock()
|
|
forwardingMutationMu.Lock()
|
|
defer forwardingMutationMu.Unlock()
|
|
dockerPortGuardServiceMu.Lock()
|
|
defer dockerPortGuardServiceMu.Unlock()
|
|
if request.Status == constant.StatusEnable {
|
|
return settingRepo.UpdateOrCreate(constant.FirewallIPv6SupportKey, request.Status)
|
|
}
|
|
ports, err := loadFirewallPortWhiteList()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
value, err := json.Marshal(ipv4PortWhitelist(ports))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
installed := lifecycle.InstalledProviders()
|
|
for _, selection := range []struct{ subsystem, key string }{
|
|
{"system", constant.FirewallSystemBackendKey},
|
|
{"forwarding", constant.FirewallForwardingBackendKey},
|
|
{"docker", constant.FirewallDockerBackendKey},
|
|
} {
|
|
backend, err := settingRepo.GetValueByKey(selection.key)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
backend = strings.ToLower(strings.TrimSpace(backend))
|
|
if backend == "" {
|
|
if selection.subsystem == "system" {
|
|
if len(installed) == 0 {
|
|
continue
|
|
}
|
|
client, err := NewSelectedSystemFirewallClient()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
backend = client.Name()
|
|
} else {
|
|
backend = constant.FirewallProviderIptables
|
|
}
|
|
}
|
|
if !slices.Contains(installed, backend) || (backend != constant.FirewallProviderIptables && backend != constant.FirewallProviderNftables) {
|
|
continue
|
|
}
|
|
t.Logf("disable IPv6 firewall bindings: subsystem=%s backend=%s", selection.subsystem, backend)
|
|
switch selection.subsystem {
|
|
case "system":
|
|
if backend == constant.FirewallProviderIptables {
|
|
err = iptables_helper.UnbindIPv6BaseChains()
|
|
} else {
|
|
err = nftables_helper.SetTableDormant(t.TaskCtx, "ip6", nftables_helper.TableName)
|
|
if err == nil {
|
|
err = nftables_helper.PersistRuleset(t.TaskCtx)
|
|
}
|
|
}
|
|
case "forwarding":
|
|
var manager forwarding.Adapter
|
|
manager, err = newForwardingAdapterFor(t.TaskCtx, backend)
|
|
if err == nil {
|
|
err = manager.UnbindFamily(constant.FirewallFamilyIPv6)
|
|
}
|
|
case "docker":
|
|
err = newDockerFirewallRuntime(t.TaskCtx, backend).Unbind(constant.FirewallFamilyIPv6)
|
|
}
|
|
if err != nil && !errors.Is(err, filter.ErrFamilyUnavailable) {
|
|
return err
|
|
}
|
|
}
|
|
if err := t.TaskCtx.Err(); err != nil {
|
|
return err
|
|
}
|
|
return settingRepo.UpdateValues(map[string]string{constant.FirewallIPv6SupportKey: request.Status, constant.FirewallPortWhiteList: string(value)})
|
|
})
|
|
}
|
|
|
|
func NewIFirewallSettingService() IFirewallSettingService {
|
|
return &FirewallSettingService{}
|
|
}
|
|
|
|
func (s *FirewallSettingService) operateSystem(request dto.FirewallBackendOperation) error {
|
|
firewallRuleMutationMu.Lock()
|
|
defer firewallRuleMutationMu.Unlock()
|
|
if _, err := lifecycle.NewClient(request.Backend); err != nil {
|
|
return err
|
|
}
|
|
previous, _ := settingRepo.GetValueByKey(constant.FirewallSystemBackendKey)
|
|
if previous == "" {
|
|
if client, err := lifecycle.NewClient(""); err == nil {
|
|
previous = client.Name()
|
|
}
|
|
}
|
|
if request.Operation == "select" && previous != "" && previous != request.Backend {
|
|
initialized, err := systemFirewallBackendInitialized(previous)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if initialized {
|
|
return buserr.WithMap("ErrFirewallBackendCleanupRequired", map[string]interface{}{"current": previous, "target": request.Backend}, nil)
|
|
}
|
|
}
|
|
if err := settingRepo.UpdateOrCreate(constant.FirewallSystemBackendKey, request.Backend); err != nil {
|
|
return err
|
|
}
|
|
rollback := func(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
_ = settingRepo.UpdateOrCreate(constant.FirewallSystemBackendKey, previous)
|
|
return err
|
|
}
|
|
if request.Operation == "select" {
|
|
return nil
|
|
}
|
|
initErr := newFirewallService().operateFilterChainBaseLocked(request.Backend, dto.FilterChainOperation{
|
|
Name: constant.FirewallBasicChain, Operate: string(firewall.BaseOperationInit),
|
|
})
|
|
if initErr != nil {
|
|
return rollback(initErr)
|
|
}
|
|
return settingRepo.UpdateOrCreate(constant.FirewallFilterInitializedKey, constant.StatusEnable)
|
|
}
|
|
|
|
func systemFirewallBackendInitialized(backend string) (bool, error) {
|
|
client, err := lifecycle.NewClient(backend)
|
|
if err != nil {
|
|
if errors.Is(err, lifecycle.ErrNotInstalled) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
if backend == constant.FirewallProviderIptables || backend == constant.FirewallProviderNftables {
|
|
for _, family := range []string{constant.FirewallFamilyIPv4, constant.FirewallFamilyIPv6} {
|
|
initialized, _, err := loadSystemFirewallFamilyStatus(backend, family)
|
|
if family == constant.FirewallFamilyIPv6 && errors.Is(err, filter.ErrFamilyUnavailable) {
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if initialized {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
return client.Status()
|
|
}
|
|
|
|
func (s *FirewallSettingService) operateForwarding(ctx context.Context, request dto.FirewallBackendOperation) error {
|
|
if _, err := newForwardingAdapterFor(ctx, request.Backend); err != nil {
|
|
return err
|
|
}
|
|
previous, _ := settingRepo.GetValueByKey(constant.FirewallForwardingBackendKey)
|
|
if request.Operation == "select" {
|
|
current := previous
|
|
if current == "" {
|
|
detected, err := newForwardingAdapter(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
current = detected.Name()
|
|
}
|
|
initialized, err := forwardingBackendInitialized(ctx, current)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if current != request.Backend && initialized {
|
|
return buserr.WithMap("ErrFirewallBackendCleanupRequired", map[string]interface{}{"current": current, "target": request.Backend}, nil)
|
|
}
|
|
}
|
|
if err := settingRepo.UpdateOrCreate(constant.FirewallForwardingBackendKey, request.Backend); err != nil {
|
|
return err
|
|
}
|
|
if request.Operation == "initialize" {
|
|
return newForwardingService().Enable(ctx)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func forwardingBackendInitialized(ctx context.Context, backend string) (bool, error) {
|
|
manager, err := newForwardingAdapterFor(ctx, backend)
|
|
if err != nil {
|
|
if errors.Is(err, lifecycle.ErrNotInstalled) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
for _, family := range []string{constant.FirewallFamilyIPv4, constant.FirewallFamilyIPv6} {
|
|
initialized, _, err := manager.FamilyStatus(family)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if initialized {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (s *FirewallSettingService) operateDocker(ctx context.Context, request dto.FirewallBackendOperation) error {
|
|
|
|
previous, _ := settingRepo.GetValueByKey(constant.FirewallDockerBackendKey)
|
|
if request.Operation == "select" {
|
|
current := previous
|
|
if current == "" {
|
|
current = constant.FirewallProviderNftables
|
|
if request.Backend == constant.FirewallProviderNftables {
|
|
current = constant.FirewallProviderIptables
|
|
}
|
|
}
|
|
initialized, err := dockerGuardBackendInitialized(ctx, current)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if current != request.Backend && initialized {
|
|
return buserr.WithMap("ErrFirewallBackendCleanupRequired", map[string]interface{}{"current": current, "target": request.Backend}, nil)
|
|
}
|
|
}
|
|
if err := settingRepo.UpdateOrCreate(constant.FirewallDockerBackendKey, request.Backend); err != nil {
|
|
return err
|
|
}
|
|
if request.Operation == "select" {
|
|
if err := (&DockerService{}).UpdateFirewallBackend(request.Backend); err != nil {
|
|
_ = settingRepo.UpdateOrCreate(constant.FirewallDockerBackendKey, previous)
|
|
return err
|
|
}
|
|
}
|
|
if request.Operation == "initialize" {
|
|
if err := newDockerPortGuardService().Operate(ctx, dto.DockerPortGuardOperation{Operation: "initialize"}); err != nil {
|
|
_ = settingRepo.UpdateOrCreate(constant.FirewallDockerBackendKey, previous)
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func dockerGuardBackendInitialized(ctx context.Context, backend string) (bool, error) {
|
|
guard := newDockerFirewallRuntime(ctx, backend)
|
|
for _, family := range []string{dockerfirewall.FamilyIPv4, dockerfirewall.FamilyIPv6} {
|
|
initialized, err := guard.Initialized(family)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if initialized {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|