mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-23 00:00:52 +00:00
215 lines
5.2 KiB
Go
215 lines
5.2 KiB
Go
package lifecycle
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall/lifecycle/providers"
|
|
)
|
|
|
|
type Operation string
|
|
|
|
const (
|
|
OperationStart Operation = "start"
|
|
OperationStop Operation = "stop"
|
|
OperationRestart Operation = "restart"
|
|
)
|
|
|
|
const (
|
|
ProviderFirewalld = constant.FirewallProviderFirewalld
|
|
ProviderUFW = constant.FirewallProviderUFW
|
|
ProviderIptables = constant.FirewallProviderIptables
|
|
ProviderNftables = constant.FirewallProviderNftables
|
|
)
|
|
|
|
var ErrNotInstalled = errors.New("is not installed")
|
|
|
|
type IptablesCommands struct {
|
|
IPv4 string
|
|
IPv6 string
|
|
Restore4 string
|
|
Restore6 string
|
|
}
|
|
|
|
func (c IptablesCommands) IPv6Available() bool {
|
|
return c.IPv6 != "" && c.Restore6 != ""
|
|
}
|
|
|
|
type Runtime struct {
|
|
Provider string
|
|
Iptables IptablesCommands
|
|
}
|
|
|
|
var which = cmd.Which
|
|
|
|
func DetectRuntime() (Runtime, error) {
|
|
hasFirewalld := which("firewalld")
|
|
hasUFW := which("ufw")
|
|
if hasFirewalld && hasUFW {
|
|
return Runtime{}, errors.New("it is detected that the system has both firewalld and ufw services. To avoid conflicts, please uninstall and try again")
|
|
}
|
|
if hasFirewalld {
|
|
return Runtime{Provider: ProviderFirewalld}, nil
|
|
}
|
|
if hasUFW {
|
|
return Runtime{Provider: ProviderUFW}, nil
|
|
}
|
|
if commands, ok := detectIptablesCommands(""); ok {
|
|
return Runtime{Provider: ProviderIptables, Iptables: commands}, nil
|
|
}
|
|
if commands, ok := detectIptablesCommands("-nft"); ok {
|
|
return Runtime{Provider: ProviderIptables, Iptables: commands}, nil
|
|
}
|
|
if which("nft") {
|
|
return Runtime{Provider: ProviderNftables}, nil
|
|
}
|
|
return Runtime{}, errors.New("no system firewall service detected (firewalld/ufw/iptables/iptables-nft/nft), please check and try again")
|
|
}
|
|
|
|
func detectIptablesCommands(suffix string) (IptablesCommands, bool) {
|
|
ipv4 := "iptables" + suffix
|
|
restore4 := "iptables" + suffix + "-restore"
|
|
if !which(ipv4) || !which(restore4) {
|
|
return IptablesCommands{}, false
|
|
}
|
|
commands := IptablesCommands{IPv4: ipv4, Restore4: restore4}
|
|
ipv6 := "ip6tables" + suffix
|
|
restore6 := "ip6tables" + suffix + "-restore"
|
|
if which(ipv6) && which(restore6) {
|
|
commands.IPv6 = ipv6
|
|
commands.Restore6 = restore6
|
|
}
|
|
return commands, true
|
|
}
|
|
|
|
func ResolveIptablesCommands() (IptablesCommands, error) {
|
|
if commands, ok := detectIptablesCommands(""); ok {
|
|
return commands, nil
|
|
}
|
|
if commands, ok := detectIptablesCommands("-nft"); ok {
|
|
return commands, nil
|
|
}
|
|
return IptablesCommands{}, fmt.Errorf("no complete iptables command family is available")
|
|
}
|
|
|
|
type Client interface {
|
|
Name() string
|
|
Start() error
|
|
Stop() error
|
|
Restart() error
|
|
Status() (bool, error)
|
|
Version() (string, error)
|
|
}
|
|
|
|
// Resetter restores a service-backed firewall to its installation defaults.
|
|
// Implementations must leave the firewall disabled after a successful reset.
|
|
type Resetter interface {
|
|
Reset() error
|
|
}
|
|
|
|
// PreStopResetter prepares a service-backed firewall reset without stopping
|
|
// the service. The lifecycle operator can then stop the service and perform
|
|
// dependent recovery, such as rebuilding Docker firewall rules, in order.
|
|
type PreStopResetter interface {
|
|
ResetBeforeStop() error
|
|
}
|
|
|
|
func NewClient(provider string) (Client, error) {
|
|
if provider == "" {
|
|
runtime, err := DetectRuntime()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
provider = runtime.Provider
|
|
}
|
|
switch provider {
|
|
case "firewalld":
|
|
if !which("firewalld") {
|
|
return nil, fmt.Errorf("firewalld %w", ErrNotInstalled)
|
|
}
|
|
return providers.NewFirewalld()
|
|
case "ufw":
|
|
if !which("ufw") {
|
|
return nil, fmt.Errorf("ufw %w", ErrNotInstalled)
|
|
}
|
|
return providers.NewUFW()
|
|
case "iptables":
|
|
commands, err := ResolveIptablesCommands()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("iptables %w: %v", ErrNotInstalled, err)
|
|
}
|
|
return providers.NewIptables(commands.IPv4)
|
|
case "nftables":
|
|
if !which("nft") {
|
|
return nil, fmt.Errorf("nftables %w", ErrNotInstalled)
|
|
}
|
|
return providers.NewNftables()
|
|
default:
|
|
return nil, fmt.Errorf("unsupported firewall provider: %s", provider)
|
|
}
|
|
}
|
|
|
|
func InstalledProviders() []string {
|
|
providers := make([]string, 0, 4)
|
|
if which("firewalld") {
|
|
providers = append(providers, ProviderFirewalld)
|
|
}
|
|
if which("ufw") {
|
|
providers = append(providers, ProviderUFW)
|
|
}
|
|
if _, err := ResolveIptablesCommands(); err == nil {
|
|
providers = append(providers, ProviderIptables)
|
|
}
|
|
if which("nft") {
|
|
providers = append(providers, ProviderNftables)
|
|
}
|
|
return providers
|
|
}
|
|
|
|
type State struct {
|
|
Name string
|
|
IsActive bool
|
|
}
|
|
|
|
type Status struct {
|
|
State
|
|
Version string
|
|
}
|
|
|
|
func LoadState(client Client) (State, error) {
|
|
state := State{Name: client.Name()}
|
|
var err error
|
|
state.IsActive, err = client.Status()
|
|
return state, err
|
|
}
|
|
|
|
func LoadStatus(client Client) (Status, error) {
|
|
status := Status{Version: "-"}
|
|
var state State
|
|
var version string
|
|
var stateErr, versionErr error
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
go func() {
|
|
defer wg.Done()
|
|
state, stateErr = LoadState(client)
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
version, versionErr = client.Version()
|
|
}()
|
|
wg.Wait()
|
|
status.State = state
|
|
if stateErr != nil {
|
|
return status, errors.Join(stateErr, versionErr)
|
|
}
|
|
if !status.IsActive {
|
|
return status, nil
|
|
}
|
|
status.Version = version
|
|
return status, versionErr
|
|
}
|