mirror of https://github.com/1Panel-dev/1Panel
fix: 工具箱管理防止命令注入 (#3215)
parent
40da8a7525
commit
a6e12d88a3
|
@ -10,6 +10,7 @@ type Fail2BanBaseInfo struct {
|
|||
BanTime string `json:"banTime"`
|
||||
FindTime string `json:"findTime"`
|
||||
BanAction string `json:"banAction"`
|
||||
LogPath string `json:"logPath"`
|
||||
}
|
||||
|
||||
type Fail2BanSearch struct {
|
||||
|
@ -17,7 +18,7 @@ type Fail2BanSearch struct {
|
|||
}
|
||||
|
||||
type Fail2BanUpdate struct {
|
||||
Key string `json:"key" validate:"required,oneof=port bantime findtime maxretry banaction"`
|
||||
Key string `json:"key" validate:"required,oneof=port bantime findtime maxretry banaction logpath"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
|
|
|
@ -109,6 +109,9 @@ func (u *DeviceService) CheckDNS(key, value string) (bool, error) {
|
|||
func (u *DeviceService) Update(key, value string) error {
|
||||
switch key {
|
||||
case "TimeZone":
|
||||
if cmd.CheckIllegal(value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
if err := ntp.UpdateSystemTimeZone(value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -123,11 +126,17 @@ func (u *DeviceService) Update(key, value string) error {
|
|||
return err
|
||||
}
|
||||
case "Hostname":
|
||||
if cmd.CheckIllegal(value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
std, err := cmd.Execf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value)
|
||||
if err != nil {
|
||||
return errors.New(std)
|
||||
}
|
||||
case "Ntp", "LocalTime":
|
||||
if cmd.CheckIllegal(value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
ntpValue := value
|
||||
if key == "LocalTime" {
|
||||
ntpItem, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
|
||||
|
@ -193,6 +202,9 @@ func (u *DeviceService) UpdateHosts(req []dto.HostHelper) error {
|
|||
}
|
||||
|
||||
func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
|
||||
if cmd.CheckIllegal(req.User, req.Passwd) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
std, err := cmd.Execf("%s echo '%s:%s' | %s chpasswd", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd())
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "does not exist") {
|
||||
|
@ -204,6 +216,9 @@ func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
|
|||
}
|
||||
|
||||
func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
|
||||
if cmd.CheckIllegal(req.Path) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
if !req.IsNew {
|
||||
std, err := cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
|
|
|
@ -208,4 +208,9 @@ func loadFailValue(line string, baseInfo *dto.Fail2BanBaseInfo) {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ func (f *Fail2ban) ReBanIPs(ips []string) error {
|
|||
|
||||
func (f *Fail2ban) ListBanned() ([]string, error) {
|
||||
var lists []string
|
||||
stdout, err := cmd.Exec("fail2ban-client get sshd banned")
|
||||
stdout, err := cmd.Exec("fail2ban-client get sshd banip")
|
||||
if err != nil {
|
||||
return lists, err
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ maxretry = 5
|
|||
findtime = 300
|
||||
bantime = 600
|
||||
action = %(action_mwl)s
|
||||
logpath = /var/log/secure`
|
||||
logpath = $logpath`
|
||||
|
||||
banaction := ""
|
||||
if active, _ := systemctl.IsActive("firewalld"); active {
|
||||
|
@ -158,6 +158,14 @@ logpath = /var/log/secure`
|
|||
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
|
||||
}
|
||||
|
|
|
@ -965,6 +965,8 @@ const message = {
|
|||
allPorts: ' (All Ports)',
|
||||
ignoreIP: 'IP Whitelist',
|
||||
bannedIP: 'IP Blacklist',
|
||||
logPath: 'Log Path',
|
||||
logPathHelper: 'Default is /var/log/secure or /var/log/auth.log',
|
||||
},
|
||||
},
|
||||
logs: {
|
||||
|
|
|
@ -916,6 +916,8 @@ const message = {
|
|||
allPorts: ' (所有端口)',
|
||||
ignoreIP: 'IP 白名單',
|
||||
bannedIP: 'IP 黑名單',
|
||||
logPath: '日誌路徑',
|
||||
logPathHelper: '預設為 /var/log/secure 或者 /var/log/auth.log',
|
||||
},
|
||||
},
|
||||
logs: {
|
||||
|
|
|
@ -917,6 +917,8 @@ const message = {
|
|||
allPorts: ' (所有端口)',
|
||||
ignoreIP: 'IP 白名单',
|
||||
bannedIP: 'IP 黑名单',
|
||||
logPath: '日志路径',
|
||||
logPathHelper: '默认 /var/log/secure 或者 /var/log/auth.log',
|
||||
},
|
||||
},
|
||||
logs: {
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<el-select v-model="row.sizeUnit" style="width: 85px">
|
||||
<el-option label="KB" value="KB" />
|
||||
<el-option label="MB" value="MB" />
|
||||
<el-option label="GB" value="G" />
|
||||
<el-option label="GB" value="GB" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
|
@ -202,6 +202,7 @@ const onSave = async (row) => {
|
|||
};
|
||||
|
||||
const loadItemSize = (row: any) => {
|
||||
console.log(row.size, row.sizeUnit);
|
||||
switch (row.sizeUnit) {
|
||||
case 'KB':
|
||||
return row.size;
|
||||
|
|
|
@ -102,6 +102,15 @@
|
|||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('toolbox.fail2ban.logPath')" prop="logPath">
|
||||
<el-input disabled v-model="form.logPath">
|
||||
<template #append>
|
||||
<el-button @click="onChangeLogPath" icon="Setting">
|
||||
{{ $t('commons.button.set') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -154,6 +163,7 @@
|
|||
<BanTime ref="banTimeRef" @search="search" />
|
||||
<FindTime ref="findTimeRef" @search="search" />
|
||||
<BanAction ref="banActionRef" @search="search" />
|
||||
<LogPath ref="logPathRef" @search="search" />
|
||||
|
||||
<IPs ref="listRef" />
|
||||
</div>
|
||||
|
@ -168,6 +178,7 @@ import MaxRetry from '@/views/toolbox/fail2ban/max-retry/index.vue';
|
|||
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 IPs from '@/views/toolbox/fail2ban/ips/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
@ -185,6 +196,7 @@ const banTimeRef = ref();
|
|||
const findTimeRef = ref();
|
||||
const banActionRef = ref();
|
||||
const listRef = ref();
|
||||
const logPathRef = ref();
|
||||
|
||||
const autoStart = ref('enable');
|
||||
|
||||
|
@ -242,6 +254,9 @@ const onChangeFindTime = () => {
|
|||
const onChangeBanAction = () => {
|
||||
banActionRef.value.acceptParams({ banAction: form.banAction });
|
||||
};
|
||||
const onChangeLogPath = () => {
|
||||
logPathRef.value.acceptParams({ logPath: form.logPath });
|
||||
};
|
||||
|
||||
const onOperate = async (operation: string) => {
|
||||
let msg = operation === 'enable' || operation === 'disable' ? 'ssh.' : 'commons.button.';
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<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.logPath')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item
|
||||
:label="$t('toolbox.fail2ban.logPath')"
|
||||
prop="logPath"
|
||||
:rules="Rules.requiredInput"
|
||||
>
|
||||
<el-input v-model="form.logPath">
|
||||
<template #prepend>
|
||||
<FileList @choose="loadLogPath"></FileList>
|
||||
</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('toolbox.fail2ban.logPathHelper') }}</span>
|
||||
</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 { updateFail2ban } from '@/api/modules/toolbox';
|
||||
import { ElMessageBox, FormInstance } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
interface DialogProps {
|
||||
logPath: string;
|
||||
}
|
||||
const drawerVisible = ref();
|
||||
const loading = ref();
|
||||
|
||||
const form = reactive({
|
||||
logPath: '',
|
||||
});
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
form.logPath = params.logPath;
|
||||
drawerVisible.value = true;
|
||||
};
|
||||
|
||||
const loadLogPath = async (path: string) => {
|
||||
form.logPath = path;
|
||||
};
|
||||
|
||||
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.logPath'), form.logPath]),
|
||||
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: 'logPath', value: form.logPath })
|
||||
.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