From 15d6eaf5532ed25acd4f8d58c429353a2f44206c Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Mon, 20 Jan 2025 23:29:03 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20http=E6=96=B9=E5=BC=8F=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=EF=BC=8C=E9=80=89=E6=8B=A9sftp=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=BF=AE=E6=94=B9=E6=96=87=E4=BB=B6=E8=AE=BF?= =?UTF-8?q?=E9=97=AE=E6=9D=83=E9=99=90=E6=AF=94=E5=A6=82777?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/plugin/cert-plugin/base.ts | 11 ++-- .../src/plugin/cert-plugin/index.ts | 2 +- .../src/plugin/cert-plugin/uploads/factory.ts | 3 ++ .../plugin/cert-plugin/uploads/impls/sftp.ts | 51 +++++++++++++++++++ packages/plugins/plugin-lib/src/ssh/index.ts | 1 + .../plugins/plugin-lib/src/ssh/sftp-access.ts | 34 +++++++++++++ packages/plugins/plugin-lib/src/ssh/ssh.ts | 12 ++--- .../http-verify-plan.vue | 10 ++-- .../plugin/upload-to-host/index.ts | 4 +- 9 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/sftp.ts create mode 100644 packages/plugins/plugin-lib/src/ssh/sftp-access.ts diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts index eb7b7561..88193661 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts @@ -58,7 +58,7 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin { }, required: false, order: 100, - helper: "PFX、jks格式证书是否加密;jks必须设置密码,不传则默认123456", + helper: "PFX、jks格式证书是否加密\njks必须设置密码,不传则默认123456", }) pfxPassword!: string; @@ -66,12 +66,17 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin { title: "PFX证书转换参数", value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", component: { - name: "a-input", + name: "a-auto-complete", vModel: "value", + options: [ + { value: "", label: "兼容 Windows Server 最新" }, + { value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2016" }, + { value: "-nomac -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2008" }, + ], }, required: false, order: 100, - helper: "兼容Server 2016,如果导入证书失败,请删除此参数", + helper: "兼容Windows Server各个版本", }) pfxArgs = "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES"; diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts index 990213dd..da9561e5 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts @@ -232,7 +232,7 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`, // { value: "ec_521", label: "EC 521" }, ], }, - helper: "如无特殊需求,默认即可", + helper: "如无特殊需求,默认即可\n选择RSA 2048 pkcs1可以获得旧版RSA证书", required: true, }) privateKeyType!: PrivateKeyType; diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/factory.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/factory.ts index cc580d2a..2b09b959 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/factory.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/factory.ts @@ -8,6 +8,9 @@ export class HttpChallengeUploaderFactory { } else if (type === "ssh") { const module = await import("./impls/ssh.js"); return module.SshHttpChallengeUploader; + } else if (type === "sftp") { + const module = await import("./impls/sftp.js"); + return module.SftpHttpChallengeUploader; } else if (type === "ftp") { const module = await import("./impls/ftp.js"); return module.FtpHttpChallengeUploader; diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/sftp.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/sftp.ts new file mode 100644 index 00000000..3af1e9c0 --- /dev/null +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/sftp.ts @@ -0,0 +1,51 @@ +import { BaseHttpChallengeUploader } from "../api.js"; +import { SshAccess, SshClient } from "@certd/plugin-lib"; +import path from "path"; +import os from "os"; +import fs from "fs"; +import { SftpAccess } from "@certd/plugin-lib"; + +export class SftpHttpChallengeUploader extends BaseHttpChallengeUploader { + async upload(filePath: string, fileContent: Buffer) { + const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath); + + // Write file to temp path + const dir = path.dirname(tmpFilePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(tmpFilePath, fileContent); + + const access = await this.ctx.accessService.getById(this.access.sshAccess); + const key = this.rootDir + filePath; + try { + const client = new SshClient(this.logger); + await client.uploadFiles({ + connectConf: access, + mkdirs: true, + transports: [ + { + localPath: tmpFilePath, + remotePath: key, + }, + ], + opts: { + mode: this.access?.fileMode ?? undefined, + }, + }); + } finally { + // Remove temp file + fs.unlinkSync(tmpFilePath); + } + } + + async remove(filePath: string) { + const access = await this.ctx.accessService.getById(this.access.sshAccess); + const client = new SshClient(this.logger); + const key = this.rootDir + filePath; + await client.removeFiles({ + connectConf: access, + files: [key], + }); + } +} diff --git a/packages/plugins/plugin-lib/src/ssh/index.ts b/packages/plugins/plugin-lib/src/ssh/index.ts index 27c0e408..63ca53e0 100644 --- a/packages/plugins/plugin-lib/src/ssh/index.ts +++ b/packages/plugins/plugin-lib/src/ssh/index.ts @@ -1,2 +1,3 @@ export * from "./ssh.js"; export * from "./ssh-access.js"; +export * from "./sftp-access.js"; diff --git a/packages/plugins/plugin-lib/src/ssh/sftp-access.ts b/packages/plugins/plugin-lib/src/ssh/sftp-access.ts new file mode 100644 index 00000000..9d4c9fe3 --- /dev/null +++ b/packages/plugins/plugin-lib/src/ssh/sftp-access.ts @@ -0,0 +1,34 @@ +import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline"; + +@IsAccess({ + name: "sftp", + title: "SFTP授权", + desc: "", + icon: "clarity:host-line", + input: {}, +}) +export class SftpAccess extends BaseAccess { + @AccessInput({ + title: "SSH授权", + component: { + name: "access-selector", + type: "ssh", + vModel: "modelValue", + }, + helper: "请选择一个SSH授权", + required: true, + }) + sshAccess!: string; + @AccessInput({ + title: "文件权限", + component: { + name: "a-input", + vModel: "value", + placeholder: "777", + }, + helper: "文件上传后是否修改文件权限", + }) + fileMode!: string; +} + +new SftpAccess(); diff --git a/packages/plugins/plugin-lib/src/ssh/ssh.ts b/packages/plugins/plugin-lib/src/ssh/ssh.ts index 4e239eca..8d534e81 100644 --- a/packages/plugins/plugin-lib/src/ssh/ssh.ts +++ b/packages/plugins/plugin-lib/src/ssh/ssh.ts @@ -80,11 +80,11 @@ export class AsyncSsh2Client { }); } - async fastPut(options: { sftp: any; localPath: string; remotePath: string }) { - const { sftp, localPath, remotePath } = options; + async fastPut(options: { sftp: any; localPath: string; remotePath: string; opts?: { mode?: number } }) { + const { sftp, localPath, remotePath, opts } = options; return new Promise((resolve, reject) => { this.logger.info(`开始上传:${localPath} => ${remotePath}`); - sftp.fastPut(localPath, remotePath, (err: Error) => { + sftp.fastPut(localPath, remotePath, { ...(opts ?? {}) }, (err: Error) => { if (err) { reject(err); this.logger.error("请确认路径是否包含文件名,路径本身不能是目录,路径不能有*?之类的特殊符号,要有写入权限"); @@ -255,8 +255,8 @@ export class SshClient { } * @param options */ - async uploadFiles(options: { connectConf: SshAccess; transports: TransportItem[]; mkdirs: boolean }) { - const { connectConf, transports, mkdirs } = options; + async uploadFiles(options: { connectConf: SshAccess; transports: TransportItem[]; mkdirs: boolean; opts?: { mode?: number } }) { + const { connectConf, transports, mkdirs, opts } = options; await this._call({ connectConf, callable: async (conn: AsyncSsh2Client) => { @@ -281,7 +281,7 @@ export class SshClient { } await conn.exec(mkdirCmd); } - await conn.fastPut({ sftp, ...transport }); + await conn.fastPut({ sftp, ...transport, opts }); } this.logger.info("文件全部上传成功"); }, diff --git a/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/http-verify-plan.vue b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/http-verify-plan.vue index 2c75a6f6..20fda021 100644 --- a/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/http-verify-plan.vue +++ b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/http-verify-plan.vue @@ -30,7 +30,7 @@ diff --git a/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts b/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts index 6a4c3fc7..9f872e9c 100644 --- a/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts @@ -5,10 +5,10 @@ import { SshAccess, SshClient } from '@certd/plugin-lib'; @IsTaskPlugin({ name: 'uploadCertToHost', - title: '主机-部署证书到主机', + title: '主机-部署证书到SSH主机', icon: 'line-md:uploading-loop', group: pluginGroups.host.key, - desc: '上传证书到主机,然后执行部署脚本命令', + desc: 'SFTP上传证书到主机,然后SSH执行部署脚本命令', default: { strategy: { runStrategy: RunStrategy.SkipWhenSucceed,