diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/index.ts new file mode 100644 index 00000000..1f20d22c --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/index.ts @@ -0,0 +1,199 @@ +import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; +import { CertInfo } from '@certd/plugin-cert'; +import { + AbstractPlusTaskPlugin, + AliyunAccess, + AliyunClient, + AliyunSslClient, + createCertDomainGetterInputDefine, + createRemoteSelectInputDefine, +} from '@certd/plugin-plus'; + +@IsTaskPlugin({ + name: 'AliyunDeployCertToWaf', + title: '部署至阿里云WAF', + icon: 'ant-design:aliyun-outlined', + group: pluginGroups.aliyun.key, + desc: '部署证书到阿里云WAF', + needPlus: true, + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +export class AliyunDeployCertToWaf extends AbstractPlusTaskPlugin { + @TaskInput({ + title: '域名证书', + helper: '请选择证书申请任务输出的域名证书\n或者选择前置任务“上传证书到阿里云”任务的证书ID,可以减少上传到阿里云的证书数量', + component: { + name: 'output-selector', + from: ['CertApply', 'CertApplyLego', 'uploadCertToAliyun'], + }, + required: true, + }) + cert!: CertInfo | number; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + @TaskInput({ + title: 'WAF接入点', + helper: '不会选就按默认', + value: 'cn-hangzhou', + component: { + name: 'a-select', + options: [ + { value: 'cn-hangzhou', label: '中国大陆-华东1(杭州)' }, + { value: 'ap-southeast-1', label: '新加坡' }, + ], + }, + required: true, + }) + regionId!: string; + + @TaskInput({ + title: '证书接入点', + helper: '跟上面保持一致即可', + value: 'cas.aliyuncs.com', + component: { + name: 'a-select', + options: [ + { value: 'cas.aliyuncs.com', label: '中国大陆' }, + { value: 'cas.ap-southeast-1.aliyuncs.com', label: '新加坡' }, + { value: 'cas.eu-central-1.aliyuncs.com', label: '德国(法兰克福)' }, + ], + }, + required: true, + }) + casEndpoint!: string; + + @TaskInput({ + title: 'Access授权', + helper: '阿里云授权AccessKeyId、AccessKeySecret', + component: { + name: 'access-selector', + type: 'aliyun', + }, + required: true, + }) + accessId!: string; + + @TaskInput( + createRemoteSelectInputDefine({ + title: 'CNAME站点', + helper: '请选择要部署证书的CNAME站点', + typeName: 'AliyunDeployCertToWaf', + action: AliyunDeployCertToWaf.prototype.onGetCnameList.name, + watches: ['accessId', 'regionId'], + }) + ) + cnameDomains!: string[]; + + async onInstance() {} + + async getWafClient(access: AliyunAccess) { + const client = new AliyunClient({ logger: this.logger }); + await client.init({ + accessKeyId: access.accessKeyId, + accessKeySecret: access.accessKeySecret, + //https://wafopenapi.cn-hangzhou.aliyuncs.com + endpoint: `https://wafopenapi.${this.regionId}.aliyuncs.com`, + apiVersion: '2021-10-01', + }); + return client; + } + + async getInstanceId(client: AliyunClient) { + const params = { + RegionId: 'cn-hangzhou', + }; + const res = await client.request('DescribeInstance', params); + this.logger.info('获取实例ID', res.InstanceId); + return res.InstanceId; + } + + async execute(): Promise { + this.logger.info('开始部署证书到阿里云'); + const access = await this.accessService.getById(this.accessId); + let certId: any = this.cert; + if (typeof this.cert === 'object') { + const sslClient = new AliyunSslClient({ + access, + logger: this.logger, + endpoint: this.casEndpoint, + }); + + certId = await sslClient.uploadCert({ + name: this.appendTimeSuffix('certd'), + cert: this.cert, + }); + } + + const client = await this.getWafClient(access); + const instanceId = await this.getInstanceId(client); + for (const siteDomain of this.cnameDomains) { + const params = { + RegionId: this.regionId, + InstanceId: instanceId, + Domain: siteDomain, + }; + const siteDetail = await client.request('DescribeDomainDetail', params); + this.logger.info('站点详情', JSON.stringify(siteDetail)); + const listen = siteDetail.Listen; + /** + * "HttpsPorts": [ + * 443 + * ], + * "CertId": "14738336-cn-hangzhou", + */ + const redirect = siteDetail.Redirect; + redirect.Backends = redirect.AllBackends; + listen.CertId = certId + '-' + this.regionId; + if (!listen.HttpsPorts || listen.HttpsPorts.length === 0) { + listen.HttpsPorts = [443]; + } + const updateParams = { + InstanceId: instanceId, + RegionId: this.regionId, + Redirect: JSON.stringify(redirect), + Listen: JSON.stringify(listen), + Domain: siteDomain, + }; + const res = await client.request('ModifyDomain', updateParams); + this.logger.info('部署成功', JSON.stringify(res)); + } + } + + async onGetCnameList(data: any) { + if (!this.accessId) { + throw new Error('请选择Access授权'); + } + const access = await this.accessService.getById(this.accessId); + const client = await this.getWafClient(access); + + const instanceId = await this.getInstanceId(client); + const params = { + RegionId: this.regionId, + InstanceId: instanceId, + PageSize: 50, + }; + + const res = await client.request('DescribeDomains', params); + if (!res?.Domains || res?.Domains.length === 0) { + throw new Error('没有找到CNAME接入的域名站点'); + } + + const options = res.Domains.map((item: any) => { + return { + label: item.Domain, + value: item.Domain, + title: item.Domain, + domain: item.Domain, + }; + }); + return this.ctx.utils.options.buildGroupOptions(options, this.certDomains); + } +} + +new AliyunDeployCertToWaf(); diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/index.ts index 46817ff8..ca0cf228 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/index.ts @@ -2,3 +2,4 @@ export * from './deploy-to-cdn/index.js'; export * from './deploy-to-dcdn/index.js'; export * from './deploy-to-oss/index.js'; export * from './upload-to-aliyun/index.js'; +export * from './deploy-to-waf/index.js';