mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-23 16:00:52 +00:00
* fix(firewall): manage SSH access and queue stop operations * fix(firewall): reconcile whitelist rules and sync differences
215 lines
5.7 KiB
Go
215 lines
5.7 KiB
Go
package providers
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/controller"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/firewall/filter"
|
|
)
|
|
|
|
type Firewalld struct{}
|
|
|
|
const firewalldConfigDir = "/etc/firewalld"
|
|
|
|
var firewalldConfigSubdirectories = []string{
|
|
"helpers",
|
|
"icmptypes",
|
|
"ipsets",
|
|
"policies",
|
|
"services",
|
|
"zones",
|
|
}
|
|
|
|
func NewFirewalld() (*Firewalld, error) {
|
|
return &Firewalld{}, nil
|
|
}
|
|
|
|
func (f *Firewalld) Name() string {
|
|
return "firewalld"
|
|
}
|
|
|
|
func (f *Firewalld) Status() (bool, error) {
|
|
stdout, err := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en")).RunWithStdout("firewall-cmd", "--state")
|
|
if err != nil {
|
|
if firewalldStopped(stdout, err) {
|
|
return false, nil
|
|
}
|
|
return false, fmt.Errorf("load firewall status failed: %w", err)
|
|
}
|
|
return strings.TrimSpace(stdout) == "running", nil
|
|
}
|
|
|
|
func firewalldStopped(stdout string, err error) bool {
|
|
message := strings.ToLower(strings.TrimSpace(stdout))
|
|
if err != nil {
|
|
message += " " + strings.ToLower(err.Error())
|
|
}
|
|
return strings.Contains(message, "not running")
|
|
}
|
|
|
|
func (f *Firewalld) Version() (string, error) {
|
|
stdout, err := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en")).RunWithStdout("firewall-cmd", "--version")
|
|
if err != nil {
|
|
return "", fmt.Errorf("load the firewall version failed, %v", err)
|
|
}
|
|
return strings.ReplaceAll(stdout, "\n ", ""), nil
|
|
}
|
|
|
|
func (f *Firewalld) Start() error {
|
|
if err := controller.HandleStart("firewalld"); err != nil {
|
|
return fmt.Errorf("enable the firewall failed, err: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func RemoveFirewalldSSHService() error {
|
|
for _, permanent := range []bool{true, false} {
|
|
args := []string{"--zone=" + filter.FirewalldInputZone, "--remove-service=ssh"}
|
|
configuration := "runtime"
|
|
if permanent {
|
|
args = append(args, "--permanent")
|
|
configuration = "permanent"
|
|
}
|
|
if _, err := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en")).RunWithStdout("firewall-cmd", args...); err != nil {
|
|
return fmt.Errorf("remove firewalld SSH service from %s configuration: %w", configuration, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Firewalld) Stop() error {
|
|
if err := controller.HandleStop("firewalld"); err != nil {
|
|
return fmt.Errorf("stop the firewall failed, err: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Firewalld) Reset() error {
|
|
if err := f.ResetBeforeStop(); err != nil {
|
|
return err
|
|
}
|
|
if err := f.Stop(); err != nil {
|
|
return fmt.Errorf("stop firewalld after reset failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Firewalld) ResetBeforeStop() error {
|
|
backupDir := fmt.Sprintf("%s.1panel-backup-%d", firewalldConfigDir, time.Now().UnixNano())
|
|
rollback, err := replaceFirewalldConfig(
|
|
firewalldConfigDir,
|
|
backupDir,
|
|
restoreFirewalldConfigContext,
|
|
validateFirewalldConfig,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("reset firewalld to defaults failed: %w", err)
|
|
}
|
|
if err := controller.Handle("disable", "firewalld"); err != nil {
|
|
return fmt.Errorf(
|
|
"reset firewalld to defaults failed: %w",
|
|
errors.Join(err, rollback()),
|
|
)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func replaceFirewalldConfig(
|
|
configDir string,
|
|
backupDir string,
|
|
prepare func(string) error,
|
|
validate func() error,
|
|
) (func() error, error) {
|
|
info, err := os.Lstat(configDir)
|
|
hadConfig := err == nil
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("inspect firewalld configuration: %w", err)
|
|
}
|
|
if hadConfig && !info.IsDir() {
|
|
return nil, fmt.Errorf("firewalld configuration path %s is not a directory", configDir)
|
|
}
|
|
if hadConfig {
|
|
if _, err := os.Lstat(backupDir); err == nil {
|
|
return nil, fmt.Errorf("firewalld backup path already exists: %s", backupDir)
|
|
} else if !os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("inspect firewalld backup path: %w", err)
|
|
}
|
|
if err := os.Rename(configDir, backupDir); err != nil {
|
|
return nil, fmt.Errorf("back up firewalld configuration: %w", err)
|
|
}
|
|
}
|
|
|
|
rollback := func() error {
|
|
removeErr := os.RemoveAll(configDir)
|
|
if !hadConfig {
|
|
return removeErr
|
|
}
|
|
restoreErr := os.Rename(backupDir, configDir)
|
|
return errors.Join(removeErr, restoreErr)
|
|
}
|
|
fail := func(cause error) (func() error, error) {
|
|
if rollbackErr := rollback(); rollbackErr != nil {
|
|
return nil, errors.Join(cause, fmt.Errorf("rollback firewalld configuration: %w", rollbackErr))
|
|
}
|
|
return nil, cause
|
|
}
|
|
|
|
mode := os.FileMode(0755)
|
|
if hadConfig {
|
|
mode = info.Mode().Perm()
|
|
}
|
|
if err := os.Mkdir(configDir, mode); err != nil {
|
|
return fail(fmt.Errorf("create firewalld configuration directory: %w", err))
|
|
}
|
|
for _, directory := range firewalldConfigSubdirectories {
|
|
if err := os.Mkdir(filepath.Join(configDir, directory), 0755); err != nil {
|
|
return fail(fmt.Errorf("create firewalld %s directory: %w", directory, err))
|
|
}
|
|
}
|
|
if prepare != nil {
|
|
if err := prepare(configDir); err != nil {
|
|
return fail(fmt.Errorf("prepare firewalld configuration directory: %w", err))
|
|
}
|
|
}
|
|
if validate != nil {
|
|
if err := validate(); err != nil {
|
|
return fail(fmt.Errorf("validate default firewalld configuration: %w", err))
|
|
}
|
|
}
|
|
return rollback, nil
|
|
}
|
|
|
|
func restoreFirewalldConfigContext(configDir string) error {
|
|
if _, err := exec.LookPath("restorecon"); err != nil {
|
|
if errors.Is(err, exec.ErrNotFound) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
_, err := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en")).RunWithOptionalSudoAndStdout(
|
|
"restorecon", "-RF", configDir,
|
|
)
|
|
return err
|
|
}
|
|
|
|
func validateFirewalldConfig() error {
|
|
_, err := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en")).RunWithOptionalSudoAndStdout(
|
|
"firewall-offline-cmd", "--get-zones",
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (f *Firewalld) Restart() error {
|
|
if err := controller.HandleRestart("firewalld"); err != nil {
|
|
return fmt.Errorf("restart the firewall failed, err: %v", err)
|
|
}
|
|
return nil
|
|
}
|