mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 14:57:16 +08:00
feat: fail2ban plugin
This commit is contained in:
459
app/http/controllers/plugins/fail2ban/fail2ban_controller.go
Normal file
459
app/http/controllers/plugins/fail2ban/fail2ban_controller.go
Normal file
@@ -0,0 +1,459 @@
|
||||
package fail2ban
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/spf13/cast"
|
||||
"panel/app/models"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/services"
|
||||
"panel/pkg/tools"
|
||||
)
|
||||
|
||||
type Fail2banController struct {
|
||||
website services.Website
|
||||
}
|
||||
|
||||
func NewFail2banController() *Fail2banController {
|
||||
return &Fail2banController{
|
||||
website: services.NewWebsiteImpl(),
|
||||
}
|
||||
}
|
||||
|
||||
type Jail struct {
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
LogPath string `json:"log_path"`
|
||||
MaxRetry int `json:"max_retry"`
|
||||
FindTime int `json:"find_time"`
|
||||
BanTime int `json:"ban_time"`
|
||||
}
|
||||
|
||||
// Status 获取运行状态
|
||||
func (c *Fail2banController) Status(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
|
||||
return
|
||||
}
|
||||
|
||||
if status == "active" {
|
||||
controllers.Success(ctx, true)
|
||||
} else {
|
||||
controllers.Success(ctx, false)
|
||||
}
|
||||
}
|
||||
|
||||
// Reload 重载配置
|
||||
func (c *Fail2banController) Reload(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl reload fail2ban")
|
||||
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
|
||||
return
|
||||
}
|
||||
|
||||
if status == "active" {
|
||||
controllers.Success(ctx, true)
|
||||
} else {
|
||||
controllers.Success(ctx, false)
|
||||
}
|
||||
}
|
||||
|
||||
// Restart 重启服务
|
||||
func (c *Fail2banController) Restart(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl restart fail2ban")
|
||||
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
|
||||
return
|
||||
}
|
||||
|
||||
if status == "active" {
|
||||
controllers.Success(ctx, true)
|
||||
} else {
|
||||
controllers.Success(ctx, false)
|
||||
}
|
||||
}
|
||||
|
||||
// Start 启动服务
|
||||
func (c *Fail2banController) Start(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl start fail2ban")
|
||||
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
|
||||
return
|
||||
}
|
||||
|
||||
if status == "active" {
|
||||
controllers.Success(ctx, true)
|
||||
} else {
|
||||
controllers.Success(ctx, false)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop 停止服务
|
||||
func (c *Fail2banController) Stop(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl stop fail2ban")
|
||||
status := tools.ExecShell("systemctl status fail2ban | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取服务运行状态失败")
|
||||
return
|
||||
}
|
||||
|
||||
if status != "active" {
|
||||
controllers.Success(ctx, true)
|
||||
} else {
|
||||
controllers.Success(ctx, false)
|
||||
}
|
||||
}
|
||||
|
||||
// List 所有 Fail2ban 规则
|
||||
func (c *Fail2banController) List(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
page := ctx.Request().QueryInt("page", 1)
|
||||
limit := ctx.Request().QueryInt("limit", 10)
|
||||
raw := tools.ReadFile("/etc/fail2ban/jail.local")
|
||||
if len(raw) == 0 {
|
||||
controllers.Error(ctx, http.StatusBadRequest, "Fail2ban 规则为空")
|
||||
return
|
||||
}
|
||||
|
||||
jailList := regexp.MustCompile(`\[(.*?)]`).FindAllStringSubmatch(raw, -1)
|
||||
if len(jailList) == 0 {
|
||||
controllers.Error(ctx, http.StatusBadRequest, "Fail2ban 规则为空")
|
||||
return
|
||||
}
|
||||
|
||||
var jails []Jail
|
||||
for i, jail := range jailList {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
jailName := jail[1]
|
||||
jailRaw := tools.Cut(raw, "# "+jailName+"-START", "# "+jailName+"-END")
|
||||
if len(jailRaw) == 0 {
|
||||
continue
|
||||
}
|
||||
jailEnabled := strings.Contains(jailRaw, "enabled = true")
|
||||
jailLogPath := regexp.MustCompile(`logpath = (.*)`).FindStringSubmatch(jailRaw)
|
||||
jailMaxRetry := regexp.MustCompile(`maxretry = (.*)`).FindStringSubmatch(jailRaw)
|
||||
jailFindTime := regexp.MustCompile(`findtime = (.*)`).FindStringSubmatch(jailRaw)
|
||||
jailBanTime := regexp.MustCompile(`bantime = (.*)`).FindStringSubmatch(jailRaw)
|
||||
|
||||
jails = append(jails, Jail{
|
||||
Name: jailName,
|
||||
Enabled: jailEnabled,
|
||||
LogPath: jailLogPath[1],
|
||||
MaxRetry: cast.ToInt(jailMaxRetry[1]),
|
||||
FindTime: cast.ToInt(jailFindTime[1]),
|
||||
BanTime: cast.ToInt(jailBanTime[1]),
|
||||
})
|
||||
}
|
||||
|
||||
startIndex := (page - 1) * limit
|
||||
endIndex := page * limit
|
||||
if startIndex > len(jails) {
|
||||
controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
"items": []Jail{},
|
||||
})
|
||||
return
|
||||
}
|
||||
if endIndex > len(jails) {
|
||||
endIndex = len(jails)
|
||||
}
|
||||
pagedJails := jails[startIndex:endIndex]
|
||||
|
||||
controllers.Success(ctx, http.Json{
|
||||
"total": len(jails),
|
||||
"items": pagedJails,
|
||||
})
|
||||
}
|
||||
|
||||
// Add 添加 Fail2ban 规则
|
||||
func (c *Fail2banController) Add(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"name": "required",
|
||||
"type": "required|in:website,service",
|
||||
"maxretry": "required",
|
||||
"findtime": "required",
|
||||
"bantime": "required",
|
||||
"website_mode": "required_if:type,website",
|
||||
"website_path": "required_if:type,website",
|
||||
})
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
jailName := ctx.Request().Input("name")
|
||||
jailType := ctx.Request().Input("type")
|
||||
jailMaxRetry := ctx.Request().Input("maxretry")
|
||||
jailFindTime := ctx.Request().Input("findtime")
|
||||
jailBanTime := ctx.Request().Input("bantime")
|
||||
jailWebsiteMode := ctx.Request().Input("website_mode")
|
||||
jailWebsitePath := ctx.Request().Input("website_path")
|
||||
|
||||
raw := tools.ReadFile("/etc/fail2ban/jail.local")
|
||||
if strings.Contains(raw, "["+jailName+"]") || (strings.Contains(raw, "["+jailName+"]"+"-cc") && jailWebsiteMode == "cc") || (strings.Contains(raw, "["+jailName+"]"+"-path") && jailWebsiteMode == "path") {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "规则已存在")
|
||||
return
|
||||
}
|
||||
|
||||
switch jailType {
|
||||
case "website":
|
||||
var website models.Website
|
||||
err := facades.Orm().Query().Where("name", jailName).FirstOrFail(&website)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "网站不存在")
|
||||
return
|
||||
}
|
||||
config, err := c.website.GetConfig(int(website.ID))
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "获取网站配置失败")
|
||||
return
|
||||
}
|
||||
var ports string
|
||||
for _, port := range config.Ports {
|
||||
if len(strings.Split(port, " ")) > 1 {
|
||||
ports += strings.Split(port, " ")[0] + ","
|
||||
} else {
|
||||
ports += port + ","
|
||||
}
|
||||
}
|
||||
|
||||
rule := `
|
||||
# ` + jailName + `-` + jailWebsiteMode + `-START
|
||||
[` + jailName + `-` + jailWebsiteMode + `]
|
||||
enabled = true
|
||||
filter = haozi-` + jailName + `-` + jailWebsiteMode + `
|
||||
port = ` + ports + `
|
||||
maxretry = ` + jailMaxRetry + `
|
||||
findtime = ` + jailFindTime + `
|
||||
bantime = ` + jailBanTime + `
|
||||
action = %(action_mwl)s
|
||||
logpath = /www/wwwlogs/` + website.Name + `.log
|
||||
# ` + jailName + `-` + jailWebsiteMode + `-END
|
||||
`
|
||||
raw += rule
|
||||
tools.WriteFile("/etc/fail2ban/jail.local", raw, 0644)
|
||||
|
||||
var filter string
|
||||
if jailWebsiteMode == "cc" {
|
||||
filter = `
|
||||
[Definition]
|
||||
failregex = ^<HOST>\s-.*HTTP/.*$
|
||||
ignoreregex =
|
||||
`
|
||||
} else {
|
||||
filter = `
|
||||
[Definition]
|
||||
failregex = ^<HOST>\s-.*\s` + jailWebsitePath + `.*HTTP/.*$
|
||||
ignoreregex =
|
||||
`
|
||||
}
|
||||
tools.WriteFile("/etc/fail2ban/filter.d/haozi-"+jailName+"-"+jailWebsiteMode+".conf", filter, 0644)
|
||||
|
||||
case "service":
|
||||
var logPath string
|
||||
var filter string
|
||||
var port string
|
||||
switch jailName {
|
||||
case "ssh":
|
||||
if tools.IsDebian() {
|
||||
logPath = "/var/log/auth.log"
|
||||
} else {
|
||||
logPath = "/var/log/secure"
|
||||
}
|
||||
filter = "sshd"
|
||||
port = tools.ExecShell("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
|
||||
case "mysql":
|
||||
logPath = "/www/server/mysql/mysql-error.log"
|
||||
filter = "mysqld-auth"
|
||||
port = tools.ExecShell("cat /www/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'")
|
||||
case "pure-ftpd":
|
||||
logPath = "/var/log/messages"
|
||||
filter = "pure-ftpd"
|
||||
port = tools.ExecShell(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
|
||||
default:
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "未知服务")
|
||||
return
|
||||
}
|
||||
if len(port) == 0 {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "获取服务端口失败,请检查是否安装")
|
||||
return
|
||||
}
|
||||
|
||||
rule := `
|
||||
# ` + jailName + `-START
|
||||
[` + jailName + `]
|
||||
enabled = true
|
||||
filter = ` + filter + `
|
||||
port = ` + port + `
|
||||
maxretry = ` + jailMaxRetry + `
|
||||
findtime = ` + jailFindTime + `
|
||||
bantime = ` + jailBanTime + `
|
||||
action = %(action_mwl)s
|
||||
logpath = ` + logPath + `
|
||||
# ` + jailName + `-END
|
||||
`
|
||||
raw += rule
|
||||
tools.WriteFile("/etc/fail2ban/jail.local", raw, 0644)
|
||||
}
|
||||
|
||||
tools.ExecShell("fail2ban-client reload")
|
||||
controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// Delete 删除规则
|
||||
func (c *Fail2banController) Delete(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
jailName := ctx.Request().Input("name")
|
||||
raw := tools.ReadFile("/etc/fail2ban/jail.local")
|
||||
if !strings.Contains(raw, "["+jailName+"]") {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "规则不存在")
|
||||
return
|
||||
}
|
||||
|
||||
rule := tools.Cut(raw, "# "+jailName+"-START", "# "+jailName+"-END")
|
||||
raw = strings.Replace(raw, "\n# "+jailName+"-START"+rule+"# "+jailName+"-END", "", -1)
|
||||
raw = strings.TrimSpace(raw)
|
||||
tools.WriteFile("/etc/fail2ban/jail.local", raw, 0644)
|
||||
|
||||
tools.ExecShell("fail2ban-client reload")
|
||||
controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// BanList 获取封禁列表
|
||||
func (c *Fail2banController) BanList(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Request().Query("name")
|
||||
if len(name) == 0 {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
|
||||
return
|
||||
}
|
||||
|
||||
currentlyBan := tools.ExecShell(`fail2ban-client status ` + name + ` | grep "Currently banned" | awk '{print $4}'`)
|
||||
totalBan := tools.ExecShell(`fail2ban-client status ` + name + ` | grep "Total banned" | awk '{print $4}'`)
|
||||
bannedIp := tools.ExecShell(`fail2ban-client status ` + name + ` | grep "Banned IP list" | awk -F ":" '{print $2}'`)
|
||||
bannedIpList := strings.Split(bannedIp, " ")
|
||||
|
||||
var list []map[string]string
|
||||
for _, ip := range bannedIpList {
|
||||
if len(ip) > 0 {
|
||||
list = append(list, map[string]string{
|
||||
"name": name,
|
||||
"ip": ip,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
controllers.Success(ctx, http.Json{
|
||||
"currentlyBan": currentlyBan,
|
||||
"totalBan": totalBan,
|
||||
"bannedIpList": list,
|
||||
})
|
||||
}
|
||||
|
||||
// Unban 解封
|
||||
func (c *Fail2banController) Unban(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Request().Input("name")
|
||||
ip := ctx.Request().Input("ip")
|
||||
if len(name) == 0 || len(ip) == 0 {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("fail2ban-client set " + name + " unbanip " + ip)
|
||||
controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// SetWhiteList 设置白名单
|
||||
func (c *Fail2banController) SetWhiteList(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
ip := ctx.Request().Input("ip")
|
||||
if len(ip) == 0 {
|
||||
controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
|
||||
return
|
||||
}
|
||||
|
||||
raw := tools.ReadFile("/etc/fail2ban/jail.local")
|
||||
// 正则替换
|
||||
reg := regexp.MustCompile(`ignoreip\s*=\s*.*\n`)
|
||||
if reg.MatchString(raw) {
|
||||
raw = reg.ReplaceAllString(raw, "ignoreip = "+ip+"\n")
|
||||
} else {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
|
||||
return
|
||||
}
|
||||
|
||||
tools.WriteFile("/etc/fail2ban/jail.local", raw, 0644)
|
||||
tools.ExecShell("fail2ban-client reload")
|
||||
controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// GetWhiteList 获取白名单
|
||||
func (c *Fail2banController) GetWhiteList(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "fail2ban") {
|
||||
return
|
||||
}
|
||||
|
||||
raw := tools.ReadFile("/etc/fail2ban/jail.local")
|
||||
reg := regexp.MustCompile(`ignoreip\s*=\s*(.*)\n`)
|
||||
if reg.MatchString(raw) {
|
||||
ignoreIp := reg.FindStringSubmatch(raw)[1]
|
||||
controllers.Success(ctx, ignoreIp)
|
||||
} else {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
|
||||
}
|
||||
}
|
||||
13
app/plugins/fail2ban/fail2ban.go
Normal file
13
app/plugins/fail2ban/fail2ban.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package fail2ban
|
||||
|
||||
var (
|
||||
Name = "Fail2ban"
|
||||
Description = "Fail2ban 扫描系统日志文件并从中找出多次尝试失败的IP地址,将该IP地址加入防火墙的拒绝访问列表中。"
|
||||
Slug = "fail2ban"
|
||||
Version = "1.0.0"
|
||||
Requires = []string{}
|
||||
Excludes = []string{}
|
||||
Install = `bash /www/panel/scripts/fail2ban/install.sh`
|
||||
Uninstall = `bash /www/panel/scripts/fail2ban/uninstall.sh`
|
||||
Update = `bash /www/panel/scripts/fail2ban/update.sh`
|
||||
)
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/goravel/framework/facades"
|
||||
|
||||
"panel/app/models"
|
||||
"panel/app/plugins/fail2ban"
|
||||
"panel/app/plugins/mysql57"
|
||||
"panel/app/plugins/mysql80"
|
||||
"panel/app/plugins/openresty"
|
||||
@@ -168,6 +169,17 @@ func (r *PluginImpl) All() []PanelPlugin {
|
||||
Uninstall: supervisor.Uninstall,
|
||||
Update: supervisor.Update,
|
||||
})
|
||||
p = append(p, PanelPlugin{
|
||||
Name: fail2ban.Name,
|
||||
Description: fail2ban.Description,
|
||||
Slug: fail2ban.Slug,
|
||||
Version: fail2ban.Version,
|
||||
Requires: fail2ban.Requires,
|
||||
Excludes: fail2ban.Excludes,
|
||||
Install: fail2ban.Install,
|
||||
Uninstall: fail2ban.Uninstall,
|
||||
Update: fail2ban.Update,
|
||||
})
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
335
public/panel/views/plugins/fail2ban.html
Normal file
335
public/panel/views/plugins/fail2ban.html
Normal file
@@ -0,0 +1,335 @@
|
||||
<!--
|
||||
Name: Fail2ban管理器
|
||||
Author: 耗子
|
||||
Date: 2023-07-30
|
||||
-->
|
||||
<title>Fail2ban</title>
|
||||
<div class="layui-fluid" id="component-tabs">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">Fail2ban 运行状态</div>
|
||||
<div class="layui-card-body">
|
||||
<blockquote id="fail2ban-status" class="layui-elem-quote layui-quote-nm">当前状态:<span
|
||||
class="layui-badge layui-bg-black">获取中</span></blockquote>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="fail2ban-start" class="layui-btn">启动</button>
|
||||
<button id="fail2ban-stop" class="layui-btn layui-btn-danger">停止</button>
|
||||
<button id="fail2ban-restart" class="layui-btn layui-btn-warm">重启</button>
|
||||
<button id="fail2ban-reload" class="layui-btn layui-btn-normal">重载</button>
|
||||
</div>
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>基本设置</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form" lay-filter="fail2ban_setting">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" style="font-size: 13px;">IP白名单</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="fail2ban_white_list" value="获取中ing..." class="layui-input"
|
||||
disabled>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">IP白名单,以英文逗号,分隔</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn layui-btn-sm" lay-submit lay-filter="fail2ban_setting_submit">
|
||||
确认修改
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">Fail2ban 规则列表</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="fail2ban-rule-list" lay-filter="fail2ban-rule-list"></table>
|
||||
<!-- 顶部工具栏 -->
|
||||
<script type="text/html" id="fail2ban-rule-list-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="add_rule">新建规则</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 右侧管理 -->
|
||||
<script type="text/html" id="fail2ban-rule-list-control">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="view">查看</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['index', 'form', 'table', 'view'], function () {
|
||||
let $ = layui.$
|
||||
, admin = layui.admin
|
||||
, table = layui.table
|
||||
, form = layui.form
|
||||
, view = layui.view;
|
||||
|
||||
// 获取运行状态并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/status"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
if (result.data) {
|
||||
$('#fail2ban-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>');
|
||||
} else {
|
||||
$('#fail2ban-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 获取白名单并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/whiteList"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
$('input[name=fail2ban_white_list]').val(result.data);
|
||||
$('input').attr('disabled', false);
|
||||
form.render();
|
||||
}
|
||||
});
|
||||
|
||||
// 监听提交
|
||||
form.on('submit(fail2ban_setting_submit)', function (data) {
|
||||
data.field.ip = $('input[name=fail2ban_white_list]').val();
|
||||
index = layer.msg('请稍候...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/whiteList"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
layer.msg('设置成功', {icon: 1});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
let websiteList = [];
|
||||
admin.req({
|
||||
url: '/api/panel/website/list?page=1&limit=10000'
|
||||
, type: 'get'
|
||||
, async: false
|
||||
, success: function (res) {
|
||||
websiteList = res.data.items;
|
||||
}
|
||||
});
|
||||
|
||||
// 获取规则列表
|
||||
table.render({
|
||||
elem: '#fail2ban-rule-list'
|
||||
, url: '/api/plugins/fail2ban/list'
|
||||
, toolbar: '#fail2ban-rule-list-bar'
|
||||
, title: 'Fail2ban 规则列表'
|
||||
, cols: [[
|
||||
{field: 'name', title: '规则名', fixed: 'left', unresize: true, sort: true}
|
||||
, {
|
||||
field: 'enabled', title: '状态', templet: function (d) {
|
||||
if (d.enabled) {
|
||||
return '<span class="layui-badge layui-bg-green">启用</span>';
|
||||
} else {
|
||||
return '<span class="layui-badge layui-bg-gray">禁用</span>';
|
||||
}
|
||||
}, sort: true
|
||||
}
|
||||
, {field: 'max_retry', title: '最大尝试次数', sort: true}
|
||||
, {field: 'ban_time', title: '封禁时间', sort: true}
|
||||
, {field: 'find_time', title: '查找时间', sort: true}
|
||||
, {field: 'log_path', title: '日志路径'}
|
||||
, {fixed: 'right', title: '操作', toolbar: '#fail2ban-rule-list-control', width: 150}
|
||||
]]
|
||||
, page: true
|
||||
, text: {
|
||||
none: '无数据'
|
||||
}
|
||||
, parseData: function (res) {
|
||||
return {
|
||||
"code": res.code,
|
||||
"msg": res.message,
|
||||
"count": res.data.total,
|
||||
"data": res.data.items
|
||||
};
|
||||
}
|
||||
});
|
||||
// 头工具栏事件
|
||||
table.on('toolbar(fail2ban-rule-list)', function (obj) {
|
||||
if (obj.event === 'add_rule') {
|
||||
index = layer.msg('加载中...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.popup({
|
||||
title: '新建Fail2ban规则'
|
||||
, area: ['600px', '600px']
|
||||
, id: 'LAY-popup-fail2ban-rule-add'
|
||||
, success: function () {
|
||||
layer.close(index);
|
||||
view(this.id).render('plugins/fail2ban/add_rule', {
|
||||
websiteList: websiteList
|
||||
}).done(function () {
|
||||
form.render(null, 'LAY-popup-fail2ban-rule-add');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(fail2ban-rule-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定要删除 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/delete"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert(data.name + '删除成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (obj.event === 'view') {
|
||||
index = layer.msg('加载中...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/ban?name=" + data.name
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.popup({
|
||||
title: '查看Fail2ban规则'
|
||||
, area: ['600px', '600px']
|
||||
, id: 'LAY-popup-fail2ban-rule-view'
|
||||
, success: function () {
|
||||
layer.close(index);
|
||||
view(this.id).render('plugins/fail2ban/view_rule', {
|
||||
data: data
|
||||
, banList: result.data
|
||||
}).done(function () {
|
||||
form.render(null, 'LAY-popup-fail2ban-rule-view');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('#fail2ban-start').click(function () {
|
||||
layer.confirm('确定要启动Fail2ban吗?', {
|
||||
btn: ['启动', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/start"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('Fail2ban启动成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#fail2ban-stop').click(function () {
|
||||
layer.confirm('停止Fail2ban将导致Fail2ban防护失效,是否继续停止?', {
|
||||
btn: ['停止', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/stop"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('Fail2ban停止成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#fail2ban-restart').click(function () {
|
||||
layer.confirm('确定要重启Fail2ban吗?', {
|
||||
btn: ['重启', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/restart"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('Fail2ban重启成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#fail2ban-reload').click(function () {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/reload"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
layer.alert('Fail2ban重载成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
170
public/panel/views/plugins/fail2ban/add_rule.html
Normal file
170
public/panel/views/plugins/fail2ban/add_rule.html
Normal file
@@ -0,0 +1,170 @@
|
||||
<!--
|
||||
Name: Fail2ban管理器 - 新建规则
|
||||
Author: 耗子
|
||||
Date: 2023-07-30
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<form class="layui-form" action="" lay-filter="add-pure-ftpd-user-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">类型</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" lay-filter="add-fail2ban-rule-type-radio" name="type" value="website"
|
||||
title="网站" checked="">
|
||||
<input type="radio" lay-filter="add-fail2ban-rule-type-radio" name="type" value="service"
|
||||
title="服务">
|
||||
</div>
|
||||
</div>
|
||||
<div id="add-fail2ban-rule-website-input" class="layui-form-item">
|
||||
<label class="layui-form-label">网站</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="website" lay-filter="add-fail2ban-rule-website">
|
||||
{{# layui.each(d.params.websiteList, function(index, item){ }}
|
||||
{{# if(index == 0){ }}
|
||||
<option value="{{ item.name }}" selected="">{{ item.name }}</option>
|
||||
{{# }else{ }}
|
||||
<option value="{{ item.name }}">{{ item.name }}</option>
|
||||
{{# } }}
|
||||
{{# }); }}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="add-fail2ban-rule-service-input" class="layui-form-item">
|
||||
<label class="layui-form-label">服务</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="service" lay-filter="add-fail2ban-rule-service">
|
||||
<option value="ssh" selected="">ssh</option>
|
||||
<option value="mysql">mysql</option>
|
||||
<option value="pure-ftpd">pure-ftpd</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="add-fail2ban-rule-website-mode-input" class="layui-form-item">
|
||||
<label class="layui-form-label">网站模式</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" lay-filter="add-fail2ban-rule-website-mode-radio" name="website_mode" value="cc"
|
||||
title="CC" checked="">
|
||||
<input type="radio" lay-filter="add-fail2ban-rule-website-mode-radio" name="website_mode" value="path"
|
||||
title="目录">
|
||||
</div>
|
||||
</div>
|
||||
<div id="add-fail2ban-rule-website-path-input" class="layui-form-item">
|
||||
<label class="layui-form-label">网站目录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="website_path"
|
||||
lay-verify="required" placeholder="输入一个禁止访问的目录" class="layui-input"
|
||||
value="/admin">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">最大重试</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="maxretry"
|
||||
lay-verify="required" placeholder="单位:次" class="layui-input"
|
||||
value="30">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">周期</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="findtime"
|
||||
lay-verify="required" placeholder="单位:秒" class="layui-input"
|
||||
value="300">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">禁止时间</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="bantime"
|
||||
lay-verify="required" placeholder="单位:秒" class="layui-input"
|
||||
value="600">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">
|
||||
在设置周期内(秒)有超过最大重试(次)的IP访问,将禁止该IP禁止时间(秒)
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">
|
||||
<span style="color: red;">防护端口自动获取,如果修改了规则项对应的端口,请删除重新添加,否则防护可能不会生效</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-footer">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="add-fail2ban-rule-submit">立即提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form', 'jquery', 'table'], function () {
|
||||
var $ = layui.jquery
|
||||
, admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, form = layui.form
|
||||
, table = layui.table;
|
||||
|
||||
form.render();
|
||||
|
||||
// 监听类型选择
|
||||
$('#add-fail2ban-rule-service-input').hide();
|
||||
form.on('radio(add-fail2ban-rule-type-radio)', function (data) {
|
||||
if (data.value == 'website') {
|
||||
$('#add-fail2ban-rule-website-input').show();
|
||||
$('#add-fail2ban-rule-website-mode-input').show();
|
||||
$('#add-fail2ban-rule-service-input').hide();
|
||||
if ($('input[name="website_mode"]:checked').val() == 'cc') {
|
||||
$('#add-fail2ban-rule-website-path-input').hide();
|
||||
} else {
|
||||
$('#add-fail2ban-rule-website-path-input').show();
|
||||
}
|
||||
} else if (data.value == 'service') {
|
||||
$('#add-fail2ban-rule-website-input').hide();
|
||||
$('#add-fail2ban-rule-website-mode-input').hide();
|
||||
$('#add-fail2ban-rule-service-input').show();
|
||||
$('#add-fail2ban-rule-website-path-input').hide();
|
||||
}
|
||||
});
|
||||
// 监听网站模式选择
|
||||
$('#add-fail2ban-rule-website-path-input').hide();
|
||||
form.on('radio(add-fail2ban-rule-website-mode-radio)', function (data) {
|
||||
if (data.value == 'cc') {
|
||||
$('#add-fail2ban-rule-website-path-input').hide();
|
||||
} else if (data.value == 'path') {
|
||||
$('#add-fail2ban-rule-website-path-input').show();
|
||||
}
|
||||
});
|
||||
|
||||
// 提交
|
||||
form.on('submit(add-fail2ban-rule-submit)', function (data) {
|
||||
if (data.field.type === 'website') {
|
||||
data.field.name = data.field.website;
|
||||
} else if (data.field.type === 'service') {
|
||||
data.field.name = data.field.service;
|
||||
}
|
||||
index = layer.msg('提交中...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/add"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
table.reload('fail2ban-rule-list');
|
||||
layer.alert('Fail2ban规则添加成功!', {
|
||||
icon: 1
|
||||
, title: '提示'
|
||||
, btn: ['确定']
|
||||
, yes: function (index) {
|
||||
layer.closeAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
101
public/panel/views/plugins/fail2ban/view_rule.html
Normal file
101
public/panel/views/plugins/fail2ban/view_rule.html
Normal file
@@ -0,0 +1,101 @@
|
||||
<!--
|
||||
Name: Fail2ban管理器 - 查看规则
|
||||
Author: 耗子
|
||||
Date: 2023-07-30
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
{{# console.log(d.params) }}
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-row layui-col-space15">
|
||||
<div class="layui-col-sm6 layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body layuiadmin-card-list">
|
||||
<p class="layuiadmin-big-font">{{ d.params.banList.currentlyBan }}</p>
|
||||
<p>当前封禁IP数</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-sm6 layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body layuiadmin-card-list">
|
||||
<p class="layuiadmin-big-font">{{ d.params.banList.totalBan }}</p>
|
||||
<p>累计封禁IP数</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-row layui-col-space15">
|
||||
<div class="layui-col-sm12 layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body layuiadmin-card-list">
|
||||
<table class="layui-table" id="fail2ban-view-rule-table"
|
||||
lay-filter="fail2ban-view-rule-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/html" id="fail2ban-view-rule-ip-control">
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="unBan">解封</a>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
if (params.banList.bannedIpList === null) {
|
||||
params.banList.bannedIpList = [];
|
||||
}
|
||||
|
||||
layui.use(['admin', 'jquery'], function () {
|
||||
var admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, table = layui.table;
|
||||
|
||||
table.render({
|
||||
elem: '#fail2ban-view-rule-table'
|
||||
, cols: [[
|
||||
{field: 'name', title: '规则名', unresize: true, hide: true}
|
||||
, {field: 'ip', title: 'IP', unresize: true, sort: true}
|
||||
, {
|
||||
fixed: 'right',
|
||||
title: '操作',
|
||||
width: 150,
|
||||
unresize: true,
|
||||
toolbar: '#fail2ban-view-rule-ip-control'
|
||||
}
|
||||
]]
|
||||
, data: params.banList.bannedIpList
|
||||
, page: true
|
||||
, text: {
|
||||
none: '无数据'
|
||||
}
|
||||
});
|
||||
|
||||
// 监听工具条
|
||||
table.on('tool(fail2ban-view-rule-table)', function (obj) {
|
||||
var data = obj.data;
|
||||
if (obj.event === 'unBan') {
|
||||
layer.confirm('确定要解封 <b style="color: red;">' + data.ip + '</b> 吗?', function (index) {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/fail2ban/unban"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert(data.ip + '解封成功!');
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
@@ -3,6 +3,7 @@ package routes
|
||||
import (
|
||||
"github.com/goravel/framework/contracts/route"
|
||||
"github.com/goravel/framework/facades"
|
||||
"panel/app/http/controllers/plugins/fail2ban"
|
||||
|
||||
"panel/app/http/controllers/plugins/mysql57"
|
||||
"panel/app/http/controllers/plugins/mysql80"
|
||||
@@ -198,4 +199,19 @@ func Plugin() {
|
||||
route.Post("addProcess", supervisorController.AddProcess)
|
||||
|
||||
})
|
||||
facades.Route().Prefix("api/plugins/fail2ban").Middleware(middleware.Jwt()).Group(func(route route.Route) {
|
||||
fail2banController := fail2ban.NewFail2banController()
|
||||
route.Get("status", fail2banController.Status)
|
||||
route.Post("start", fail2banController.Start)
|
||||
route.Post("stop", fail2banController.Stop)
|
||||
route.Post("restart", fail2banController.Restart)
|
||||
route.Post("reload", fail2banController.Reload)
|
||||
route.Get("list", fail2banController.List)
|
||||
route.Post("add", fail2banController.Add)
|
||||
route.Post("delete", fail2banController.Delete)
|
||||
route.Get("ban", fail2banController.BanList)
|
||||
route.Post("unban", fail2banController.Unban)
|
||||
route.Post("whiteList", fail2banController.SetWhiteList)
|
||||
route.Get("whiteList", fail2banController.GetWhiteList)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ fi
|
||||
# 修改 fail2ban 配置文件
|
||||
sed -i 's!# logtarget.*!logtarget = /var/log/fail2ban.log!' /etc/fail2ban/fail2ban.conf
|
||||
sed -i 's!logtarget\s*=.*!logtarget = /var/log/fail2ban.log!' /etc/fail2ban/jail.conf
|
||||
cat >/etc/fail2ban/jail.local <<EOF
|
||||
cat > /etc/fail2ban/jail.local << EOF
|
||||
[DEFAULT]
|
||||
ignoreip = 127.0.0.1/8
|
||||
bantime = 600
|
||||
@@ -78,8 +78,8 @@ if [ "${sshPort}" == "" ]; then
|
||||
sshPort="22"
|
||||
fi
|
||||
sed -i "s/port = 22/port = ${sshPort}/g" /etc/fail2ban/jail.local
|
||||
if [ -f "/etc/pure-ftpd/pure-ftpd.conf" ]; then
|
||||
ftpPort=$(cat /etc/pure-ftpd/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}')
|
||||
if [ -f "/www/server/pure-ftpd/etc/pure-ftpd.conf" ]; then
|
||||
ftpPort=$(cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}')
|
||||
fi
|
||||
if [ "${ftpPort}" == "" ]; then
|
||||
ftpPort="21"
|
||||
@@ -87,10 +87,17 @@ if [ "${ftpPort}" == "" ]; then
|
||||
else
|
||||
sed -i "s/port = 21/port = ${ftpPort}/g" /etc/fail2ban/jail.local
|
||||
fi
|
||||
|
||||
# Debian 的特殊处理
|
||||
if [ "${OS}" == "debian" ]; then
|
||||
sed -i "s/\/var\/log\/secure/\/var\/log\/auth.log/g" /etc/fail2ban/jail.local
|
||||
sed -i "s/banaction = firewallcmd-ipset/banaction = ufw/g" /etc/fail2ban/jail.local
|
||||
fi
|
||||
|
||||
# 启动 fail2ban
|
||||
systemctl unmask fail2ban
|
||||
systemctl daemon-reload
|
||||
systemctl enable fail2ban
|
||||
systemctl restart fail2ban
|
||||
|
||||
panel writePlugin fail2ban
|
||||
panel writePlugin fail2ban 1.0.0
|
||||
|
||||
@@ -20,6 +20,8 @@ limitations under the License.
|
||||
HR="+----------------------------------------------------"
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
|
||||
fail2ban-client unban --all
|
||||
fail2ban-client stop
|
||||
systemctl stop fail2ban
|
||||
systemctl disable fail2ban
|
||||
|
||||
|
||||
37
scripts/fail2ban/update.sh
Normal file
37
scripts/fail2ban/update.sh
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
|
||||
|
||||
: '
|
||||
Copyright 2022 HaoZi Technology Co., Ltd.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
'
|
||||
|
||||
HR="+----------------------------------------------------"
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
|
||||
if [ "${OS}" == "centos" ]; then
|
||||
dnf install -y fail2ban
|
||||
elif [ "${OS}" == "debian" ]; then
|
||||
apt install -y fail2ban
|
||||
else
|
||||
echo -e $HR
|
||||
echo "错误:不支持的操作系统"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
echo -e $HR
|
||||
echo "错误:fail2ban安装失败,请截图错误信息寻求帮助。"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user