mirror of https://github.com/1Panel-dev/1Panel
appstorecrontabdatabasedockerdocker-composedocker-containerdocker-imagedocker-uifilemanagerlamplnmppanel
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
6.2 KiB
251 lines
6.2 KiB
package service |
|
|
|
import ( |
|
"bufio" |
|
"fmt" |
|
"os" |
|
"strconv" |
|
"strings" |
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/dto" |
|
"github.com/1Panel-dev/1Panel/backend/buserr" |
|
"github.com/1Panel-dev/1Panel/backend/utils/firewall" |
|
"github.com/1Panel-dev/1Panel/backend/utils/toolbox" |
|
) |
|
|
|
const defaultFail2BanPath = "/etc/fail2ban/jail.local" |
|
|
|
type Fail2BanService struct{} |
|
|
|
type IFail2BanService interface { |
|
LoadBaseInfo() (dto.Fail2BanBaseInfo, error) |
|
Search(search dto.Fail2BanSearch) ([]string, error) |
|
Operate(operation string) error |
|
OperateSSHD(req dto.Fail2BanSet) error |
|
UpdateConf(req dto.Fail2BanUpdate) error |
|
UpdateConfByFile(req dto.UpdateByFile) error |
|
} |
|
|
|
func NewIFail2BanService() IFail2BanService { |
|
return &Fail2BanService{} |
|
} |
|
|
|
func (u *Fail2BanService) LoadBaseInfo() (dto.Fail2BanBaseInfo, error) { |
|
var baseInfo dto.Fail2BanBaseInfo |
|
client, err := toolbox.NewFail2Ban() |
|
if err != nil { |
|
return baseInfo, err |
|
} |
|
baseInfo.IsEnable, baseInfo.IsActive, baseInfo.IsExist = client.Status() |
|
if !baseInfo.IsActive { |
|
baseInfo.Version = "-" |
|
} else { |
|
baseInfo.Version = client.Version() |
|
} |
|
conf, err := os.ReadFile(defaultFail2BanPath) |
|
if err != nil { |
|
if baseInfo.IsActive { |
|
return baseInfo, fmt.Errorf("read fail2ban conf of %s failed, err: %v", defaultFail2BanPath, err) |
|
} else { |
|
return baseInfo, nil |
|
} |
|
} |
|
lines := strings.Split(string(conf), "\n") |
|
|
|
block := "" |
|
for _, line := range lines { |
|
if strings.HasPrefix(strings.ToLower(line), "[default]") { |
|
block = "default" |
|
continue |
|
} |
|
if strings.HasPrefix(line, "[sshd]") { |
|
block = "sshd" |
|
continue |
|
} |
|
if strings.HasPrefix(line, "[") { |
|
block = "" |
|
continue |
|
} |
|
if block != "default" && block != "sshd" { |
|
continue |
|
} |
|
loadFailValue(line, &baseInfo) |
|
} |
|
|
|
return baseInfo, nil |
|
} |
|
|
|
func (u *Fail2BanService) Search(req dto.Fail2BanSearch) ([]string, error) { |
|
var list []string |
|
client, err := toolbox.NewFail2Ban() |
|
if err != nil { |
|
return nil, err |
|
} |
|
if req.Status == "banned" { |
|
list, err = client.ListBanned() |
|
|
|
} else { |
|
list, err = client.ListIgnore() |
|
} |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return list, nil |
|
} |
|
|
|
func (u *Fail2BanService) Operate(operation string) error { |
|
client, err := toolbox.NewFail2Ban() |
|
if err != nil { |
|
return err |
|
} |
|
return client.Operate(operation) |
|
} |
|
|
|
func (u *Fail2BanService) UpdateConf(req dto.Fail2BanUpdate) error { |
|
if req.Key == "banaction" { |
|
if req.Value == "firewallcmd-ipset" || req.Value == "ufw" { |
|
itemName := "ufw" |
|
if req.Value == "firewallcmd-ipset" { |
|
itemName = "firewalld" |
|
} |
|
client, err := firewall.NewFirewallClient() |
|
if err != nil { |
|
return err |
|
} |
|
if client.Name() != itemName { |
|
return buserr.WithName("ErrBanAction", itemName) |
|
} |
|
status, _ := client.Status() |
|
if status != "running" { |
|
return buserr.WithName("ErrBanAction", itemName) |
|
} |
|
} |
|
} |
|
if req.Key == "logpath" { |
|
if _, err := os.Stat(req.Value); err != nil { |
|
return err |
|
} |
|
} |
|
conf, err := os.ReadFile(defaultFail2BanPath) |
|
if err != nil { |
|
return fmt.Errorf("read fail2ban conf of %s failed, err: %v", defaultFail2BanPath, err) |
|
} |
|
lines := strings.Split(string(conf), "\n") |
|
|
|
isStart, isEnd, hasKey := false, false, false |
|
newFile := "" |
|
for index, line := range lines { |
|
if !isStart && strings.HasPrefix(line, "[sshd]") { |
|
isStart = true |
|
newFile += fmt.Sprintf("%s\n", line) |
|
continue |
|
} |
|
if !isStart || isEnd { |
|
newFile += fmt.Sprintf("%s\n", line) |
|
continue |
|
} |
|
if strings.HasPrefix(line, req.Key) { |
|
hasKey = true |
|
newFile += fmt.Sprintf("%s = %s\n", req.Key, req.Value) |
|
continue |
|
} |
|
if strings.HasPrefix(line, "[") || index == len(lines)-1 { |
|
isEnd = true |
|
if !hasKey { |
|
newFile += fmt.Sprintf("%s = %s\n", req.Key, req.Value) |
|
} |
|
} |
|
newFile += line |
|
if index != len(lines)-1 { |
|
newFile += "\n" |
|
} |
|
} |
|
file, err := os.OpenFile(defaultFail2BanPath, os.O_WRONLY|os.O_TRUNC, 0640) |
|
if err != nil { |
|
return err |
|
} |
|
defer file.Close() |
|
write := bufio.NewWriter(file) |
|
_, _ = write.WriteString(newFile) |
|
write.Flush() |
|
|
|
client, err := toolbox.NewFail2Ban() |
|
if err != nil { |
|
return err |
|
} |
|
if err := client.Operate("restart"); err != nil { |
|
return err |
|
} |
|
return nil |
|
} |
|
|
|
func (u *Fail2BanService) UpdateConfByFile(req dto.UpdateByFile) error { |
|
file, err := os.OpenFile(defaultFail2BanPath, os.O_WRONLY|os.O_TRUNC, 0640) |
|
if err != nil { |
|
return err |
|
} |
|
defer file.Close() |
|
write := bufio.NewWriter(file) |
|
_, _ = write.WriteString(req.File) |
|
write.Flush() |
|
|
|
client, err := toolbox.NewFail2Ban() |
|
if err != nil { |
|
return err |
|
} |
|
if err := client.Operate("restart"); err != nil { |
|
return err |
|
} |
|
return nil |
|
} |
|
|
|
func (u *Fail2BanService) OperateSSHD(req dto.Fail2BanSet) error { |
|
if req.Operate == "ignore" { |
|
if err := u.UpdateConf(dto.Fail2BanUpdate{Key: "ignoreip", Value: strings.Join(req.IPs, ",")}); err != nil { |
|
return err |
|
} |
|
return nil |
|
} |
|
client, err := toolbox.NewFail2Ban() |
|
if err != nil { |
|
return err |
|
} |
|
if err := client.ReBanIPs(req.IPs); err != nil { |
|
return err |
|
} |
|
return nil |
|
} |
|
|
|
func loadFailValue(line string, baseInfo *dto.Fail2BanBaseInfo) { |
|
if strings.HasPrefix(line, "port") { |
|
itemValue := strings.ReplaceAll(line, "port", "") |
|
itemValue = strings.ReplaceAll(itemValue, "=", "") |
|
baseInfo.Port, _ = strconv.Atoi(strings.TrimSpace(itemValue)) |
|
} |
|
if strings.HasPrefix(line, "maxretry") { |
|
itemValue := strings.ReplaceAll(line, "maxretry", "") |
|
itemValue = strings.ReplaceAll(itemValue, "=", "") |
|
baseInfo.MaxRetry, _ = strconv.Atoi(strings.TrimSpace(itemValue)) |
|
} |
|
if strings.HasPrefix(line, "findtime") { |
|
itemValue := strings.ReplaceAll(line, "findtime", "") |
|
itemValue = strings.ReplaceAll(itemValue, "=", "") |
|
baseInfo.FindTime = strings.TrimSpace(itemValue) |
|
} |
|
if strings.HasPrefix(line, "bantime") { |
|
itemValue := strings.ReplaceAll(line, "bantime", "") |
|
itemValue = strings.ReplaceAll(itemValue, "=", "") |
|
baseInfo.BanTime = strings.TrimSpace(itemValue) |
|
} |
|
if strings.HasPrefix(line, "banaction") { |
|
itemValue := strings.ReplaceAll(line, "banaction", "") |
|
itemValue = strings.ReplaceAll(itemValue, "=", "") |
|
baseInfo.BanAction = strings.TrimSpace(itemValue) |
|
} |
|
if strings.HasPrefix(line, "logpath") { |
|
itemValue := strings.ReplaceAll(line, "logpath", "") |
|
itemValue = strings.ReplaceAll(itemValue, "=", "") |
|
baseInfo.LogPath = strings.TrimSpace(itemValue) |
|
} |
|
}
|
|
|