1Panel/backend/utils/toolbox/fail2ban.go
巴山夜语 79020abb1c
feat(systemctl): implement service manager initialization and command execution (#8380)
* feat(systemctl): 实现服务管理器初始化和命令执行

- 新增 systemctl 包,实现对 systemd、openrc 和 sysvinit 三种服务管理器的支持
- 添加服务状态检查、启动、停止、重启和启用/禁用功能
- 实现服务发现和智能服务名处理
- 添加配置文件查看功能
- 优化错误处理和日志记录

* refactor(system): 重构系统服务管理逻辑

- 引入 systemctl 工具包以统一处理系统服务
- 优化服务状态获取、配置文件路径解析等逻辑
- 重构 HostToolService 中的 GetToolStatus 方法
- 更新 DockerService、SettingService 等相关服务的处理方式
- 调整快照创建和恢复过程中的服务处理逻辑

* feat(utils): 添加目录复制功能并优化文件复制逻辑

- 新增 CopyDirs 函数,用于复制整个目录及其内容
- 添加对符号链接的复制支持
- 实现通用的 Copy 函数,根据文件类型自动选择 CopyFile 或 CopyDirs
- 在 CopyFile 函数中增加对源文件是目录的检查和错误提示

* refactortoolbox: 重构 Fail2ban 和 Pure-FTPd 的管理逻辑

- 优化了 Fail2ban 和 Pure-FTPd 的启动、停止、重启等操作的实现
- 改进了 Fail2ban 版本信息的获取方法
- 统一了错误处理和日志记录的格式
- 调整了部分导入的包,提高了代码的可维护性

* build: 禁用 CGO 以提高构建性能和兼容性

- 在 Linux 后端构建命令中添加 CGO_ENABLED=0 环境变量
- 此修改可以提高构建速度,并确保生成的二进制文件在没有 C 库依赖的环境中也能运行

* refactor(docker): 重构 Docker 服务的重启和操作逻辑

- 添加 isDockerSnapInstalled 函数来判断 Docker 是否通过 Snap 安装
- 在 OperateDocker 和 restartDocker 函数中增加对 Snap 安装的处理
- 移除未使用的 getDockerRestartCommand 函数

* fix(service): 优化快照恢复后的服务重启逻辑

- 在使用 systemd 管理服务时,增加 daemon-reload 操作以确保服务配置更新
- 重启 1panel 服务,以应用快照恢复的更改

* refactor(server): 支持非 systemd 系统的恢复操作

- 增加 isSystemd 函数判断系统是否为 systemd 类型
- 根据系统类型选择性地恢复服务文件
- 兼容 systemd 和非 systemd 系统的恢复流程

* fix(upgrade): 优化升级过程中的服务重启逻辑

- 移动服务重启逻辑到版本号更新之后,修复因提前重启导致的版本号未更新BUG。
- 在 systemctl 重启之前添加 daemon-reload 命令

---------

Co-authored-by: gcsong023 <gcsong023@users.noreply.github.com>
2025-04-17 10:26:13 +08:00

187 lines
4.7 KiB
Go

package toolbox
import (
"fmt"
"os"
"regexp"
"strings"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
)
type Fail2ban struct{}
const defaultPath = "/etc/fail2ban/jail.local"
type FirewallClient interface {
Status() (bool, bool, bool)
Version() (string, error)
Operate(operate string) error
OperateSSHD(operate, ip string) error
}
func NewFail2Ban() (*Fail2ban, error) {
isExist, _ := systemctl.IsExist("fail2ban")
if isExist {
if _, err := os.Stat(defaultPath); err != nil {
if err := initLocalFile(); err != nil {
return nil, err
}
err := systemctl.Restart("fail2ban")
if err != nil {
global.LOG.Errorf("restart fail2ban failed, err: %s", err)
return nil, err
}
}
}
return &Fail2ban{}, nil
}
func (f *Fail2ban) Status() (bool, bool, bool) {
isEnable, _ := systemctl.IsEnable("fail2ban.service")
isActive, _ := systemctl.IsActive("fail2ban.service")
isExist, _ := systemctl.IsExist("fail2ban.service")
return isEnable, isActive, isExist
}
func (f *Fail2ban) Version() string {
stdout, err := cmd.Exec("fail2ban-client --version")
if err != nil {
global.LOG.Errorf("load the fail2ban version failed, err: %s", stdout)
return "-"
}
versionRe := regexp.MustCompile(`(?i)fail2ban[:\s-]*v?(\d+\.\d+\.\d+)`)
matches := versionRe.FindStringSubmatch(stdout)
if len(matches) > 1 {
return matches[1]
}
global.LOG.Errorf("Version regex failed to match output: %s", stdout)
return "-"
}
func (f *Fail2ban) Operate(operate string) error {
switch operate {
case "start", "restart", "stop", "enable", "disable":
stdout, err := systemctl.CustomAction(operate, "fail2ban")
if err != nil {
return fmt.Errorf("%s the fail2ban failed, err: %s", operate, stdout.Output)
}
return nil
case "reload":
stdout, err := cmd.Exec("fail2ban-client reload")
if err != nil {
return fmt.Errorf("fail2ban-client reload, err: %s", stdout)
}
return nil
default:
return fmt.Errorf("not support such operation: %v", operate)
}
}
func (f *Fail2ban) ReBanIPs(ips []string) error {
ipItems, _ := f.ListBanned()
stdout, err := cmd.Execf("fail2ban-client unban --all")
if err != nil {
stdout1, err := cmd.Execf("fail2ban-client set sshd banip %s", strings.Join(ipItems, " "))
if err != nil {
global.LOG.Errorf("rebanip after fail2ban-client unban --all failed, err: %s", stdout1)
}
return fmt.Errorf("fail2ban-client unban --all failed, err: %s", stdout)
}
stdout1, err := cmd.Execf("fail2ban-client set sshd banip %s", strings.Join(ips, " "))
if err != nil {
return fmt.Errorf("handle `fail2ban-client set sshd banip %s` failed, err: %s", strings.Join(ips, " "), stdout1)
}
return nil
}
func (f *Fail2ban) ListBanned() ([]string, error) {
var lists []string
stdout, err := cmd.Exec("fail2ban-client status sshd | grep 'Banned IP list:'")
if err != nil {
return lists, err
}
itemList := strings.Split(strings.Trim(stdout, "\n"), "Banned IP list:")
if len(itemList) != 2 {
return lists, nil
}
ips := strings.Fields(itemList[1])
for _, item := range ips {
if len(item) != 0 {
lists = append(lists, item)
}
}
return lists, nil
}
func (f *Fail2ban) ListIgnore() ([]string, error) {
var lists []string
stdout, err := cmd.Exec("fail2ban-client get sshd ignoreip")
if err != nil {
return lists, err
}
stdout = strings.ReplaceAll(stdout, "|", "")
stdout = strings.ReplaceAll(stdout, "`", "")
stdout = strings.ReplaceAll(stdout, "\n", "")
addrs := strings.Split(stdout, "-")
for _, addr := range addrs {
if !strings.HasPrefix(addr, " ") {
continue
}
lists = append(lists, strings.ReplaceAll(addr, " ", ""))
}
return lists, nil
}
func initLocalFile() error {
f, err := os.Create(defaultPath)
if err != nil {
return err
}
defer f.Close()
initFile := `#DEFAULT-START
[DEFAULT]
bantime = 600
findtime = 300
maxretry = 5
banaction = $banaction
action = %(action_mwl)s
#DEFAULT-END
[sshd]
ignoreip = 127.0.0.1/8
enabled = true
filter = sshd
port = 22
maxretry = 5
findtime = 300
bantime = 600
banaction = $banaction
action = %(action_mwl)s
logpath = $logpath`
banaction := ""
if active, _ := systemctl.IsActive("firewalld"); active {
banaction = "firewallcmd-ipset"
} else if active, _ := systemctl.IsActive("ufw"); active {
banaction = "ufw"
} else {
banaction = "iptables-allports"
}
initFile = strings.ReplaceAll(initFile, "$banaction", banaction)
logPath := ""
if _, err := os.Stat("/var/log/secure"); err == nil {
logPath = "/var/log/secure"
} else {
logPath = "/var/log/auth.log"
}
initFile = strings.ReplaceAll(initFile, "$logpath", logPath)
if err := os.WriteFile(defaultPath, []byte(initFile), 0640); err != nil {
return err
}
return nil
}