Files
1Panel/agent/utils/firewall/lifecycle/operator.go
T

140 lines
3.6 KiB
Go

package lifecycle
import (
"fmt"
"os"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
)
const fail2BanRestoreWithFirewallMarker = "/run/1panel_fail2ban_restore_with_firewall"
type Operation string
const (
OperationStart Operation = "start"
OperationStop Operation = "stop"
OperationRestart Operation = "restart"
)
type Operator struct {
client Client
}
// DockerRestartError reports that the requested firewall operation completed,
// but rebuilding Docker's firewall rules failed.
type DockerRestartError struct {
Err error
}
func (e *DockerRestartError) Error() string {
return fmt.Sprintf("failed to restart Docker: %v", e.Err)
}
func (e *DockerRestartError) Unwrap() error {
return e.Err
}
func NewOperator(client Client) *Operator {
return &Operator{client: client}
}
func (o *Operator) Operate(operation Operation, withDockerRestart bool, prepareStart func(Client) error) error {
switch operation {
case OperationStart:
if err := o.client.Start(); err != nil {
return err
}
if prepareStart != nil {
if err := prepareStart(o.client); err != nil {
return err
}
}
case OperationStop:
return o.StopWithPrepare(withDockerRestart, nil)
case OperationRestart:
if err := o.client.Restart(); err != nil {
return err
}
if prepareStart != nil {
if err := prepareStart(o.client); err != nil {
return err
}
}
default:
return fmt.Errorf("not supported operation: %s", operation)
}
if withDockerRestart {
if err := controller.HandleRestart("docker"); err != nil {
return &DockerRestartError{Err: err}
}
}
if o.client.Name() == ProviderFirewalld && operation == OperationStart {
return restoreFail2BanAfterFirewallStart()
}
return nil
}
// StopWithPrepare records dependent service state, runs preparation, stops the
// firewall, and optionally restarts Docker in that order.
func (o *Operator) StopWithPrepare(withDockerRestart bool, prepareStop func() error) error {
if o.client.Name() == ProviderFirewalld {
if err := rememberFail2BanBeforeFirewallStop(); err != nil {
return err
}
}
if prepareStop != nil {
if err := prepareStop(); err != nil {
return err
}
}
if err := o.client.Stop(); err != nil {
return err
}
if withDockerRestart {
if err := controller.HandleRestart("docker"); err != nil {
return &DockerRestartError{Err: err}
}
}
return nil
}
func rememberFail2BanBeforeFirewallStop() error {
exists, err := controller.CheckExist("fail2ban.service")
if err != nil {
global.LOG.Warnf("check fail2ban.service installation before stopping the firewall failed: %v", err)
}
if !exists {
return nil
}
active, err := controller.CheckActive("fail2ban.service")
if err != nil {
global.LOG.Warnf("check fail2ban.service status before stopping the firewall failed: %v", err)
}
if !active {
return nil
}
if err := os.WriteFile(fail2BanRestoreWithFirewallMarker, nil, 0600); err != nil {
return fmt.Errorf("mark Fail2Ban for restoration with the firewall: %w", err)
}
return nil
}
func restoreFail2BanAfterFirewallStart() error {
if _, err := os.Stat(fail2BanRestoreWithFirewallMarker); err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("load Fail2Ban restore marker after starting the firewall: %w", err)
}
if err := controller.HandleStart("fail2ban.service"); err != nil {
return fmt.Errorf("restore Fail2Ban after starting the firewall: %w", err)
}
if err := os.Remove(fail2BanRestoreWithFirewallMarker); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("clear Fail2Ban firewall restore status: %w", err)
}
return nil
}