diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index 309ffa841..0293a12c6 100644 --- a/packages/ui/certd-server/package.json +++ b/packages/ui/certd-server/package.json @@ -70,6 +70,8 @@ "querystring": "^0.2.1", "reflect-metadata": "^0.1.13", "rimraf": "^5.0.5", + "socks": "^2.8.3", + "socks-proxy-agent": "^8.0.4", "ssh2": "^1.15.0", "strip-ansi": "^7.1.0", "svg-captcha": "^1.4.0", diff --git a/packages/ui/certd-server/src/modules/pipeline/service/access-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/access-service.ts index 64df6e37e..36ba80603 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/access-service.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/access-service.ts @@ -60,7 +60,7 @@ export class AccessService extends BaseService implements IAccessS const value = json[key]; const accessInputDefine = accessDefine.input[key]; if (!accessInputDefine) { - throw new ValidateException(`授权类型${accessType}不存在字段${key}`); + continue; } if (!accessInputDefine.encrypt || !value || typeof value !== 'string') { //定义无需加密、value为空、不是字符串 这些不需要加密 diff --git a/packages/ui/certd-server/src/plugins/plugin-host/access/ssh-access.ts b/packages/ui/certd-server/src/plugins/plugin-host/access/ssh-access.ts index e5bad29a8..b490acb52 100644 --- a/packages/ui/certd-server/src/plugins/plugin-host/access/ssh-access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-host/access/ssh-access.ts @@ -18,7 +18,7 @@ export class SshAccess implements IAccess, ConnectConfig { host!: string; @AccessInput({ title: '端口', - value: '22', + value: 22, component: { name: 'a-input-number', placeholder: '22', @@ -64,6 +64,17 @@ export class SshAccess implements IAccess, ConnectConfig { }) passphrase!: string; + @AccessInput({ + title: '代理', + helper: '代理配置,格式:socks5://$username:$password@$host:$port', + component: { + name: 'a-input', + vModel: 'value', + }, + encrypt: false, + }) + socksProxy!: string; + @AccessInput({ title: '是否Windows', helper: '如果是Windows主机,请勾选此项', diff --git a/packages/ui/certd-server/src/plugins/plugin-host/lib/ssh.ts b/packages/ui/certd-server/src/plugins/plugin-host/lib/ssh.ts index 5533e2fa5..a574b09de 100644 --- a/packages/ui/certd-server/src/plugins/plugin-host/lib/ssh.ts +++ b/packages/ui/certd-server/src/plugins/plugin-host/lib/ssh.ts @@ -5,10 +5,13 @@ import * as _ from 'lodash-es'; import { ILogger } from '@certd/pipeline'; import { SshAccess } from '../access/index.js'; import stripAnsi from 'strip-ansi'; +import { SocksClient } from 'socks'; +import { SocksProxy, SocksProxyType } from 'socks/typings/common/constants.js'; + export class AsyncSsh2Client { conn: ssh2.Client; logger: ILogger; - connConf: ssh2.ConnectConfig; + connConf: SshAccess & ssh2.ConnectConfig; windows = false; encoding: string; constructor(connConf: SshAccess, logger: ILogger) { @@ -27,6 +30,23 @@ export class AsyncSsh2Client { async connect() { this.logger.info(`开始连接,${this.connConf.host}:${this.connConf.port}`); + if (this.connConf.socksProxy) { + this.logger.info(`使用代理${this.connConf.socksProxy}`); + if (typeof this.connConf.port === 'string') { + this.connConf.port = parseInt(this.connConf.port); + } + const proxyOption: SocksProxy = this.parseSocksProxyFromUri(this.connConf.socksProxy); + const info = await SocksClient.createConnection({ + proxy: proxyOption, + command: 'connect', + destination: { + host: this.connConf.host, + port: this.connConf.port, + }, + }); + this.logger.info('代理连接成功'); + this.connConf.sock = info.socket; + } return new Promise((resolve, reject) => { try { const conn = new ssh2.Client(); @@ -160,6 +180,26 @@ export class AsyncSsh2Client { this.conn.end(); } } + + private parseSocksProxyFromUri(socksProxyUri: string): SocksProxy { + const url = new URL(socksProxyUri); + let type: SocksProxyType = 5; + if (url.protocol.startsWith('socks4')) { + type = 4; + } + const proxy: SocksProxy = { + host: url.hostname, + port: parseInt(url.port), + type, + }; + if (url.username) { + proxy.userId = url.username; + } + if (url.password) { + proxy.password = url.password; + } + return proxy; + } } export class SshClient {