mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-24 11:09:16 +08:00
feat: 优化 Fail2ban 禁用方式错误返回 (#3227)
This commit is contained in:
parent
bd2003c1b6
commit
504d5f8596
@ -8,6 +8,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/toolbox"
|
||||
)
|
||||
|
||||
@ -97,6 +99,25 @@ func (u *Fail2BanService) Operate(operation string) error {
|
||||
}
|
||||
|
||||
func (u *Fail2BanService) UpdateConf(req dto.Fail2BanUpdate) error {
|
||||
if req.Key == "banaction" {
|
||||
switch req.Value {
|
||||
case "firewallcmd-ipset":
|
||||
isActive, _ := systemctl.IsActive("firewalld")
|
||||
if !isActive {
|
||||
return buserr.WithName("ErrBanAction", "firewalld")
|
||||
}
|
||||
case "ufw":
|
||||
isActive, _ := systemctl.IsActive("ufw")
|
||||
if !isActive {
|
||||
return buserr.WithName("ErrBanAction", "ufw")
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
@ -160,3 +160,4 @@ ErrBashExecute: "Script execution error, please check the specific information i
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "The current user does not exist. Please modify and retry!"
|
||||
ErrBanAction: "Setting failed, the current {{ .name }} service is unavailable, please check and try again!"
|
||||
|
@ -161,3 +161,4 @@ ErrBashExecute: "腳本執行錯誤,請在任務輸出文本域中查看具體
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "當前使用者不存在,請修改後重試!"
|
||||
ErrBanAction: "設置失敗,當前 {{ .name }} 服務不可用,請檢查後重試!"
|
||||
|
@ -159,4 +159,5 @@ ErrFirewall: "当前未检测到系统 firewalld 或 ufw 服务,请检查后
|
||||
ErrBashExecute: "脚本执行错误,请在任务输出文本域中查看具体信息。"
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "当前用户不存在,请修改后重试!"
|
||||
ErrNotExistUser: "当前用户不存在,请修改后重试!"
|
||||
ErrBanAction: "设置失败,当前 {{ .name }} 服务不可用,请检查后重试!"
|
@ -1,7 +1,6 @@
|
||||
package toolbox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
@ -98,10 +97,11 @@ func (f *Fail2ban) ListBanned() ([]string, error) {
|
||||
if err != nil {
|
||||
return lists, err
|
||||
}
|
||||
stdout = strings.ReplaceAll(stdout, "\n", "")
|
||||
stdout = strings.ReplaceAll(stdout, "'", "\"")
|
||||
if err := json.Unmarshal([]byte(stdout), &lists); err != nil {
|
||||
return lists, fmt.Errorf("handle json unmarshal (%s) failed, err: %v", stdout, err)
|
||||
itemList := strings.Split(stdout, "\n")
|
||||
for _, item := range itemList {
|
||||
if len(item) != 0 {
|
||||
lists = append(lists, item)
|
||||
}
|
||||
}
|
||||
return lists, nil
|
||||
}
|
||||
@ -146,6 +146,7 @@ port = 22
|
||||
maxretry = 5
|
||||
findtime = 300
|
||||
bantime = 600
|
||||
banaction = $banaction
|
||||
action = %(action_mwl)s
|
||||
logpath = $logpath`
|
||||
|
||||
|
@ -950,6 +950,8 @@ const message = {
|
||||
dnsTestFailed: 'DNS configuration information is not available. Please modify and try again!',
|
||||
},
|
||||
fail2ban: {
|
||||
sshPort: 'Listen to SSH Port',
|
||||
sshPortHelper: 'Current Fail2ban listens to the SSH connection port of the host',
|
||||
noFail2ban: 'Fail2ban service not detected, please refer to the official documentation for installation',
|
||||
unActive: 'The Fail2ban service is not enabled at present, please enable it first!',
|
||||
operation: 'Perform [{0}] operation on Fail2ban service, continue?',
|
||||
@ -959,6 +961,8 @@ const message = {
|
||||
maxRetry: 'Maximum Retry Attempts',
|
||||
banTime: 'Ban Time',
|
||||
banTimeHelper: 'Default ban time is 10 minutes, -1 indicates permanent ban',
|
||||
banTimeRule: 'Please enter a valid ban time or -1',
|
||||
banAllTime: 'Permanent Ban',
|
||||
findTime: 'Discovery Period',
|
||||
banAction: 'Ban Action',
|
||||
banActionOption: 'Ban specified IP addresses using {0}',
|
||||
|
@ -901,12 +901,16 @@ const message = {
|
||||
dnsTestFailed: 'DNS 配置信息不可用,請修改後重試!',
|
||||
},
|
||||
fail2ban: {
|
||||
sshPort: '監聽 SSH 端口',
|
||||
sshPortHelper: '當前 Fail2ban 監聽主機 SSH 連接端口',
|
||||
noFail2ban: '未檢測到 Fail2ban 服務,請參考官方文檔進行安裝',
|
||||
unActive: '當前未開啟 Fail2ban 服務,請先開啟!',
|
||||
operation: '對 Fail2ban 服務進行 [{0}] 操作,是否繼續?',
|
||||
fail2banChange: 'Fail2ban 配置修改',
|
||||
ignoreHelper: '白名單中的 IP 列表將被忽略屏蔽,是否繼續?',
|
||||
bannedHelper: '黑名單中的 IP 列表將被伺服器屏蔽,是否繼續?',
|
||||
banTimeRule: '請輸入正確的禁用時間或 -1',
|
||||
banAllTime: '永久禁用',
|
||||
maxRetry: '最大重試次數',
|
||||
banTime: '禁用時間',
|
||||
banTimeHelper: '默認禁用時間為 10 分鐘,禁用時間為 -1 則表示永久禁用',
|
||||
|
@ -902,6 +902,8 @@ const message = {
|
||||
dnsTestFailed: 'DNS 配置信息不可用,请修改后重试!',
|
||||
},
|
||||
fail2ban: {
|
||||
sshPort: '监听 SSH 端口',
|
||||
sshPortHelper: '当前 Fail2ban 监听主机 SSH 连接端口',
|
||||
noFail2ban: '未检测到 Fail2ban 服务,请参考官方文档进行安装',
|
||||
unActive: '当前未开启 Fail2ban 服务,请先开启!',
|
||||
operation: '对 Fail2ban 服务进行 [{0}] 操作,是否继续?',
|
||||
@ -911,6 +913,8 @@ const message = {
|
||||
maxRetry: '最大重试次数',
|
||||
banTime: '禁用时间',
|
||||
banTimeHelper: '默认禁用时间为 10 分钟,禁用时间为 -1 则表示永久禁用',
|
||||
banTimeRule: '请输入正确的禁用时间或者 -1',
|
||||
banAllTime: '永久禁用',
|
||||
findTime: '发现周期',
|
||||
banAction: '禁用方式',
|
||||
banActionOption: '通过 {0} 来禁用指定的 IP 地址',
|
||||
|
@ -246,7 +246,7 @@ const onOperate = async (operation: string) => {
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
autoStart.value = operation === 'enable' ? 'disable' : 'enable';
|
||||
search();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -4,14 +4,17 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('toolbox.fail2ban.banTime')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
label-position="top"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
@submit.prevent
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item
|
||||
:label="$t('toolbox.fail2ban.banTime')"
|
||||
prop="banTime"
|
||||
:rules="Rules.integerNumber"
|
||||
>
|
||||
<el-form-item :label="$t('toolbox.fail2ban.banTime')" prop="banTime">
|
||||
<el-input type="number" v-model.number="form.banTime">
|
||||
<template #append>
|
||||
<el-select v-model.number="form.banTimeUnit" style="width: 100px">
|
||||
@ -43,7 +46,6 @@ import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { updateFail2ban } from '@/api/modules/toolbox';
|
||||
import { splitTime, transTimeUnit } from '@/utils/util';
|
||||
@ -61,6 +63,20 @@ const form = reactive({
|
||||
banTimeUnit: 's',
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
banTime: [{ validator: checkBanTime, trigger: 'blur' }],
|
||||
});
|
||||
function checkBanTime(rule: any, value: any, callback: any) {
|
||||
if (value === -1) {
|
||||
callback();
|
||||
}
|
||||
const reg = /^[1-9]\d*$/;
|
||||
if (!reg.test(value)) {
|
||||
return callback(new Error(i18n.global.t('toolbox.fail2ban.banTimeRule')));
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
@ -74,11 +90,12 @@ const onSave = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
let itemMsg =
|
||||
form.banTime === -1
|
||||
? i18n.global.t('toolbox.fail2ban.banAllTime')
|
||||
: form.banTime + transTimeUnit(form.banTimeUnit);
|
||||
ElMessageBox.confirm(
|
||||
i18n.global.t('ssh.sshChangeHelper', [
|
||||
i18n.global.t('toolbox.fail2ban.banTime'),
|
||||
form.banTime + transTimeUnit(form.banTimeUnit),
|
||||
]),
|
||||
i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('toolbox.fail2ban.banTime'), itemMsg]),
|
||||
i18n.global.t('toolbox.fail2ban.fail2banChange'),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
@ -86,7 +103,8 @@ const onSave = async (formEl: FormInstance | undefined) => {
|
||||
type: 'info',
|
||||
},
|
||||
).then(async () => {
|
||||
await updateFail2ban({ key: 'bantime', value: form.banTime + form.banTimeUnit })
|
||||
let itemValue = form.banTime === -1 ? '-1' : form.banTime + form.banTimeUnit;
|
||||
await updateFail2ban({ key: 'bantime', value: itemValue })
|
||||
.then(async () => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
loading.value = false;
|
||||
|
@ -65,6 +65,16 @@
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :xs="24" :sm="20" :md="20" :lg="10" :xl="10">
|
||||
<el-form :model="form" label-position="left" ref="formRef" label-width="120px">
|
||||
<el-form-item :label="$t('toolbox.fail2ban.sshPort')" prop="port">
|
||||
<el-input disabled v-model="form.port">
|
||||
<template #append>
|
||||
<el-button @click="onChangePort" icon="Setting">
|
||||
{{ $t('commons.button.set') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('toolbox.fail2ban.sshPortHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('toolbox.fail2ban.maxRetry')" prop="maxRetry">
|
||||
<el-input disabled v-model="form.maxRetry">
|
||||
<template #append>
|
||||
@ -164,6 +174,7 @@
|
||||
<FindTime ref="findTimeRef" @search="search" />
|
||||
<BanAction ref="banActionRef" @search="search" />
|
||||
<LogPath ref="logPathRef" @search="search" />
|
||||
<Port ref="portRef" @search="search" />
|
||||
|
||||
<IPs ref="listRef" />
|
||||
</div>
|
||||
@ -179,6 +190,7 @@ import BanTime from '@/views/toolbox/fail2ban/ban-time/index.vue';
|
||||
import FindTime from '@/views/toolbox/fail2ban/find-time/index.vue';
|
||||
import BanAction from '@/views/toolbox/fail2ban/ban-action/index.vue';
|
||||
import LogPath from '@/views/toolbox/fail2ban/log-path/index.vue';
|
||||
import Port from '@/views/toolbox/fail2ban/port/index.vue';
|
||||
import IPs from '@/views/toolbox/fail2ban/ips/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
@ -191,6 +203,7 @@ const formRef = ref();
|
||||
const extensions = [javascript(), oneDark];
|
||||
const confShowType = ref('base');
|
||||
|
||||
const portRef = ref();
|
||||
const maxRetryRef = ref();
|
||||
const banTimeRef = ref();
|
||||
const findTimeRef = ref();
|
||||
@ -242,6 +255,9 @@ const onSaveFile = async () => {
|
||||
});
|
||||
});
|
||||
};
|
||||
const onChangePort = () => {
|
||||
portRef.value.acceptParams({ port: form.port });
|
||||
};
|
||||
const onChangeMaxRetry = () => {
|
||||
maxRetryRef.value.acceptParams({ maxRetry: form.maxRetry });
|
||||
};
|
||||
@ -279,7 +295,7 @@ const onOperate = async (operation: string) => {
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
autoStart.value = operation === 'enable' ? 'disable' : 'enable';
|
||||
search();
|
||||
});
|
||||
};
|
||||
|
||||
@ -307,7 +323,8 @@ const search = async () => {
|
||||
form.port = res.data.port;
|
||||
form.maxRetry = res.data.maxRetry;
|
||||
form.banTime = res.data.banTime;
|
||||
form.banTimeItem = transTimeUnit(form.banTime);
|
||||
form.banTimeItem =
|
||||
form.banTime === '-1' ? i18n.global.t('toolbox.fail2ban.banAllTime') : transTimeUnit(form.banTime);
|
||||
form.findTime = res.data.findTime;
|
||||
form.findTimeItem = transTimeUnit(form.findTime);
|
||||
form.banAction = res.data.banAction;
|
||||
|
100
frontend/src/views/toolbox/fail2ban/port/index.vue
Normal file
100
frontend/src/views/toolbox/fail2ban/port/index.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('toolbox.fail2ban.sshPort')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
label-position="top"
|
||||
:rules="rules"
|
||||
:model="form"
|
||||
@submit.prevent
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('toolbox.fail2ban.sshPort')" prop="port">
|
||||
<el-input type="number" clearable v-model.number="form.port" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button :disabled="loading" type="primary" @click="onSave(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { ElMessageBox, FormInstance } from 'element-plus';
|
||||
import { Rules, checkNumberRange } from '@/global/form-rules';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { updateFail2ban } from '@/api/modules/toolbox';
|
||||
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
interface DialogProps {
|
||||
port: string;
|
||||
}
|
||||
const drawerVisible = ref();
|
||||
const loading = ref();
|
||||
|
||||
const form = reactive({
|
||||
port: 22,
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
port: [Rules.integerNumber, checkNumberRange(1, 65535)],
|
||||
});
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
form.port = Number(params.port);
|
||||
drawerVisible.value = true;
|
||||
};
|
||||
|
||||
const onSave = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
ElMessageBox.confirm(
|
||||
i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('toolbox.fail2ban.sshPort'), form.port]),
|
||||
i18n.global.t('toolbox.fail2ban.fail2banChange'),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'info',
|
||||
},
|
||||
).then(async () => {
|
||||
await updateFail2ban({ key: 'port', value: form.port + '' })
|
||||
.then(async () => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
loading.value = false;
|
||||
drawerVisible.value = false;
|
||||
emit('search');
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user