@@ -1,29 +1,36 @@
import { AbstractTaskPlugin , IsTaskPlugin , pluginGroups , RunStrategy , TaskInput } from ' @certd/pipeline' ;
import { CertInfo , CertApplyPluginNames, CertReader } from ' @certd/plugin-cert' ;
import { AliyunAccess , AliyunClient , AliyunSslClient , createCertDomainGetterInputDefine , createRemoteSelectInputDefine } from '@certd/plugin-lib' ;
import { AbstractTaskPlugin , IsTaskPlugin , pluginGroups , RunStrategy , TaskInput } from " @certd/pipeline" ;
import { CertApplyPluginNames , CertInfo , CertReader } from " @certd/plugin-cert" ;
import {
AliyunAccess ,
AliyunClient ,
AliyunClientV2 ,
AliyunSslClient ,
createCertDomainGetterInputDefine ,
createRemoteSelectInputDefine
} from "@certd/plugin-lib" ;
@IsTaskPlugin ( {
name : ' AliyunDeployCertToALB' ,
title : ' 阿里云-部署至ALB( 应用负载均衡) ' ,
icon : ' svg:icon-aliyun' ,
name : " AliyunDeployCertToALB" ,
title : " 阿里云-部署至ALB( 应用负载均衡) " ,
icon : " svg:icon-aliyun" ,
group : pluginGroups.aliyun.key ,
desc : ' ALB,更新监听器的默认证书' ,
desc : " ALB,更新监听器的默认证书" ,
needPlus : false ,
default : {
strategy : {
runStrategy : RunStrategy.SkipWhenSucceed ,
} ,
} ,
runStrategy : RunStrategy.SkipWhenSucceed
}
}
} )
export class AliyunDeployCertToALB extends AbstractTaskPlugin {
@TaskInput ( {
title : ' 域名证书' ,
helper : ' 请选择证书申请任务输出的域名证书\n或者选择前置任务“上传证书到阿里云”任务的证书ID, 可以减少上传到阿里云的证书数量' ,
title : " 域名证书" ,
helper : " 请选择证书申请任务输出的域名证书\n或者选择前置任务“上传证书到阿里云”任务的证书ID, 可以减少上传到阿里云的证书数量" ,
component : {
name : ' output-selector' ,
from : [ . . . CertApplyPluginNames , ' uploadCertToAliyun' ] ,
name : " output-selector" ,
from : [ . . . CertApplyPluginNames , " uploadCertToAliyun" ]
} ,
required : true ,
required : true
} )
cert ! : CertInfo | number ;
@@ -31,122 +38,257 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin {
certDomains ! : string [ ] ;
@TaskInput ( {
title : 'Access授权' ,
helper : '阿里云授权AccessKeyId、AccessKeySecret' ,
title : "证书接入点" ,
helper : "不会选就保持默认即可" ,
value : "cas.aliyuncs.com" ,
component : {
name : 'access -selector' ,
type : 'aliyun' ,
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 ,
required : true
} )
casEndpoint ! : string ;
@TaskInput ( {
title : "Access授权" ,
helper : "阿里云授权AccessKeyId、AccessKeySecret" ,
component : {
name : "access-selector" ,
type : "aliyun"
} ,
required : true
} )
accessId ! : string ;
@TaskInput (
createRemoteSelectInputDefine ( {
title : ' ALB所在地区' ,
typeName : ' AliyunDeployCertToALB' ,
title : " ALB所在地区" ,
typeName : " AliyunDeployCertToALB" ,
multi : false ,
action : AliyunDeployCertToALB.prototype.onGetRegionList.name ,
watches : [ ' accessId' ] ,
watches : [ " accessId" ]
} )
)
regionId : string ;
@TaskInput (
createRemoteSelectInputDefine ( {
title : ' 负载均衡列表' ,
helper : ' 要部署证书的负载均衡ID' ,
typeName : ' AliyunDeployCertToALB' ,
title : " 负载均衡列表" ,
helper : " 要部署证书的负载均衡ID" ,
typeName : " AliyunDeployCertToALB" ,
action : AliyunDeployCertToALB.prototype.onGetLoadBalanceList.name ,
watches : [ ' regionId' ] ,
watches : [ " regionId" ]
} )
)
loadBalancers ! : string [ ] ;
@TaskInput (
createRemoteSelectInputDefine ( {
title : ' 监听器列表' ,
helper : ' 要部署证书的监听器列表' ,
typeName : ' AliyunDeployCertToALB' ,
title : " 监听器列表" ,
helper : " 要部署证书的监听器列表" ,
typeName : " AliyunDeployCertToALB" ,
action : AliyunDeployCertToALB.prototype.onGetListenerList.name ,
watches : [ ' loadBalancers' ] ,
watches : [ " loadBalancers" ]
} )
)
listeners ! : 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 ;
async onInstance() { }
@TaskInput ( {
title : "部署证书类型" ,
value : "default" ,
component : {
name : "a-select" ,
vModel : "value" ,
options : [
{
label : "默认证书" ,
value : "default"
} ,
{
label : "扩展证书" ,
value : "extension"
}
]
}
}
)
deployType ! : string ;
async onInstance() {
}
async getLBClient ( access : AliyunAccess , region : string ) {
const client = new AliyunClient ( { logger : this.logger } ) ;
const version = ' 2020-06-16' ;
const version = " 2020-06-16" ;
await client . init ( {
accessKeyId : access.accessKeyId ,
accessKeySecret : access.accessKeySecret ,
//https://wafopenapi.cn-hangzhou.aliyuncs.com
endpoint : ` https://alb. ${ region } .aliyuncs.com ` ,
apiVersion : version ,
apiVersion : version
} ) ;
return client ;
}
getALBClientV2 ( access : AliyunAccess ) {
return access . getClient ( ` alb. ${ this . regionId } .aliyuncs.com ` ) ;
}
async execute ( ) : Promise < void > {
this . logger . info ( ` 开始部署证书到阿里云(alb) ` ) ;
const access = await this . getAccess < AliyunAccess > ( this . accessId ) ;
const certId = await this . getAliyunCertId ( access ) ;
const client = await this . getLBClient ( access , this . regionId ) ;
if ( this . deployType === "extension" ) {
//部署扩展证书
const client = this . getALBClientV2 ( access ) ;
await this . deployExtensionCert ( client , certId ) ;
} else {
const client = await this . getLBClient ( access , this . regionId ) ;
await this . deployDefaultCert ( certId , client ) ;
}
this . logger . info ( "执行完成" ) ;
}
private async deployDefaultCert ( certId : any , client : AliyunClient ) {
for ( const listener of this . listeners ) {
//查询原来的证书
let params : any = { } ;
params = {
ListenerId : listener ,
Certificates : [
{
CertificateId : certId ,
} ,
] ,
CertificateId : certId
}
]
} ;
const res = await client . request ( ' UpdateListenerAttribute' , params ) ;
const res = await client . request ( " UpdateListenerAttribute" , params ) ;
this . checkRet ( res ) ;
this . logger . info ( ` 部署 ${ listener } 监听器证书成功 ` , JSON . stringify ( res ) ) ;
//删除旧证书关联
}
this . logger . info ( '执行完成' ) ;
}
async deployExtensionCert ( client : AliyunClientV2 , certId : any ) {
for ( const listenerId of this . listeners ) {
this . logger . info ( ` 开始部署监听器 ${ listenerId } 的扩展证书 ` ) ;
await client . doRequest ( {
// 接口名称
action : "AssociateAdditionalCertificatesWithListener" ,
// 接口版本
version : "2020-06-16" ,
data : {
query : {
ListenerId : listenerId ,
Certificates : [
{
CertificateId : certId
}
]
}
}
} ) ;
this . logger . info ( ` 部署监听器 ${ listenerId } 的扩展证书成功 ` ) ;
}
await this . ctx . utils . sleep ( 10000 )
for ( const listener of this . listeners ) {
await this . clearInvalidCert ( client , listener ) ;
}
}
async clearInvalidCert ( client : AliyunClientV2 , listener : string ) {
const req = {
// 接口名称
action : "ListListenerCertificates" ,
// 接口版本
version : "2020-06-16" ,
data : {
query : {
ListenerId : listener
}
}
} ;
const res = await client . doRequest ( req ) ;
const list = res . Certificates ;
if ( list . length === 0 ) {
this . logger . info ( ` 监听器 ${ listener } 没有绑定证书 ` ) ;
return
}
const sslClient = new AliyunSslClient ( {
access : client.access ,
logger : this.logger ,
endpoint : this.casEndpoint
} ) ;
const certIds = [ ] ;
for ( const item of list ) {
if ( item . Status !== "Associated" ) {
continue ;
}
if ( item . IsDefault ) {
continue ;
}
certIds . push ( parseInt ( item . CertificateId ) ) ;
}
//检查是否过期,过期则删除
const invalidCertIds = [ ] ;
for ( const certId of certIds ) {
const res = await sslClient . getCertInfo ( certId ) ;
if ( res . notAfter < new Date ( ) . getTime ( ) ) {
invalidCertIds . push ( certId ) ;
}
}
if ( invalidCertIds . length === 0 ) {
this . logger . info ( ` 监听器 ${ listener } 没有过期的证书 ` ) ;
return
}
this . logger . info ( ` 开始解绑过期的证书: ${ invalidCertIds } ` ) ;
await client . doRequest ( {
// 接口名称
action : "DissociateAdditionalCertificatesFromListener" ,
// 接口版本
version : "2020-06-16" ,
data : {
query : {
ListenerId : listener ,
Certificates : invalidCertIds.map ( ( item ) = > {
return {
CertificateId : item
}
} )
}
}
} ) ;
this . logger . info ( ` 解绑过期证书成功 ` ) ;
}
async getAliyunCertId ( access : AliyunAccess ) {
let certId : any = this . cert ;
if ( typeof this . cert === ' object' ) {
if ( typeof this . cert === " object" ) {
const sslClient = new AliyunSslClient ( {
access ,
logger : this.logger ,
endpoint : this.casEndpoint ,
endpoint : this.casEndpoint
} ) ;
const certName = this . buildCertName ( CertReader . getMainDomain ( this . cert . crt ) )
const certName = this . buildCertName ( CertReader . getMainDomain ( this . cert . crt ) ) ;
certId = await sslClient . uploadCert ( {
name : certName ,
cert : this.cert ,
cert : this.cert
} ) ;
}
return certId ;
@@ -154,74 +296,74 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin {
async onGetRegionList ( data : any ) {
if ( ! this . accessId ) {
throw new Error ( ' 请选择Access授权' ) ;
throw new Error ( " 请选择Access授权" ) ;
}
const access = await this . getAccess < AliyunAccess > ( this . accessId ) ;
const client = await this . getLBClient ( access , ' cn-shanghai' ) ;
const client = await this . getLBClient ( access , " cn-shanghai" ) ;
const res = await client . request ( ' DescribeRegions' , { } ) ;
const res = await client . request ( " DescribeRegions" , { } ) ;
this . checkRet ( res ) ;
if ( ! res ? . Regions || res ? . Regions . length === 0 ) {
throw new Error ( ' 没有找到Regions列表' ) ;
throw new Error ( " 没有找到Regions列表" ) ;
}
return res . Regions . map ( ( item : any ) = > {
return {
label : item.LocalName ,
value : item.RegionId ,
endpoint : item.RegionEndpoint ,
endpoint : item.RegionEndpoint
} ;
} ) ;
}
async onGetLoadBalanceList ( data : any ) {
if ( ! this . accessId ) {
throw new Error ( ' 请先选择Access授权' ) ;
throw new Error ( " 请先选择Access授权" ) ;
}
if ( ! this . regionId ) {
throw new Error ( ' 请先选择地区' ) ;
throw new Error ( " 请先选择地区" ) ;
}
const access = await this . getAccess < AliyunAccess > ( this . accessId ) ;
const client = await this . getLBClient ( access , this . regionId ) ;
const params = {
MaxResults : 100 ,
MaxResults : 100
} ;
const res = await client . request ( ' ListLoadBalancers' , params ) ;
const res = await client . request ( " ListLoadBalancers" , params ) ;
this . checkRet ( res ) ;
if ( ! res ? . LoadBalancers || res ? . LoadBalancers . length === 0 ) {
throw new Error ( ' 没有找到LoadBalancers' ) ;
throw new Error ( " 没有找到LoadBalancers" ) ;
}
return res . LoadBalancers . map ( ( item : any ) = > {
const label = ` ${ item . LoadBalancerId } < ${ item . LoadBalancerName } }> ` ;
return {
label : label ,
value : item.LoadBalancerId ,
value : item.LoadBalancerId
} ;
} ) ;
}
async onGetListenerList ( data : any ) {
if ( ! this . accessId ) {
throw new Error ( ' 请先选择Access授权' ) ;
throw new Error ( " 请先选择Access授权" ) ;
}
if ( ! this . regionId ) {
throw new Error ( ' 请先选择地区' ) ;
throw new Error ( " 请先选择地区" ) ;
}
const access = await this . getAccess < AliyunAccess > ( this . accessId ) ;
const client = await this . getLBClient ( access , this . regionId ) ;
const params : any = {
MaxResults : 100 ,
MaxResults : 100
} ;
if ( this . loadBalancers && this . loadBalancers . length > 0 ) {
params . LoadBalancerIds = this . loadBalancers ;
}
const res = await client . request ( ' ListListeners' , params ) ;
const res = await client . request ( " ListListeners" , params ) ;
this . checkRet ( res ) ;
if ( ! res ? . Listeners || res ? . Listeners . length === 0 ) {
throw new Error ( ' 没有找到HTTPS监听器' ) ;
throw new Error ( " 没有找到HTTPS监听器" ) ;
}
return res . Listeners . map ( ( item : any ) = > {
@@ -229,16 +371,19 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin {
return {
label : label ,
value : item.ListenerId ,
lbid : item.LoadBalancerId ,
lbid : item.LoadBalancerId
} ;
} ) ;
}
checkRet ( ret : any ) {
if ( ret . Code != null ) {
throw new Error ( ret . Message ) ;
}
}
}
new AliyunDeployCertToALB ( ) ;