fix SSH 新增超时时间配置

This commit is contained in:
bwcx_jzy 2022-06-12 16:33:41 +08:00
parent 63f9dc7528
commit 75cd1f66a9
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
7 changed files with 72 additions and 17 deletions

View File

@ -4,6 +4,10 @@
### 🐣 新增功能 ### 🐣 新增功能
1. 【server】SSH 终端新增全屏方式打开
(感谢[@jaseeon](https://gitee.com/jaseeon) [Gitee issues I5BS52](https://gitee.com/dromara/Jpom/issues/I5BS52)
2. 【server】SSH 新增超时时间配置(感谢@带刺的玫瑰)
### 🐞 解决BUG、优化功能 ### 🐞 解决BUG、优化功能
1. 【server】升级 h2 版本,低版本存在漏洞(CVE-2021-23463) 1. 【server】升级 h2 版本,低版本存在漏洞(CVE-2021-23463)

View File

@ -25,6 +25,7 @@ package io.jpom.controller.ssh;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrSplitter; import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
@ -53,6 +54,7 @@ import io.jpom.permission.SystemPermission;
import io.jpom.service.dblog.BuildInfoService; import io.jpom.service.dblog.BuildInfoService;
import io.jpom.service.dblog.SshTerminalExecuteLogService; import io.jpom.service.dblog.SshTerminalExecuteLogService;
import io.jpom.service.node.ssh.SshService; import io.jpom.service.node.ssh.SshService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -71,6 +73,7 @@ import java.util.List;
@RestController @RestController
@RequestMapping(value = "node/ssh") @RequestMapping(value = "node/ssh")
@Feature(cls = ClassFeature.SSH) @Feature(cls = ClassFeature.SSH)
@Slf4j
public class SshController extends BaseServerController { public class SshController extends BaseServerController {
private final SshService sshService; private final SshService sshService;
@ -176,6 +179,8 @@ public class SshController extends BaseServerController {
sshModel.setConnectType(connectType.name()); sshModel.setConnectType(connectType.name());
// 获取允许编辑的后缀 // 获取允许编辑的后缀
String allowEditSuffix = getParameter("allowEditSuffix"); String allowEditSuffix = getParameter("allowEditSuffix");
int timeOut = getParameterInt("timeOut", 5);
sshModel.setTimeOut(timeOut);
List<String> allowEditSuffixList = AgentWhitelist.parseToList(allowEditSuffix, "允许编辑的文件后缀不能为空"); List<String> allowEditSuffixList = AgentWhitelist.parseToList(allowEditSuffix, "允许编辑的文件后缀不能为空");
sshModel.allowEditSuffix(allowEditSuffixList); sshModel.allowEditSuffix(allowEditSuffixList);
try { try {
@ -205,6 +210,7 @@ public class SshController extends BaseServerController {
Session session = SshService.getSessionByModel(sshModel); Session session = SshService.getSessionByModel(sshModel);
JschUtil.close(session); JschUtil.close(session);
} catch (Exception e) { } catch (Exception e) {
log.warn("ssh连接失败", e);
return JsonMessage.getString(505, "ssh连接失败" + e.getMessage()); return JsonMessage.getString(505, "ssh连接失败" + e.getMessage());
} }
if (add) { if (add) {

View File

@ -80,6 +80,10 @@ public class SshModel extends BaseWorkspaceModel {
* 允许编辑的后缀文件 * 允许编辑的后缀文件
*/ */
private String allowEditSuffix; private String allowEditSuffix;
/**
* 节点超时时间
*/
private Integer timeOut;
public SshModel(String id) { public SshModel(String id) {
this.setId(id); this.setId(id);
@ -118,6 +122,18 @@ public class SshModel extends BaseWorkspaceModel {
return pas; return pas;
} }
/**
* 超时时间
*
* @return 最小值 1 分钟
*/
public int timeOut() {
if (this.timeOut == null) {
return 5;
}
return Math.max(1, this.timeOut);
}
public Charset getCharsetT() { public Charset getCharsetT() {
Charset charset; Charset charset;
try { try {

View File

@ -30,6 +30,7 @@ import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.*; import cn.hutool.core.util.*;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.ssh.ChannelType; import cn.hutool.extra.ssh.ChannelType;
import cn.hutool.extra.ssh.JschRuntimeException;
import cn.hutool.extra.ssh.JschUtil; import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.extra.ssh.Sftp; import cn.hutool.extra.ssh.Sftp;
import cn.jiangzeyin.common.spring.SpringUtil; import cn.jiangzeyin.common.spring.SpringUtil;
@ -69,16 +70,16 @@ public class SshService extends BaseWorkspaceService<SshModel> {
data.setPrivateKey(null); data.setPrivateKey(null);
} }
/** // /**
* 获取 ssh 回话 // * 获取 ssh 回话
* // *
* @param sshId id // * @param sshId id
* @return session // * @return session
*/ // */
public static Session getSession(String sshId) { // public static Session getSession(String sshId) {
SshModel sshModel = SpringUtil.getBean(SshService.class).getByKey(sshId, false); // SshModel sshModel = SpringUtil.getBean(SshService.class).getByKey(sshId, false);
return getSessionByModel(sshModel); // return getSessionByModel(sshModel);
} // }
/** /**
* 获取 ssh 回话 * 获取 ssh 回话
@ -88,9 +89,10 @@ public class SshService extends BaseWorkspaceService<SshModel> {
*/ */
public static Session getSessionByModel(SshModel sshModel) { public static Session getSessionByModel(SshModel sshModel) {
Session session = null; Session session = null;
int timeOut = (int) TimeUnit.SECONDS.toMillis(sshModel.timeOut());
SshModel.ConnectType connectType = sshModel.connectType(); SshModel.ConnectType connectType = sshModel.connectType();
if (connectType == SshModel.ConnectType.PASS) { if (connectType == SshModel.ConnectType.PASS) {
session = JschUtil.openSession(sshModel.getHost(), sshModel.getPort(), sshModel.getUser(), sshModel.getPassword()); session = JschUtil.openSession(sshModel.getHost(), sshModel.getPort(), sshModel.getUser(), sshModel.getPassword(), timeOut);
} else if (connectType == SshModel.ConnectType.PUBKEY) { } else if (connectType == SshModel.ConnectType.PUBKEY) {
File rsaFile = null; File rsaFile = null;
@ -126,8 +128,13 @@ public class SshService extends BaseWorkspaceService<SshModel> {
if (session == null) { if (session == null) {
// 简要私钥文件是否存在 // 简要私钥文件是否存在
Assert.state(FileUtil.isFile(rsaFile), "私钥文件不存在:" + FileUtil.getAbsolutePath(rsaFile)); Assert.state(FileUtil.isFile(rsaFile), "私钥文件不存在:" + FileUtil.getAbsolutePath(rsaFile));
session = JschUtil.openSession(sshModel.getHost(), session = JschUtil.createSession(sshModel.getHost(),
sshModel.getPort(), sshModel.getUser(), FileUtil.getAbsolutePath(rsaFile), sshModel.password()); sshModel.getPort(), sshModel.getUser(), FileUtil.getAbsolutePath(rsaFile), sshModel.password());
try {
session.connect(timeOut);
} catch (JSchException e) {
throw new JschRuntimeException(e);
}
} }
} else { } else {
throw new IllegalArgumentException("不支持的模式"); throw new IllegalArgumentException("不支持的模式");

View File

@ -97,4 +97,9 @@ ALTER TABLE NODE_INFO
ALTER TABLE NODE_INFO ALTER TABLE NODE_INFO
ADD IF NOT EXISTS httpProxyType VARCHAR(20) comment 'http 代理类型'; ADD IF NOT EXISTS httpProxyType VARCHAR(20) comment 'http 代理类型';
ALTER TABLE SSH_INFO
ADD IF NOT EXISTS timeOut int default 0 comment '节点超时时间';

View File

@ -59,6 +59,7 @@ export function editSsh(params) {
fileDirs: params.fileDirs, fileDirs: params.fileDirs,
notAllowedCommand: params.notAllowedCommand, notAllowedCommand: params.notAllowedCommand,
allowEditSuffix: params.allowEditSuffix, allowEditSuffix: params.allowEditSuffix,
timeOut: params.timeOut,
}; };
return axios({ return axios({
url: "/node/ssh/save.json", url: "/node/ssh/save.json",

View File

@ -137,9 +137,7 @@
<a-textarea v-model="temp.privateKey" :auto-size="{ minRows: 3, maxRows: 5 }" placeholder="私钥内容,不填将使用默认的 $HOME/.ssh 目录中的配置。支持配置文件目录:file:/xxxx/xx" /> <a-textarea v-model="temp.privateKey" :auto-size="{ minRows: 3, maxRows: 5 }" placeholder="私钥内容,不填将使用默认的 $HOME/.ssh 目录中的配置。支持配置文件目录:file:/xxxx/xx" />
</a-form-model-item> </a-form-model-item>
<a-form-model-item label="编码格式" prop="charset">
<a-input v-model="temp.charset" placeholder="编码格式" />
</a-form-model-item>
<a-form-model-item prop="fileDirs"> <a-form-model-item prop="fileDirs">
<template slot="label"> <template slot="label">
文件目录 文件目录
@ -175,6 +173,16 @@
placeholder="请输入允许编辑文件的后缀及文件编码不设置编码则默认取系统编码多个使用换行。示例设置编码txt@utf-8 不设置编码txt" placeholder="请输入允许编辑文件的后缀及文件编码不设置编码则默认取系统编码多个使用换行。示例设置编码txt@utf-8 不设置编码txt"
/> />
</a-form-model-item> </a-form-model-item>
<a-collapse>
<a-collapse-panel key="1" header="其他配置">
<a-form-model-item label="编码格式" prop="charset">
<a-input v-model="temp.charset" placeholder="编码格式" />
</a-form-model-item>
<a-form-model-item label="超时时间(s)" prop="timeOut">
<a-input-number v-model="temp.timeOut" :min="1" placeholder="单位秒,最小值 1 秒" style="width: 100%" />
</a-form-model-item>
</a-collapse-panel>
</a-collapse>
</a-form-model> </a-form-model>
</a-modal> </a-modal>
<!-- 安装节点 --> <!-- 安装节点 -->
@ -554,6 +562,7 @@ export default {
type: "add", type: "add",
charset: "UTF-8", charset: "UTF-8",
port: 22, port: 22,
timeOut: 5,
connectType: "PASS", connectType: "PASS",
}; };
this.editSshVisible = true; this.editSshVisible = true;
@ -563,9 +572,16 @@ export default {
// //
handleEdit(record) { handleEdit(record) {
this.temp = Object.assign({}, record); this.temp = Object.assign({}, record);
this.temp.fileDirs = record.fileDirs ? JSON.parse(record.fileDirs).join("\r\n") : ""; // this.temp.;
this.temp.allowEditSuffix = record.allowEditSuffix ? JSON.parse(record.allowEditSuffix).join("\r\n") : ""; this.temp.allowEditSuffix = record.allowEditSuffix ? JSON.parse(record.allowEditSuffix).join("\r\n") : "";
this.temp.type = "edit"; // this.temp.type = "edit";
this.temp = {
...this.temp,
fileDirs: record.fileDirs ? JSON.parse(record.fileDirs).join("\r\n") : "",
type: "edit",
allowEditSuffix: record.allowEditSuffix ? JSON.parse(record.allowEditSuffix).join("\r\n") : "",
timeOut: record.timeOut || 5,
};
this.editSshVisible = true; this.editSshVisible = true;
// @author jzy 08-04 // @author jzy 08-04
this.$refs["editSshForm"] && this.$refs["editSshForm"].resetFields(); this.$refs["editSshForm"] && this.$refs["editSshForm"].resetFields();