mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
Merge pull request #1038 from fudiwei:main
This commit is contained in:
@@ -17,14 +17,15 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := aliyunapigw.NewSSLDeployerProvider(&aliyunapigw.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
ServiceType: xmaps.GetString(options.ProviderExtendedConfig, "serviceType"),
|
||||
GatewayId: xmaps.GetString(options.ProviderExtendedConfig, "gatewayId"),
|
||||
GroupId: xmaps.GetString(options.ProviderExtendedConfig, "groupId"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
ServiceType: xmaps.GetString(options.ProviderExtendedConfig, "serviceType"),
|
||||
GatewayId: xmaps.GetString(options.ProviderExtendedConfig, "gatewayId"),
|
||||
GroupId: xmaps.GetString(options.ProviderExtendedConfig, "groupId"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,11 +17,12 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := aliyuncdn.NewSSLDeployerProvider(&aliyuncdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,11 +17,12 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := aliyundcdn.NewSSLDeployerProvider(&aliyundcdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,11 +17,12 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := aliyunddospro.NewSSLDeployerProvider(&aliyunddospro.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,12 +17,13 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := aliyunfc.NewSSLDeployerProvider(&aliyunfc.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
ServiceVersion: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "serviceVersion", "3.0"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
ServiceVersion: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "serviceVersion", "3.0"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,10 +17,11 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := aliyunlive.NewSSLDeployerProvider(&aliyunlive.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,11 +17,12 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := aliyunvod.NewSSLDeployerProvider(&aliyunvod.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := baiducloudcdn.NewSSLDeployerProvider(&baiducloudcdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := baishancdn.NewSSLDeployerProvider(&baishancdn.SSLDeployerProviderConfig{
|
||||
ApiToken: credentials.ApiToken,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
|
||||
ApiToken: credentials.ApiToken,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := bytepluscdn.NewSSLDeployerProvider(&bytepluscdn.SSLDeployerProviderConfig{
|
||||
AccessKey: credentials.AccessKey,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKey: credentials.AccessKey,
|
||||
SecretKey: credentials.SecretKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := ctcccloudao.NewSSLDeployerProvider(&ctcccloudao.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := ctcccloudcdn.NewSSLDeployerProvider(&ctcccloudcdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := ctcccloudicdn.NewSSLDeployerProvider(&ctcccloudicdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := ctcccloudlvdn.NewSSLDeployerProvider(&ctcccloudlvdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := pDogeCDN.NewSSLDeployerProvider(&pDogeCDN.SSLDeployerProviderConfig{
|
||||
AccessKey: credentials.AccessKey,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKey: credentials.AccessKey,
|
||||
SecretKey: credentials.SecretKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -21,6 +21,7 @@ func init() {
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
EnterpriseProjectId: credentials.EnterpriseProjectId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := jdcloudcdn.NewSSLDeployerProvider(&jdcloudcdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := jdcloudlive.NewSSLDeployerProvider(&jdcloudlive.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := jdcloudvod.NewSSLDeployerProvider(&jdcloudvod.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,10 +17,11 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := ksyuncdn.NewSSLDeployerProvider(&ksyuncdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := qiniucdn.NewSSLDeployerProvider(&qiniucdn.SSLDeployerProviderConfig{
|
||||
AccessKey: credentials.AccessKey,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKey: credentials.AccessKey,
|
||||
SecretKey: credentials.SecretKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -19,6 +19,7 @@ func init() {
|
||||
provider, err := qiniukodo.NewSSLDeployerProvider(&qiniukodo.SSLDeployerProviderConfig{
|
||||
AccessKey: credentials.AccessKey,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Bucket: xmaps.GetString(options.ProviderExtendedConfig, "bucket"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
|
||||
@@ -17,10 +17,11 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := qiniupili.NewSSLDeployerProvider(&qiniupili.SSLDeployerProviderConfig{
|
||||
AccessKey: credentials.AccessKey,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Hub: xmaps.GetString(options.ProviderExtendedConfig, "hub"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKey: credentials.AccessKey,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Hub: xmaps.GetString(options.ProviderExtendedConfig, "hub"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := rainyunrcdn.NewSSLDeployerProvider(&rainyunrcdn.SSLDeployerProviderConfig{
|
||||
ApiKey: credentials.ApiKey,
|
||||
InstanceId: xmaps.GetInt64(options.ProviderExtendedConfig, "instanceId"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
ApiKey: credentials.ApiKey,
|
||||
InstanceId: xmaps.GetInt64(options.ProviderExtendedConfig, "instanceId"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,10 +17,11 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := tencentcloudcss.NewSSLDeployerProvider(&tencentcloudcss.SSLDeployerProviderConfig{
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,11 +17,12 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := tencentcloudscf.NewSSLDeployerProvider(&tencentcloudscf.SSLDeployerProviderConfig{
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,11 +17,12 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := tencentcloudvod.NewSSLDeployerProvider(&tencentcloudvod.SSLDeployerProviderConfig{
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
SubAppId: xmaps.GetInt64(options.ProviderExtendedConfig, "subAppId"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
SubAppId: xmaps.GetInt64(options.ProviderExtendedConfig, "subAppId"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := upyuncdn.NewSSLDeployerProvider(&upyuncdn.SSLDeployerProviderConfig{
|
||||
Username: credentials.Username,
|
||||
Password: credentials.Password,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
Username: credentials.Username,
|
||||
Password: credentials.Password,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
upyuncdn "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/upyun-cdn"
|
||||
upyunfile "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/upyun-file"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
@@ -16,9 +16,10 @@ func init() {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := upyuncdn.NewSSLDeployerProvider(&upyuncdn.SSLDeployerProviderConfig{
|
||||
provider, err := upyunfile.NewSSLDeployerProvider(&upyunfile.SSLDeployerProviderConfig{
|
||||
Username: credentials.Username,
|
||||
Password: credentials.Password,
|
||||
Bucket: xmaps.GetString(options.ProviderExtendedConfig, "bucket"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
|
||||
@@ -17,9 +17,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := volcenginedcdn.NewSSLDeployerProvider(&volcenginedcdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.SecretAccessKey,
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.SecretAccessKey,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -20,9 +20,10 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := wangsucdn.NewSSLDeployerProvider(&wangsucdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
Domains: lo.Filter(strings.Split(xmaps.GetString(options.ProviderExtendedConfig, "domains"), ";"), func(s string, _ int) bool { return s != "" }),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domains: lo.Filter(strings.Split(xmaps.GetString(options.ProviderExtendedConfig, "domains"), ";"), func(s string, _ int) bool { return s != "" }),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -17,13 +17,14 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := wangsucdnpro.NewSSLDeployerProvider(&wangsucdnpro.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ApiKey: credentials.ApiKey,
|
||||
Environment: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "environment", "production"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
|
||||
WebhookId: xmaps.GetString(options.ProviderExtendedConfig, "webhookId"),
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ApiKey: credentials.ApiKey,
|
||||
Environment: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "environment", "production"),
|
||||
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
|
||||
WebhookId: xmaps.GetString(options.ProviderExtendedConfig, "webhookId"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
|
||||
@@ -106,7 +106,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
}
|
||||
|
||||
siteName := dns01.UnFqdn(authZone)
|
||||
siteID, err := d.getSiteId(siteName)
|
||||
siteID, err := d.findSiteIdByName(siteName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err)
|
||||
}
|
||||
@@ -158,17 +158,17 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
|
||||
pageNumber := 1
|
||||
pageSize := 500
|
||||
func (d *DNSProvider) findSiteIdByName(siteName string) (int64, error) {
|
||||
aliListSitesPageNumber := 1
|
||||
aliListSitesPageSize := 500
|
||||
for {
|
||||
// REF: https://www.alibabacloud.com/help/en/edge-security-acceleration/esa/api-esa-2024-09-10-listsites
|
||||
aliListSitesReq := &aliesa.ListSitesRequest{
|
||||
SiteName: tea.String(siteName),
|
||||
SiteSearchType: tea.String("exact"),
|
||||
PageNumber: tea.Int32(int32(pageNumber)),
|
||||
PageSize: tea.Int32(int32(pageSize)),
|
||||
AccessType: tea.String("NS"),
|
||||
PageNumber: tea.Int32(int32(aliListSitesPageNumber)),
|
||||
PageSize: tea.Int32(int32(aliListSitesPageSize)),
|
||||
}
|
||||
aliListSitesResp, err := d.client.ListSites(aliListSitesReq)
|
||||
if err != nil {
|
||||
@@ -177,20 +177,20 @@ func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
|
||||
|
||||
if aliListSitesResp.Body == nil {
|
||||
break
|
||||
} else {
|
||||
for _, site := range aliListSitesResp.Body.Sites {
|
||||
if *site.GetSiteName() == siteName {
|
||||
return *site.GetSiteId(), nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(aliListSitesResp.Body.Sites) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageNumber++
|
||||
}
|
||||
|
||||
for _, siteItem := range aliListSitesResp.Body.Sites {
|
||||
if *siteItem.GetSiteName() == siteName {
|
||||
return *siteItem.GetSiteId(), nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(aliListSitesResp.Body.Sites) < aliListSitesPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
aliListSitesPageNumber++
|
||||
}
|
||||
|
||||
return 0, errors.New("site not found")
|
||||
return 0, fmt.Errorf("could not find site '%s'", siteName)
|
||||
}
|
||||
|
||||
@@ -145,31 +145,31 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(zoneName, subDomain, tokenValue string) (*bcedns.Record, error) {
|
||||
pageMarker := ""
|
||||
pageSize := 1000
|
||||
bceListRecordPageMarker := ""
|
||||
for {
|
||||
// REF: https://cloud.baidu.com/doc/DNS/s/El4s7lssr#%E6%9F%A5%E8%AF%A2%E8%A7%A3%E6%9E%90%E8%AE%B0%E5%BD%95%E5%88%97%E8%A1%A8
|
||||
bceListRecordReq := &bcedns.ListRecordRequest{}
|
||||
bceListRecordReq.Rr = subDomain
|
||||
bceListRecordReq.Marker = pageMarker
|
||||
bceListRecordReq.MaxKeys = pageSize
|
||||
bceListRecordReq.Marker = bceListRecordPageMarker
|
||||
bceListRecordReq.MaxKeys = 1000
|
||||
|
||||
ceListRecordResp, err := d.client.ListRecord(zoneName, bceListRecordReq)
|
||||
bceListRecordResp, err := d.client.ListRecord(zoneName, bceListRecordReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, record := range ceListRecordResp.Records {
|
||||
for _, record := range bceListRecordResp.Records {
|
||||
if record.Type == "TXT" && record.Rr == subDomain && record.Value == tokenValue {
|
||||
return &record, nil
|
||||
}
|
||||
}
|
||||
|
||||
pageMarker = ceListRecordResp.NextMarker
|
||||
if pageMarker == "" {
|
||||
if bceListRecordResp.NextMarker == "" {
|
||||
break
|
||||
}
|
||||
|
||||
bceListRecordPageMarker = bceListRecordResp.NextMarker
|
||||
}
|
||||
|
||||
return nil, errors.New("record not found")
|
||||
return nil, errors.New("could not find record")
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
zone, err := d.getDNSZone(dns01.UnFqdn(authZone))
|
||||
zone, err := d.findZone(dns01.UnFqdn(authZone))
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: error when list zones: %w", err)
|
||||
}
|
||||
@@ -149,34 +149,36 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZone(zoneName string) (*dnslasdk.DomainRecord, error) {
|
||||
pageIndex := int32(1)
|
||||
pageSize := int32(100)
|
||||
func (d *DNSProvider) findZone(zoneName string) (*dnslasdk.DomainRecord, error) {
|
||||
dnslaListDomainsPageIndex := 1
|
||||
dnslaListDomainsPageSize := 100
|
||||
for {
|
||||
// REF: https://www.dnsla.cn/docs/ApiDoc
|
||||
dnslaListDomainsReq := &dnslasdk.ListDomainsRequest{
|
||||
PageIndex: &pageIndex,
|
||||
PageSize: &pageSize,
|
||||
PageIndex: lo.ToPtr(int32(dnslaListDomainsPageIndex)),
|
||||
PageSize: lo.ToPtr(int32(dnslaListDomainsPageSize)),
|
||||
}
|
||||
dnslaListDomainsResp, err := d.client.ListDomains(dnslaListDomainsReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dnslaListDomainsResp.Data != nil {
|
||||
for _, item := range dnslaListDomainsResp.Data.Results {
|
||||
if strings.TrimRight(item.Domain, ".") == zoneName || strings.TrimRight(item.DisplayDomain, ".") == zoneName {
|
||||
return item, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dnslaListDomainsResp.Data == nil || len(dnslaListDomainsResp.Data.Results) < int(pageSize) {
|
||||
if dnslaListDomainsResp.Data == nil {
|
||||
break
|
||||
}
|
||||
|
||||
pageIndex++
|
||||
for _, domainItem := range dnslaListDomainsResp.Data.Results {
|
||||
if strings.TrimRight(domainItem.Domain, ".") == zoneName || strings.TrimRight(domainItem.DisplayDomain, ".") == zoneName {
|
||||
return domainItem, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(dnslaListDomainsResp.Data.Results) < dnslaListDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
dnslaListDomainsPageIndex++
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("zone '%s' not found", zoneName)
|
||||
return nil, fmt.Errorf("could not find zone '%s'", zoneName)
|
||||
}
|
||||
|
||||
@@ -137,5 +137,5 @@ func (d *DNSProvider) findDNSRecord(zoneName, subDomain, tokenValue string) (lib
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("record not found")
|
||||
return nil, errors.New("could not find record")
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
zone, err := d.getDNSZone(dns01.UnFqdn(authZone))
|
||||
zone, err := d.findZone(dns01.UnFqdn(authZone))
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: error when list zones: %w", err)
|
||||
}
|
||||
@@ -144,7 +144,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("jdcloud: unknown record ID for '%s'", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
zone, err := d.getDNSZone(dns01.UnFqdn(authZone))
|
||||
zone, err := d.findZone(dns01.UnFqdn(authZone))
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: error when list zones: %w", err)
|
||||
}
|
||||
@@ -166,15 +166,15 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZone(zoneName string) (*jddnsmodel.DomainInfo, error) {
|
||||
pageNumber := 1
|
||||
pageSize := 10
|
||||
func (d *DNSProvider) findZone(zoneName string) (*jddnsmodel.DomainInfo, error) {
|
||||
jddnsDescribeDomainsPageNumber := 1
|
||||
jddnsDescribeDomainsPageSize := 10
|
||||
for {
|
||||
// REF: https://docs.jdcloud.com/cn/jd-cloud-dns/api/describedomains
|
||||
jddnsDescribeDomainsReq := jddns.NewDescribeDomainsRequestWithoutParam()
|
||||
jddnsDescribeDomainsReq.SetRegionId(d.config.RegionId)
|
||||
jddnsDescribeDomainsReq.SetPageNumber(pageNumber)
|
||||
jddnsDescribeDomainsReq.SetPageSize(pageSize)
|
||||
jddnsDescribeDomainsReq.SetPageNumber(jddnsDescribeDomainsPageNumber)
|
||||
jddnsDescribeDomainsReq.SetPageSize(jddnsDescribeDomainsPageSize)
|
||||
jddnsDescribeDomainsReq.SetDomainName(zoneName)
|
||||
|
||||
jddnsDescribeDomainsResp, err := d.client.DescribeDomains(jddnsDescribeDomainsReq)
|
||||
@@ -182,18 +182,18 @@ func (d *DNSProvider) getDNSZone(zoneName string) (*jddnsmodel.DomainInfo, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range jddnsDescribeDomainsResp.Result.DataList {
|
||||
if item.DomainName == zoneName {
|
||||
return &item, nil
|
||||
for _, domainItem := range jddnsDescribeDomainsResp.Result.DataList {
|
||||
if domainItem.DomainName == zoneName {
|
||||
return &domainItem, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(jddnsDescribeDomainsResp.Result.DataList) < pageSize {
|
||||
if len(jddnsDescribeDomainsResp.Result.DataList) < jddnsDescribeDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageNumber++
|
||||
jddnsDescribeDomainsPageNumber++
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("jdcloud: zone %s not found", zoneName)
|
||||
return nil, fmt.Errorf("could not find zone '%s'", zoneName)
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
getLoadBalancerAttributeReq := &alialb.GetLoadBalancerAttributeRequest{
|
||||
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
||||
}
|
||||
getLoadBalancerAttributeResp, err := d.sdkClients.ALB.GetLoadBalancerAttributeWithContext(context.TODO(), getLoadBalancerAttributeReq, &dara.RuntimeOptions{})
|
||||
getLoadBalancerAttributeResp, err := d.sdkClients.ALB.GetLoadBalancerAttributeWithContext(ctx, getLoadBalancerAttributeReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'alb.GetLoadBalancerAttribute'", slog.Any("request", getLoadBalancerAttributeReq), slog.Any("response", getLoadBalancerAttributeResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.GetLoadBalancerAttribute': %w", err)
|
||||
@@ -144,8 +144,7 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
// 查询 HTTPS 监听列表
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
||||
listenerIds := make([]string, 0)
|
||||
listListenersLimit := int32(100)
|
||||
var listListenersToken *string = nil
|
||||
listListenersToken := (*string)(nil)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -154,28 +153,30 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
}
|
||||
|
||||
listListenersReq := &alialb.ListListenersRequest{
|
||||
MaxResults: tea.Int32(listListenersLimit),
|
||||
NextToken: listListenersToken,
|
||||
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
||||
MaxResults: tea.Int32(100),
|
||||
LoadBalancerIds: tea.StringSlice([]string{d.config.LoadbalancerId}),
|
||||
ListenerProtocol: tea.String("HTTPS"),
|
||||
}
|
||||
listListenersResp, err := d.sdkClients.ALB.ListListenersWithContext(context.TODO(), listListenersReq, &dara.RuntimeOptions{})
|
||||
listListenersResp, err := d.sdkClients.ALB.ListListenersWithContext(ctx, listListenersReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.ListListeners': %w", err)
|
||||
}
|
||||
|
||||
if listListenersResp.Body.Listeners != nil {
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
if listListenersResp.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
|
||||
if len(listListenersResp.Body.Listeners) == 0 || listListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
}
|
||||
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
}
|
||||
|
||||
// 查询 QUIC 监听列表
|
||||
@@ -189,28 +190,30 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
}
|
||||
|
||||
listListenersReq := &alialb.ListListenersRequest{
|
||||
MaxResults: tea.Int32(listListenersLimit),
|
||||
NextToken: listListenersToken,
|
||||
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
||||
MaxResults: tea.Int32(100),
|
||||
LoadBalancerIds: tea.StringSlice([]string{d.config.LoadbalancerId}),
|
||||
ListenerProtocol: tea.String("QUIC"),
|
||||
}
|
||||
listListenersResp, err := d.sdkClients.ALB.ListListenersWithContext(context.TODO(), listListenersReq, &dara.RuntimeOptions{})
|
||||
listListenersResp, err := d.sdkClients.ALB.ListListenersWithContext(ctx, listListenersReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.ListListeners': %w", err)
|
||||
}
|
||||
|
||||
if listListenersResp.Body.Listeners != nil {
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
if listListenersResp.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
|
||||
if len(listListenersResp.Body.Listeners) == 0 || listListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
}
|
||||
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
}
|
||||
|
||||
// 遍历更新监听证书
|
||||
@@ -258,7 +261,7 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
getListenerAttributeReq := &alialb.GetListenerAttributeRequest{
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
}
|
||||
getListenerAttributeResp, err := d.sdkClients.ALB.GetListenerAttributeWithContext(context.TODO(), getListenerAttributeReq, &dara.RuntimeOptions{})
|
||||
getListenerAttributeResp, err := d.sdkClients.ALB.GetListenerAttributeWithContext(ctx, getListenerAttributeReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'alb.GetListenerAttribute'", slog.Any("request", getListenerAttributeReq), slog.Any("response", getListenerAttributeResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.GetListenerAttribute': %w", err)
|
||||
@@ -275,7 +278,7 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
CertificateId: tea.String(cloudCertId),
|
||||
}},
|
||||
}
|
||||
updateListenerAttributeResp, err := d.sdkClients.ALB.UpdateListenerAttributeWithContext(context.TODO(), updateListenerAttributeReq, &dara.RuntimeOptions{})
|
||||
updateListenerAttributeResp, err := d.sdkClients.ALB.UpdateListenerAttributeWithContext(ctx, updateListenerAttributeReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'alb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerAttributeResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.UpdateListenerAttribute': %w", err)
|
||||
@@ -286,8 +289,7 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
// 查询监听证书列表
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
|
||||
listenerCertificates := make([]alialb.ListListenerCertificatesResponseBodyCertificates, 0)
|
||||
listListenerCertificatesLimit := int32(100)
|
||||
var listListenerCertificatesToken *string = nil
|
||||
listListenerCertificatesToken := (*string)(nil)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -297,30 +299,32 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
|
||||
listListenerCertificatesReq := &alialb.ListListenerCertificatesRequest{
|
||||
NextToken: listListenerCertificatesToken,
|
||||
MaxResults: tea.Int32(listListenerCertificatesLimit),
|
||||
MaxResults: tea.Int32(100),
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
CertificateType: tea.String("Server"),
|
||||
}
|
||||
listListenerCertificatesResp, err := d.sdkClients.ALB.ListListenerCertificatesWithContext(context.TODO(), listListenerCertificatesReq, &dara.RuntimeOptions{})
|
||||
listListenerCertificatesResp, err := d.sdkClients.ALB.ListListenerCertificatesWithContext(ctx, listListenerCertificatesReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'alb.ListListenerCertificates'", slog.Any("request", listListenerCertificatesReq), slog.Any("response", listListenerCertificatesResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.ListListenerCertificates': %w", err)
|
||||
}
|
||||
|
||||
if listListenerCertificatesResp.Body.Certificates != nil {
|
||||
for _, listenerCertificate := range listListenerCertificatesResp.Body.Certificates {
|
||||
listenerCertificates = append(listenerCertificates, *listenerCertificate)
|
||||
}
|
||||
if listListenerCertificatesResp.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, listenerCertificate := range listListenerCertificatesResp.Body.Certificates {
|
||||
listenerCertificates = append(listenerCertificates, *listenerCertificate)
|
||||
}
|
||||
|
||||
if len(listListenerCertificatesResp.Body.Certificates) == 0 || listListenerCertificatesResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenerCertificatesToken = listListenerCertificatesResp.Body.NextToken
|
||||
}
|
||||
|
||||
listListenerCertificatesToken = listListenerCertificatesResp.Body.NextToken
|
||||
}
|
||||
|
||||
// 遍历查询监听证书,并找出需要解除关联的证书
|
||||
// 查询监听证书,并找出需要解除关联的证书
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
|
||||
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-getusercertificatedetail
|
||||
certificateIsAlreadyAssociated := false
|
||||
@@ -354,7 +358,7 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
getUserCertificateDetailReq := &alicas.GetUserCertificateDetailRequest{
|
||||
CertId: tea.Int64(certificateIdAsInt64),
|
||||
}
|
||||
getUserCertificateDetailResp, err := d.sdkClients.CAS.GetUserCertificateDetailWithContext(context.TODO(), getUserCertificateDetailReq, &dara.RuntimeOptions{})
|
||||
getUserCertificateDetailResp, err := d.sdkClients.CAS.GetUserCertificateDetailWithContext(ctx, getUserCertificateDetailReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'cas.GetUserCertificateDetail'", slog.Any("request", getUserCertificateDetailReq), slog.Any("response", getUserCertificateDetailResp))
|
||||
if err != nil {
|
||||
if sdkerr, ok := err.(*tea.SDKError); ok {
|
||||
@@ -397,7 +401,7 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
},
|
||||
},
|
||||
}
|
||||
associateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.AssociateAdditionalCertificatesWithListenerWithContext(context.TODO(), associateAdditionalCertificatesFromListenerReq, &dara.RuntimeOptions{})
|
||||
associateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.AssociateAdditionalCertificatesWithListenerWithContext(ctx, associateAdditionalCertificatesFromListenerReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'alb.AssociateAdditionalCertificatesWithListener'", slog.Any("request", associateAdditionalCertificatesFromListenerReq), slog.Any("response", associateAdditionalCertificatesFromListenerResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.AssociateAdditionalCertificatesWithListener': %w", err)
|
||||
@@ -418,7 +422,7 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
Certificates: dissociateAdditionalCertificates,
|
||||
}
|
||||
dissociateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.DissociateAdditionalCertificatesFromListenerWithContext(context.TODO(), dissociateAdditionalCertificatesFromListenerReq, &dara.RuntimeOptions{})
|
||||
dissociateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.DissociateAdditionalCertificatesFromListenerWithContext(ctx, dissociateAdditionalCertificatesFromListenerReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'alb.DissociateAdditionalCertificatesFromListener'", slog.Any("request", dissociateAdditionalCertificatesFromListenerReq), slog.Any("response", dissociateAdditionalCertificatesFromListenerResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.DissociateAdditionalCertificatesFromListener': %w", err)
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/aliyun-apigw/internal"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -37,6 +39,9 @@ type SSLDeployerProviderConfig struct {
|
||||
// API 分组 ID。
|
||||
// 服务类型为 [SERVICE_TYPE_TRADITIONAL] 时必填。
|
||||
GroupId string `json:"groupId,omitempty"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 自定义域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -116,23 +121,87 @@ func (d *SSLDeployerProvider) deployToTraditional(ctx context.Context, certPEM s
|
||||
if d.config.GroupId == "" {
|
||||
return errors.New("config `groupId` is required")
|
||||
}
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getTraditionalAllDomainsByGroupId(ctx, d.config.GroupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getTraditionalAllDomainsByGroupId(ctx, d.config.GroupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 为自定义域名添加 SSL 证书
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/traditional-api-gateway/developer-reference/api-cloudapi-2016-07-14-setdomaincertificate
|
||||
setDomainCertificateReq := &alicloudapi.SetDomainCertificateRequest{
|
||||
GroupId: tea.String(d.config.GroupId),
|
||||
DomainName: tea.String(d.config.Domain),
|
||||
CertificateName: tea.String(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
|
||||
CertificateBody: tea.String(certPEM),
|
||||
CertificatePrivateKey: tea.String(privkeyPEM),
|
||||
}
|
||||
setDomainCertificateResp, err := d.sdkClients.TraditionalAPIGateway.SetDomainCertificateWithContext(context.TODO(), setDomainCertificateReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'apigateway.SetDomainCertificate'", slog.Any("request", setDomainCertificateReq), slog.Any("response", setDomainCertificateResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apigateway.SetDomainCertificate': %w", err)
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no apigw domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found apigw domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if err := d.updateTraditionalDomainCertificate(ctx, d.config.GroupId, domain, certPEM, privkeyPEM); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -142,66 +211,6 @@ func (d *SSLDeployerProvider) deployToCloudNative(ctx context.Context, certPEM s
|
||||
if d.config.GatewayId == "" {
|
||||
return errors.New("config `gatewayId` is required")
|
||||
}
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 遍历查询域名列表,获取域名 ID
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-listdomains
|
||||
var domainId string
|
||||
listDomainsPageNumber := int32(1)
|
||||
listDomainsPageSize := int32(10)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listDomainsReq := &aliapig.ListDomainsRequest{
|
||||
ResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId),
|
||||
GatewayId: tea.String(d.config.GatewayId),
|
||||
NameLike: tea.String(d.config.Domain),
|
||||
PageNumber: tea.Int32(listDomainsPageNumber),
|
||||
PageSize: tea.Int32(listDomainsPageSize),
|
||||
}
|
||||
listDomainsResp, err := d.sdkClients.CloudNativeAPIGateway.ListDomainsWithContext(context.TODO(), listDomainsReq, make(map[string]*string), &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'apig.ListDomains'", slog.Any("request", listDomainsReq), slog.Any("response", listDomainsResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apig.ListDomains': %w", err)
|
||||
}
|
||||
|
||||
if listDomainsResp.Body.Data.Items != nil {
|
||||
for _, domainInfo := range listDomainsResp.Body.Data.Items {
|
||||
if strings.EqualFold(tea.StringValue(domainInfo.Name), d.config.Domain) {
|
||||
domainId = tea.StringValue(domainInfo.DomainId)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if domainId != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if listDomainsResp.Body.Data.Items == nil || len(listDomainsResp.Body.Data.Items) < int(listDomainsPageSize) {
|
||||
break
|
||||
} else {
|
||||
listDomainsPageNumber++
|
||||
}
|
||||
}
|
||||
if domainId == "" {
|
||||
return errors.New("domain not found")
|
||||
}
|
||||
|
||||
// 查询域名
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-getdomain
|
||||
getDomainReq := &aliapig.GetDomainRequest{}
|
||||
getDomainResp, err := d.sdkClients.CloudNativeAPIGateway.GetDomainWithContext(context.TODO(), tea.String(domainId), getDomainReq, make(map[string]*string), &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'apig.GetDomain'", slog.String("domainId", domainId), slog.Any("request", getDomainReq), slog.Any("response", getDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apig.GetDomain': %w", err)
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
@@ -211,6 +220,196 @@ func (d *SSLDeployerProvider) deployToCloudNative(ctx context.Context, certPEM s
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getCloudNativeAllDomainsByGatewayId(ctx, d.config.GatewayId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getCloudNativeAllDomainsByGatewayId(ctx, d.config.GatewayId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no apigw domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found apigw domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
certId := upres.ExtendedData["CertIdentifier"].(string)
|
||||
if err := d.updateCloudNativeDomainCertificate(ctx, d.config.GatewayId, domain, certId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getTraditionalAllDomainsByGroupId(ctx context.Context, cloudGroupId string) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询 API 分组详情
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/traditional-api-gateway/developer-reference/api-cloudapi-2016-07-14-describeapigroup
|
||||
describeApiGroupReq := &alicloudapi.DescribeApiGroupRequest{
|
||||
GroupId: tea.String(cloudGroupId),
|
||||
}
|
||||
describeApiGroupResp, err := d.sdkClients.TraditionalAPIGateway.DescribeApiGroupWithContext(ctx, describeApiGroupReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'apigateway.DescribeApiGroup'", slog.Any("request", describeApiGroupReq), slog.Any("response", describeApiGroupResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'apigateway.DescribeApiGroup': %w", err)
|
||||
}
|
||||
|
||||
for _, domainItem := range describeApiGroupResp.Body.CustomDomains.DomainItem {
|
||||
if strings.EqualFold(tea.StringValue(domainItem.DomainBindingStatus), "BINDING") {
|
||||
domains = append(domains, tea.StringValue(domainItem.DomainName))
|
||||
}
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getCloudNativeAllDomainsByGatewayId(ctx context.Context, cloudGatewayId string) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询域名列表
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-listdomains
|
||||
listDomainsPageNumber := 1
|
||||
listDomainsPageSize := 10
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listDomainsReq := &aliapig.ListDomainsRequest{
|
||||
ResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId),
|
||||
GatewayId: tea.String(cloudGatewayId),
|
||||
PageNumber: tea.Int32(int32(listDomainsPageNumber)),
|
||||
PageSize: tea.Int32(int32(listDomainsPageSize)),
|
||||
}
|
||||
listDomainsResp, err := d.sdkClients.CloudNativeAPIGateway.ListDomainsWithContext(ctx, listDomainsReq, make(map[string]*string), &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'apig.ListDomains'", slog.Any("request", listDomainsReq), slog.Any("response", listDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'apig.ListDomains': %w", err)
|
||||
}
|
||||
|
||||
if listDomainsResp.Body == nil || listDomainsResp.Body.Data == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, domainItem := range listDomainsResp.Body.Data.Items {
|
||||
if strings.EqualFold(tea.StringValue(domainItem.Status), "Published") {
|
||||
domains = append(domains, tea.StringValue(domainItem.Name))
|
||||
}
|
||||
}
|
||||
|
||||
if len(listDomainsResp.Body.Data.Items) < listDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
listDomainsPageNumber++
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateTraditionalDomainCertificate(ctx context.Context, cloudGroupId string, domain string, certPEM, privkeyPEM string) error {
|
||||
// 为自定义域名添加 SSL 证书
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/traditional-api-gateway/developer-reference/api-cloudapi-2016-07-14-setdomaincertificate
|
||||
setDomainCertificateReq := &alicloudapi.SetDomainCertificateRequest{
|
||||
GroupId: tea.String(cloudGroupId),
|
||||
DomainName: tea.String(domain),
|
||||
CertificateName: tea.String(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
|
||||
CertificateBody: tea.String(certPEM),
|
||||
CertificatePrivateKey: tea.String(privkeyPEM),
|
||||
}
|
||||
setDomainCertificateResp, err := d.sdkClients.TraditionalAPIGateway.SetDomainCertificateWithContext(ctx, setDomainCertificateReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'apigateway.SetDomainCertificate'", slog.Any("request", setDomainCertificateReq), slog.Any("response", setDomainCertificateResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apigateway.SetDomainCertificate': %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateCloudNativeDomainCertificate(ctx context.Context, cloudGatewayId string, domain string, cloudCertId string) error {
|
||||
// 获取域名 ID
|
||||
domainId, err := d.findCloudNativeDomainIdByDomain(ctx, cloudGatewayId, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查询域名
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-getdomain
|
||||
getDomainReq := &aliapig.GetDomainRequest{}
|
||||
getDomainResp, err := d.sdkClients.CloudNativeAPIGateway.GetDomainWithContext(ctx, tea.String(domainId), getDomainReq, make(map[string]*string), &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'apig.GetDomain'", slog.String("domainId", domainId), slog.Any("request", getDomainReq), slog.Any("response", getDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apig.GetDomain': %w", err)
|
||||
}
|
||||
|
||||
// 更新域名
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-updatedomain
|
||||
updateDomainReq := &aliapig.UpdateDomainRequest{
|
||||
@@ -221,9 +420,9 @@ func (d *SSLDeployerProvider) deployToCloudNative(ctx context.Context, certPEM s
|
||||
TlsMin: getDomainResp.Body.Data.TlsMin,
|
||||
TlsMax: getDomainResp.Body.Data.TlsMax,
|
||||
TlsCipherSuitesConfig: getDomainResp.Body.Data.TlsCipherSuitesConfig,
|
||||
CertIdentifier: tea.String(upres.ExtendedData["CertIdentifier"].(string)),
|
||||
CertIdentifier: tea.String(cloudCertId),
|
||||
}
|
||||
updateDomainResp, err := d.sdkClients.CloudNativeAPIGateway.UpdateDomainWithContext(context.TODO(), tea.String(domainId), updateDomainReq, make(map[string]*string), &dara.RuntimeOptions{})
|
||||
updateDomainResp, err := d.sdkClients.CloudNativeAPIGateway.UpdateDomainWithContext(ctx, tea.String(domainId), updateDomainReq, make(map[string]*string), &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'apig.UpdateDomain'", slog.String("domainId", domainId), slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apig.UpdateDomain': %w", err)
|
||||
@@ -232,6 +431,51 @@ func (d *SSLDeployerProvider) deployToCloudNative(ctx context.Context, certPEM s
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) findCloudNativeDomainIdByDomain(ctx context.Context, cloudGatewayId string, domain string) (string, error) {
|
||||
// 查询域名列表
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-listdomains
|
||||
listDomainsPageNumber := 1
|
||||
listDomainsPageSize := 10
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listDomainsReq := &aliapig.ListDomainsRequest{
|
||||
ResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId),
|
||||
GatewayId: tea.String(cloudGatewayId),
|
||||
NameLike: tea.String(domain),
|
||||
PageNumber: tea.Int32(int32(listDomainsPageNumber)),
|
||||
PageSize: tea.Int32(int32(listDomainsPageSize)),
|
||||
}
|
||||
listDomainsResp, err := d.sdkClients.CloudNativeAPIGateway.ListDomainsWithContext(ctx, listDomainsReq, make(map[string]*string), &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'apig.ListDomains'", slog.Any("request", listDomainsReq), slog.Any("response", listDomainsResp))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to execute sdk request 'apig.ListDomains': %w", err)
|
||||
}
|
||||
|
||||
if listDomainsResp.Body == nil || listDomainsResp.Body.Data == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, domainItem := range listDomainsResp.Body.Data.Items {
|
||||
if strings.EqualFold(tea.StringValue(domainItem.Name), domain) {
|
||||
return tea.StringValue(domainItem.DomainId), nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(listDomainsResp.Body.Data.Items) < listDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
listDomainsPageNumber++
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not find domain '%s'", domain)
|
||||
}
|
||||
|
||||
func createSDKClients(accessKeyId, accessKeySecret, region string) (*wSDKClients, error) {
|
||||
// 接入点一览 https://api.aliyun.com/product/APIG
|
||||
var cloudNativeAPIGEndpoint string
|
||||
|
||||
@@ -69,13 +69,14 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
ServiceType: fServiceType,
|
||||
GatewayId: fGatewayId,
|
||||
GroupId: fGroupId,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
ServiceType: fServiceType,
|
||||
GatewayId: fGatewayId,
|
||||
GroupId: fGroupId,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -6,3 +6,12 @@ const (
|
||||
// 服务类型:云原生 API 网关。
|
||||
SERVICE_TYPE_CLOUDNATIVE = "cloudnative"
|
||||
)
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
|
||||
@@ -197,6 +197,48 @@ func NewCloudapiClient(config *openapiutil.Config) (*CloudapiClient, error) {
|
||||
return client, err
|
||||
}
|
||||
|
||||
func (client *CloudapiClient) DescribeApiGroupWithContext(ctx context.Context, request *alicloudapi.DescribeApiGroupRequest, runtime *dara.RuntimeOptions) (_result *alicloudapi.DescribeApiGroupResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
query := map[string]interface{}{}
|
||||
|
||||
if !dara.IsNil(request.GroupId) {
|
||||
query["GroupId"] = request.GroupId
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.SecurityToken) {
|
||||
query["SecurityToken"] = request.SecurityToken
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.Tag) {
|
||||
query["Tag"] = request.Tag
|
||||
}
|
||||
|
||||
req := &openapiutil.OpenApiRequest{
|
||||
Query: openapiutil.Query(query),
|
||||
}
|
||||
params := &openapiutil.Params{
|
||||
Action: dara.String("DescribeApiGroup"),
|
||||
Version: dara.String("2016-07-14"),
|
||||
Protocol: dara.String("HTTPS"),
|
||||
Pathname: dara.String("/"),
|
||||
Method: dara.String("POST"),
|
||||
AuthType: dara.String("AK"),
|
||||
Style: dara.String("RPC"),
|
||||
ReqBodyType: dara.String("formData"),
|
||||
BodyType: dara.String("json"),
|
||||
}
|
||||
_result = &alicloudapi.DescribeApiGroupResponse{}
|
||||
_body, _err := client.CallApiWithCtx(ctx, params, req, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_err = dara.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *CloudapiClient) SetDomainCertificateWithContext(ctx context.Context, request *alicloudapi.SetDomainCertificateRequest, runtime *dara.RuntimeOptions) (_result *alicloudapi.SetDomainCertificateResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
|
||||
@@ -102,7 +102,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
ShowSize: tea.Int32(1),
|
||||
CurrentPage: tea.Int32(1),
|
||||
}
|
||||
listContactResp, err := d.sdkClient.ListContactWithContext(context.TODO(), listContactReq, &dara.RuntimeOptions{})
|
||||
listContactResp, err := d.sdkClient.ListContactWithContext(ctx, listContactReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'cas.ListContact'", slog.Any("request", listContactReq), slog.Any("response", listContactResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cas.ListContact': %w", err)
|
||||
@@ -122,7 +122,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
ResourceIds: tea.String(strings.Join(d.config.ResourceIds, ",")),
|
||||
ContactIds: tea.String(strings.Join(contactIds, ",")),
|
||||
}
|
||||
createDeploymentJobResp, err := d.sdkClient.CreateDeploymentJobWithContext(context.TODO(), createDeploymentJobReq, &dara.RuntimeOptions{})
|
||||
createDeploymentJobResp, err := d.sdkClient.CreateDeploymentJobWithContext(ctx, createDeploymentJobReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'cas.CreateDeploymentJob'", slog.Any("request", createDeploymentJobReq), slog.Any("response", createDeploymentJobResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cas.CreateDeploymentJob': %w", err)
|
||||
@@ -140,17 +140,16 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
describeDeploymentJobReq := &alicas.DescribeDeploymentJobRequest{
|
||||
JobId: createDeploymentJobResp.Body.JobId,
|
||||
}
|
||||
describeDeploymentJobResp, err := d.sdkClient.DescribeDeploymentJobWithContext(context.TODO(), describeDeploymentJobReq, &dara.RuntimeOptions{})
|
||||
describeDeploymentJobResp, err := d.sdkClient.DescribeDeploymentJobWithContext(ctx, describeDeploymentJobReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'cas.DescribeDeploymentJob'", slog.Any("request", describeDeploymentJobReq), slog.Any("response", describeDeploymentJobResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cas.DescribeDeploymentJob': %w", err)
|
||||
}
|
||||
|
||||
if describeDeploymentJobResp.Body.Status == nil || *describeDeploymentJobResp.Body.Status == "editing" {
|
||||
status := tea.StringValue(describeDeploymentJobResp.Body.Status)
|
||||
if status == "" || status == "editing" {
|
||||
return nil, errors.New("unexpected aliyun deployment job status")
|
||||
}
|
||||
|
||||
if *describeDeploymentJobResp.Body.Status == "success" || *describeDeploymentJobResp.Body.Status == "error" {
|
||||
} else if status == "success" || status == "error" {
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/aliyun-cdn/internal"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -28,6 +30,9 @@ type SSLDeployerProviderConfig struct {
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -80,10 +85,6 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
@@ -92,28 +93,163 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// "*.example.com" → ".example.com",适配阿里云 CDN 要求的泛域名格式
|
||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// "*.example.com" → ".example.com",适配阿里云 CDN 要求的泛域名格式
|
||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||
domains = []string{domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain) ||
|
||||
strings.TrimPrefix(d.config.Domain, "*") == strings.TrimPrefix(domain, "*")
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil ||
|
||||
strings.TrimPrefix(d.config.Domain, "*") == strings.TrimPrefix(domain, "*")
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no cdn domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found cdn domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
if err := d.updateDomainCertificate(ctx, domain, certId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getAllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询域名列表
|
||||
// REF: https://help.aliyun.com/zh/cdn/developer-reference/api-cdn-2018-05-10-describeuserdomains
|
||||
describeUserDomainsPageNumber := 1
|
||||
describeUserDomainsPageSize := 500
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
describeUserDomainsReq := &alicdn.DescribeUserDomainsRequest{
|
||||
ResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId),
|
||||
PageNumber: tea.Int32(int32(describeUserDomainsPageNumber)),
|
||||
PageSize: tea.Int32(int32(describeUserDomainsPageSize)),
|
||||
}
|
||||
describeUserDomainsResp, err := d.sdkClient.DescribeUserDomainsWithContext(ctx, describeUserDomainsReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'cdn.DescribeUserDomains'", slog.Any("request", describeUserDomainsReq), slog.Any("response", describeUserDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeUserDomains': %w", err)
|
||||
}
|
||||
|
||||
if describeUserDomainsResp.Body == nil || describeUserDomainsResp.Body.Domains == nil {
|
||||
break
|
||||
}
|
||||
|
||||
ignoredStatuses := []string{"offline", "checking", "check_failed", "stopping", "deleting"}
|
||||
for _, domainItem := range describeUserDomainsResp.Body.Domains.PageData {
|
||||
if lo.Contains(ignoredStatuses, tea.StringValue(domainItem.DomainStatus)) {
|
||||
continue
|
||||
}
|
||||
|
||||
domains = append(domains, tea.StringValue(domainItem.DomainName))
|
||||
}
|
||||
|
||||
if len(describeUserDomainsResp.Body.Domains.PageData) < describeUserDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
describeUserDomainsPageNumber++
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, cloudCertId int64) error {
|
||||
// 设置 CDN 域名域名证书
|
||||
// REF: https://help.aliyun.com/zh/cdn/developer-reference/api-cdn-2018-05-10-setcdndomainsslcertificate
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
setCdnDomainSSLCertificateReq := &alicdn.SetCdnDomainSSLCertificateRequest{
|
||||
DomainName: tea.String(domain),
|
||||
CertType: tea.String("cas"),
|
||||
CertId: tea.Int64(certId),
|
||||
CertId: tea.Int64(cloudCertId),
|
||||
CertRegion: lo.
|
||||
If(d.config.Region == "" || strings.HasPrefix(d.config.Region, "cn-"), tea.String("cn-hangzhou")).
|
||||
Else(tea.String("ap-southeast-1")),
|
||||
SSLProtocol: tea.String("on"),
|
||||
}
|
||||
setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificateWithContext(context.TODO(), setCdnDomainSSLCertificateReq, &dara.RuntimeOptions{})
|
||||
setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificateWithContext(ctx, setCdnDomainSSLCertificateReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'cdn.SetCdnDomainSSLCertificate'", slog.Any("request", setCdnDomainSSLCertificateReq), slog.Any("response", setCdnDomainSSLCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.SetCdnDomainSSLCertificate': %w", err)
|
||||
return fmt.Errorf("failed to execute sdk request 'cdn.SetCdnDomainSSLCertificate': %w", err)
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, accessKeySecret string) (*internal.CdnClient, error) {
|
||||
|
||||
@@ -53,9 +53,10 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package aliyuncdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -35,6 +35,96 @@ func (client *CdnClient) Init(config *openapiutil.Config) (_err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *CdnClient) DescribeUserDomainsWithContext(ctx context.Context, request *alicdn.DescribeUserDomainsRequest, runtime *dara.RuntimeOptions) (_result *alicdn.DescribeUserDomainsResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
query := map[string]interface{}{}
|
||||
|
||||
if !dara.IsNil(request.CdnType) {
|
||||
query["CdnType"] = request.CdnType
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.ChangeEndTime) {
|
||||
query["ChangeEndTime"] = request.ChangeEndTime
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.ChangeStartTime) {
|
||||
query["ChangeStartTime"] = request.ChangeStartTime
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.CheckDomainShow) {
|
||||
query["CheckDomainShow"] = request.CheckDomainShow
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.Coverage) {
|
||||
query["Coverage"] = request.Coverage
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainName) {
|
||||
query["DomainName"] = request.DomainName
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainSearchType) {
|
||||
query["DomainSearchType"] = request.DomainSearchType
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainStatus) {
|
||||
query["DomainStatus"] = request.DomainStatus
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.OwnerId) {
|
||||
query["OwnerId"] = request.OwnerId
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.PageNumber) {
|
||||
query["PageNumber"] = request.PageNumber
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.PageSize) {
|
||||
query["PageSize"] = request.PageSize
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.ResourceGroupId) {
|
||||
query["ResourceGroupId"] = request.ResourceGroupId
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.SecurityToken) {
|
||||
query["SecurityToken"] = request.SecurityToken
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.Source) {
|
||||
query["Source"] = request.Source
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.Tag) {
|
||||
query["Tag"] = request.Tag
|
||||
}
|
||||
|
||||
req := &openapiutil.OpenApiRequest{
|
||||
Query: openapiutil.Query(query),
|
||||
}
|
||||
params := &openapiutil.Params{
|
||||
Action: dara.String("DescribeUserDomains"),
|
||||
Version: dara.String("2018-05-10"),
|
||||
Protocol: dara.String("HTTPS"),
|
||||
Pathname: dara.String("/"),
|
||||
Method: dara.String("POST"),
|
||||
AuthType: dara.String("AK"),
|
||||
Style: dara.String("RPC"),
|
||||
ReqBodyType: dara.String("formData"),
|
||||
BodyType: dara.String("json"),
|
||||
}
|
||||
_result = &alicdn.DescribeUserDomainsResponse{}
|
||||
_body, _err := client.CallApiWithCtx(ctx, params, req, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_err = dara.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *CdnClient) SetCdnDomainSSLCertificateWithContext(ctx context.Context, request *alicdn.SetCdnDomainSSLCertificateRequest, runtime *dara.RuntimeOptions) (_result *alicdn.SetCdnDomainSSLCertificateResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
|
||||
@@ -132,8 +132,7 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
// 查询 HTTPS 监听列表
|
||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerlisteners
|
||||
listenerPorts := make([]int32, 0)
|
||||
describeLoadBalancerListenersLimit := int32(100)
|
||||
var describeLoadBalancerListenersToken *string = nil
|
||||
describeLoadBalancerListenersToken := (*string)(nil)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -143,9 +142,9 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
|
||||
describeLoadBalancerListenersReq := &alislb.DescribeLoadBalancerListenersRequest{
|
||||
RegionId: tea.String(d.config.Region),
|
||||
MaxResults: tea.Int32(describeLoadBalancerListenersLimit),
|
||||
NextToken: describeLoadBalancerListenersToken,
|
||||
LoadBalancerId: []*string{tea.String(d.config.LoadbalancerId)},
|
||||
MaxResults: tea.Int32(100),
|
||||
LoadBalancerId: tea.StringSlice([]string{d.config.LoadbalancerId}),
|
||||
ListenerProtocol: tea.String("https"),
|
||||
}
|
||||
describeLoadBalancerListenersResp, err := d.sdkClient.DescribeLoadBalancerListeners(describeLoadBalancerListenersReq)
|
||||
@@ -154,17 +153,19 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerListeners': %w", err)
|
||||
}
|
||||
|
||||
if describeLoadBalancerListenersResp.Body.Listeners != nil {
|
||||
for _, listener := range describeLoadBalancerListenersResp.Body.Listeners {
|
||||
listenerPorts = append(listenerPorts, *listener.ListenerPort)
|
||||
}
|
||||
if describeLoadBalancerListenersResp.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, listener := range describeLoadBalancerListenersResp.Body.Listeners {
|
||||
listenerPorts = append(listenerPorts, *listener.ListenerPort)
|
||||
}
|
||||
|
||||
if len(describeLoadBalancerListenersResp.Body.Listeners) == 0 || describeLoadBalancerListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
describeLoadBalancerListenersToken = describeLoadBalancerListenersResp.Body.NextToken
|
||||
}
|
||||
|
||||
describeLoadBalancerListenersToken = describeLoadBalancerListenersResp.Body.NextToken
|
||||
}
|
||||
|
||||
// 遍历更新监听证书
|
||||
@@ -254,7 +255,7 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
return fmt.Errorf("failed to execute sdk request 'slb.DescribeDomainExtensions': %w", err)
|
||||
}
|
||||
|
||||
// 遍历修改扩展域名
|
||||
// 遍历修改扩展域名证书
|
||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setdomainextensionattribute
|
||||
if describeDomainExtensionsResp.Body.DomainExtensions != nil && describeDomainExtensionsResp.Body.DomainExtensions.DomainExtension != nil {
|
||||
var errs []error
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/aliyun-dcdn/internal"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -28,6 +30,9 @@ type SSLDeployerProviderConfig struct {
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -80,10 +85,6 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
@@ -92,28 +93,164 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// "*.example.com" → ".example.com",适配阿里云 DCDN 要求的泛域名格式
|
||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// "*.example.com" → ".example.com",适配阿里云 DCDN 要求的泛域名格式
|
||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||
domains = []string{domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain) ||
|
||||
strings.TrimPrefix(d.config.Domain, "*") == strings.TrimPrefix(domain, "*")
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil ||
|
||||
strings.TrimPrefix(d.config.Domain, "*") == strings.TrimPrefix(domain, "*")
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no dcdn domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found dcdn domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
if err := d.updateDomainCertificate(ctx, domain, certId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getAllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询域名列表
|
||||
// REF: https://help.aliyun.com/zh/edge-security-acceleration/dcdn/developer-reference/api-dcdn-2018-01-15-describedcdnuserdomains
|
||||
describeUserDomainsPageNumber := 1
|
||||
describeUserDomainsPageSize := 500
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
describeDcdnUserDomainsReq := &alidcdn.DescribeDcdnUserDomainsRequest{
|
||||
ResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId),
|
||||
CheckDomainShow: tea.Bool(true),
|
||||
PageNumber: tea.Int32(int32(describeUserDomainsPageNumber)),
|
||||
PageSize: tea.Int32(int32(describeUserDomainsPageSize)),
|
||||
}
|
||||
describeDcdnUserDomainsResp, err := d.sdkClient.DescribeDcdnUserDomainsWithContext(ctx, describeDcdnUserDomainsReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'dcdn.DescribeDcdnUserDomains'", slog.Any("request", describeDcdnUserDomainsReq), slog.Any("response", describeDcdnUserDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'dcdn.DescribeDcdnUserDomains': %w", err)
|
||||
}
|
||||
|
||||
if describeDcdnUserDomainsResp.Body == nil || describeDcdnUserDomainsResp.Body.Domains == nil {
|
||||
break
|
||||
}
|
||||
|
||||
ignoredStatuses := []string{"offline", "checking", "check_failed", "stopping", "deleting"}
|
||||
for _, domainItem := range describeDcdnUserDomainsResp.Body.Domains.PageData {
|
||||
if lo.Contains(ignoredStatuses, tea.StringValue(domainItem.DomainStatus)) {
|
||||
continue
|
||||
}
|
||||
|
||||
domains = append(domains, tea.StringValue(domainItem.DomainName))
|
||||
}
|
||||
|
||||
if len(describeDcdnUserDomainsResp.Body.Domains.PageData) < describeUserDomainsPageNumber {
|
||||
break
|
||||
}
|
||||
|
||||
describeUserDomainsPageNumber++
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, cloudCertId int64) error {
|
||||
// 配置域名证书
|
||||
// REF: https://help.aliyun.com/zh/edge-security-acceleration/dcdn/developer-reference/api-dcdn-2018-01-15-setdcdndomainsslcertificate
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
setDcdnDomainSSLCertificateReq := &alidcdn.SetDcdnDomainSSLCertificateRequest{
|
||||
DomainName: tea.String(domain),
|
||||
CertType: tea.String("cas"),
|
||||
CertId: tea.Int64(int64(certId)),
|
||||
CertId: tea.Int64(cloudCertId),
|
||||
CertRegion: lo.
|
||||
If(d.config.Region == "" || strings.HasPrefix(d.config.Region, "cn-"), tea.String("cn-hangzhou")).
|
||||
Else(tea.String("ap-southeast-1")),
|
||||
SSLProtocol: tea.String("on"),
|
||||
}
|
||||
setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificateWithContext(context.TODO(), setDcdnDomainSSLCertificateReq, &dara.RuntimeOptions{})
|
||||
setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificateWithContext(ctx, setDcdnDomainSSLCertificateReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'dcdn.SetDcdnDomainSSLCertificate'", slog.Any("request", setDcdnDomainSSLCertificateReq), slog.Any("response", setDcdnDomainSSLCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'dcdn.SetDcdnDomainSSLCertificate': %w", err)
|
||||
return fmt.Errorf("failed to execute sdk request 'dcdn.SetDcdnDomainSSLCertificate': %w", err)
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, accessKeySecret string) (*internal.DcdnClient, error) {
|
||||
|
||||
@@ -53,9 +53,10 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package aliyundcdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -35,6 +35,92 @@ func (client *DcdnClient) Init(config *openapiutil.Config) (_err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DcdnClient) DescribeDcdnUserDomainsWithContext(ctx context.Context, request *alidcdn.DescribeDcdnUserDomainsRequest, runtime *dara.RuntimeOptions) (_result *alidcdn.DescribeDcdnUserDomainsResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
query := map[string]interface{}{}
|
||||
|
||||
if !dara.IsNil(request.ChangeEndTime) {
|
||||
query["ChangeEndTime"] = request.ChangeEndTime
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.ChangeStartTime) {
|
||||
query["ChangeStartTime"] = request.ChangeStartTime
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.CheckDomainShow) {
|
||||
query["CheckDomainShow"] = request.CheckDomainShow
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.Coverage) {
|
||||
query["Coverage"] = request.Coverage
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainName) {
|
||||
query["DomainName"] = request.DomainName
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainSearchType) {
|
||||
query["DomainSearchType"] = request.DomainSearchType
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainStatus) {
|
||||
query["DomainStatus"] = request.DomainStatus
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.OwnerId) {
|
||||
query["OwnerId"] = request.OwnerId
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.PageNumber) {
|
||||
query["PageNumber"] = request.PageNumber
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.PageSize) {
|
||||
query["PageSize"] = request.PageSize
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.ResourceGroupId) {
|
||||
query["ResourceGroupId"] = request.ResourceGroupId
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.SecurityToken) {
|
||||
query["SecurityToken"] = request.SecurityToken
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.Tag) {
|
||||
query["Tag"] = request.Tag
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.WebSiteType) {
|
||||
query["WebSiteType"] = request.WebSiteType
|
||||
}
|
||||
|
||||
req := &openapiutil.OpenApiRequest{
|
||||
Query: openapiutil.Query(query),
|
||||
}
|
||||
params := &openapiutil.Params{
|
||||
Action: dara.String("DescribeDcdnUserDomains"),
|
||||
Version: dara.String("2018-01-15"),
|
||||
Protocol: dara.String("HTTPS"),
|
||||
Pathname: dara.String("/"),
|
||||
Method: dara.String("POST"),
|
||||
AuthType: dara.String("AK"),
|
||||
Style: dara.String("RPC"),
|
||||
ReqBodyType: dara.String("formData"),
|
||||
BodyType: dara.String("json"),
|
||||
}
|
||||
_result = &alidcdn.DescribeDcdnUserDomainsResponse{}
|
||||
_body, _err := client.CallApiWithCtx(ctx, params, req, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_err = dara.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *DcdnClient) SetDcdnDomainSSLCertificateWithContext(ctx context.Context, request *alidcdn.SetDcdnDomainSSLCertificateRequest, runtime *dara.RuntimeOptions) (_result *alidcdn.SetDcdnDomainSSLCertificateResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/aliyun-ddospro/internal"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -27,6 +29,9 @@ type SSLDeployerProviderConfig struct {
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 网站域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -81,10 +86,6 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
@@ -93,21 +94,129 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 为网站业务转发规则关联 SSL 证书
|
||||
// REF: https://help.aliyun.com/zh/anti-ddos/anti-ddos-pro-and-premium/developer-reference/api-ddoscoo-2020-01-01-associatewebcert
|
||||
associateWebCertReq := &aliddoscoo.AssociateWebCertRequest{
|
||||
Domain: tea.String(d.config.Domain),
|
||||
CertIdentifier: tea.String(upres.ExtendedData["CertIdentifier"].(string)),
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
associateWebCertResp, err := d.sdkClient.AssociateWebCertWithContext(context.TODO(), associateWebCertReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'dcdn.AssociateWebCert'", slog.Any("request", associateWebCertReq), slog.Any("response", associateWebCertResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'dcdn.AssociateWebCert': %w", err)
|
||||
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no ddoscoo domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found ddoscoo domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
certId := upres.ExtendedData["CertIdentifier"].(string)
|
||||
if err := d.updateDomainCertificate(ctx, domain, certId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getAllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询已配置网站业务转发规则的域名
|
||||
// REF: https://help.aliyun.com/zh/anti-ddos/anti-ddos-pro-and-premium/developer-reference/api-ddoscoo-2020-01-01-describedomains
|
||||
describeDomainsReq := &aliddoscoo.DescribeDomainsRequest{
|
||||
ResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId),
|
||||
}
|
||||
describeDomainsResp, err := d.sdkClient.DescribeDomainsWithContext(ctx, describeDomainsReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'aliddoscoo.DescribeLiveUserDomains'", slog.Any("request", describeDomainsReq), slog.Any("response", describeDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'aliddoscoo.DescribeDomains': %w", err)
|
||||
}
|
||||
|
||||
for _, domain := range describeDomainsResp.Body.Domains {
|
||||
domains = append(domains, tea.StringValue(domain))
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, cloudCertId string) error {
|
||||
// 为网站业务转发规则关联 SSL 证书
|
||||
// REF: https://help.aliyun.com/zh/anti-ddos/anti-ddos-pro-and-premium/developer-reference/api-ddoscoo-2020-01-01-associatewebcert
|
||||
associateWebCertReq := &aliddoscoo.AssociateWebCertRequest{
|
||||
Domain: tea.String(domain),
|
||||
CertIdentifier: tea.String(cloudCertId),
|
||||
}
|
||||
associateWebCertResp, err := d.sdkClient.AssociateWebCertWithContext(ctx, associateWebCertReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'dcdn.AssociateWebCert'", slog.Any("request", associateWebCertReq), slog.Any("response", associateWebCertResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'dcdn.AssociateWebCert': %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, accessKeySecret, region string) (*internal.DdoscooClient, error) {
|
||||
// 接入点一览 https://api.aliyun.com/product/ddoscoo
|
||||
var endpoint string
|
||||
|
||||
@@ -57,10 +57,11 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package aliyunddospro
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -92,3 +92,41 @@ func (client *DdoscooClient) AssociateWebCertWithContext(ctx context.Context, re
|
||||
_err = dara.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *DdoscooClient) DescribeDomainsWithContext(ctx context.Context, request *aliddoscoo.DescribeDomainsRequest, runtime *dara.RuntimeOptions) (_result *aliddoscoo.DescribeDomainsResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
query := map[string]interface{}{}
|
||||
|
||||
if !dara.IsNil(request.InstanceIds) {
|
||||
query["InstanceIds"] = request.InstanceIds
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.ResourceGroupId) {
|
||||
query["ResourceGroupId"] = request.ResourceGroupId
|
||||
}
|
||||
|
||||
req := &openapiutil.OpenApiRequest{
|
||||
Query: openapiutil.Query(query),
|
||||
}
|
||||
params := &openapiutil.Params{
|
||||
Action: dara.String("DescribeDomains"),
|
||||
Version: dara.String("2020-01-01"),
|
||||
Protocol: dara.String("HTTPS"),
|
||||
Pathname: dara.String("/"),
|
||||
Method: dara.String("POST"),
|
||||
AuthType: dara.String("AK"),
|
||||
Style: dara.String("RPC"),
|
||||
ReqBodyType: dara.String("formData"),
|
||||
BodyType: dara.String("json"),
|
||||
}
|
||||
_result = &aliddoscoo.DescribeDomainsResponse{}
|
||||
_body, _err := client.CallApiWithCtx(ctx, params, req, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_err = dara.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
CasId: tea.Int64(certId),
|
||||
Region: tea.String(d.config.Region),
|
||||
}
|
||||
setCertificateResp, err := d.sdkClient.SetCertificateWithContext(context.TODO(), setCertificateReq, &dara.RuntimeOptions{})
|
||||
setCertificateResp, err := d.sdkClient.SetCertificateWithContext(ctx, setCertificateReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'esa.SetCertificate'", slog.Any("request", setCertificateReq), slog.Any("response", setCertificateResp))
|
||||
if err != nil {
|
||||
var sdkError *tea.SDKError
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
@@ -12,9 +13,12 @@ import (
|
||||
alifc2 "github.com/alibabacloud-go/fc-open-20210406/v2/client"
|
||||
"github.com/alibabacloud-go/tea/dara"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/aliyun-fc/internal"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -29,6 +33,9 @@ type SSLDeployerProviderConfig struct {
|
||||
// 服务版本。
|
||||
// 可取值 "2.0"、"3.0"。
|
||||
ServiceVersion string `json:"serviceVersion"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 自定义域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -91,16 +98,270 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) deployToFC3(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getFC3AllDomains(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getFC3AllDomains(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no fc domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found fc domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if err := d.updateFC3DomainCertificate(ctx, domain, certPEM, privkeyPEM); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) deployToFC2(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getFC2AllDomains(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getFC2AllDomains(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no fc domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found fc domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if err := d.updateFC2DomainCertificate(ctx, domain, certPEM, privkeyPEM); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getFC3AllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 列出自定义域名
|
||||
// REF: https://help.aliyun.com/zh/functioncompute/fc/developer-reference/api-fc-2023-03-30-listcustomdomains
|
||||
listCustomDomainsNextToken := (*string)(nil)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listCustomDomainsReq := &alifc3.ListCustomDomainsRequest{
|
||||
NextToken: listCustomDomainsNextToken,
|
||||
Limit: tea.Int32(100),
|
||||
}
|
||||
listCustomDomainsResp, err := d.sdkClients.FC3.ListCustomDomainsWithContext(ctx, listCustomDomainsReq, make(map[string]*string, 0), &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'fc.ListCustomDomains'", slog.Any("request", listCustomDomainsReq), slog.Any("response", listCustomDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'fc.ListCustomDomains': %w", err)
|
||||
}
|
||||
|
||||
if listCustomDomainsResp.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, domainItem := range listCustomDomainsResp.Body.CustomDomains {
|
||||
domains = append(domains, tea.StringValue(domainItem.DomainName))
|
||||
}
|
||||
|
||||
if len(listCustomDomainsResp.Body.CustomDomains) == 0 || listCustomDomainsResp.Body.NextToken == nil {
|
||||
break
|
||||
}
|
||||
|
||||
listCustomDomainsNextToken = listCustomDomainsResp.Body.NextToken
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getFC2AllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 列出自定义域名
|
||||
// REF: https://help.aliyun.com/zh/functioncompute/fc-2-0/developer-reference/api-fc-open-2021-04-06-listcustomdomains
|
||||
listCustomDomainsNextToken := (*string)(nil)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listCustomDomainsReq := &alifc2.ListCustomDomainsRequest{
|
||||
NextToken: listCustomDomainsNextToken,
|
||||
Limit: tea.Int32(100),
|
||||
}
|
||||
listCustomDomainsResp, err := d.sdkClients.FC2.ListCustomDomains(listCustomDomainsReq)
|
||||
d.logger.Debug("sdk request 'fc.ListCustomDomains'", slog.Any("request", listCustomDomainsReq), slog.Any("response", listCustomDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'fc.ListCustomDomains': %w", err)
|
||||
}
|
||||
|
||||
if listCustomDomainsResp.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, domainItem := range listCustomDomainsResp.Body.CustomDomains {
|
||||
domains = append(domains, tea.StringValue(domainItem.DomainName))
|
||||
}
|
||||
|
||||
if len(listCustomDomainsResp.Body.CustomDomains) == 0 || listCustomDomainsResp.Body.NextToken == nil {
|
||||
break
|
||||
}
|
||||
|
||||
listCustomDomainsNextToken = listCustomDomainsResp.Body.NextToken
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateFC3DomainCertificate(ctx context.Context, domain string, certPEM, privkeyPEM string) error {
|
||||
// 获取自定义域名
|
||||
// REF: https://help.aliyun.com/zh/functioncompute/fc-3-0/developer-reference/api-fc-2023-03-30-getcustomdomain
|
||||
getCustomDomainResp, err := d.sdkClients.FC3.GetCustomDomainWithContext(context.TODO(), tea.String(d.config.Domain), make(map[string]*string), &dara.RuntimeOptions{})
|
||||
getCustomDomainResp, err := d.sdkClients.FC3.GetCustomDomainWithContext(ctx, tea.String(domain), make(map[string]*string), &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'fc.GetCustomDomain'", slog.Any("response", getCustomDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'fc.GetCustomDomain': %w", err)
|
||||
} else {
|
||||
if getCustomDomainResp.Body.CertConfig != nil && tea.StringValue(getCustomDomainResp.Body.CertConfig.Certificate) == certPEM {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 更新自定义域名
|
||||
@@ -119,7 +380,7 @@ func (d *SSLDeployerProvider) deployToFC3(ctx context.Context, certPEM string, p
|
||||
if tea.StringValue(updateCustomDomainReq.Body.Protocol) == "HTTP" {
|
||||
updateCustomDomainReq.Body.Protocol = tea.String("HTTP,HTTPS")
|
||||
}
|
||||
updateCustomDomainResp, err := d.sdkClients.FC3.UpdateCustomDomainWithContext(context.TODO(), tea.String(d.config.Domain), updateCustomDomainReq, make(map[string]*string), &dara.RuntimeOptions{})
|
||||
updateCustomDomainResp, err := d.sdkClients.FC3.UpdateCustomDomainWithContext(ctx, tea.String(domain), updateCustomDomainReq, make(map[string]*string), &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'fc.UpdateCustomDomain'", slog.Any("request", updateCustomDomainReq), slog.Any("response", updateCustomDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'fc.UpdateCustomDomain': %w", err)
|
||||
@@ -128,17 +389,17 @@ func (d *SSLDeployerProvider) deployToFC3(ctx context.Context, certPEM string, p
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) deployToFC2(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateFC2DomainCertificate(ctx context.Context, domain string, certPEM, privkeyPEM string) error {
|
||||
// 获取自定义域名
|
||||
// REF: https://help.aliyun.com/zh/functioncompute/fc-2-0/developer-reference/api-fc-open-2021-04-06-getcustomdomain
|
||||
getCustomDomainResp, err := d.sdkClients.FC2.GetCustomDomain(tea.String(d.config.Domain))
|
||||
getCustomDomainResp, err := d.sdkClients.FC2.GetCustomDomain(tea.String(domain))
|
||||
d.logger.Debug("sdk request 'fc.GetCustomDomain'", slog.Any("response", getCustomDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'fc.GetCustomDomain': %w", err)
|
||||
} else {
|
||||
if getCustomDomainResp.Body.CertConfig != nil && tea.StringValue(getCustomDomainResp.Body.CertConfig.Certificate) == certPEM {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 更新自定义域名
|
||||
@@ -155,7 +416,7 @@ func (d *SSLDeployerProvider) deployToFC2(ctx context.Context, certPEM string, p
|
||||
if tea.StringValue(updateCustomDomainReq.Protocol) == "HTTP" {
|
||||
updateCustomDomainReq.Protocol = tea.String("HTTP,HTTPS")
|
||||
}
|
||||
updateCustomDomainResp, err := d.sdkClients.FC2.UpdateCustomDomain(tea.String(d.config.Domain), updateCustomDomainReq)
|
||||
updateCustomDomainResp, err := d.sdkClients.FC2.UpdateCustomDomain(tea.String(domain), updateCustomDomainReq)
|
||||
d.logger.Debug("sdk request 'fc.UpdateCustomDomain'", slog.Any("request", updateCustomDomainReq), slog.Any("response", updateCustomDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'fc.UpdateCustomDomain': %w", err)
|
||||
|
||||
@@ -57,10 +57,12 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
ServiceVersion: "3.0",
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package aliyunfc
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -63,6 +63,49 @@ func (client *FcClient) GetCustomDomainWithContext(ctx context.Context, domainNa
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *FcClient) ListCustomDomainsWithContext(ctx context.Context, request *alifc.ListCustomDomainsRequest, headers map[string]*string, runtime *dara.RuntimeOptions) (_result *alifc.ListCustomDomainsResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
query := map[string]interface{}{}
|
||||
|
||||
if !dara.IsNil(request.Limit) {
|
||||
query["limit"] = request.Limit
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.NextToken) {
|
||||
query["nextToken"] = request.NextToken
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.Prefix) {
|
||||
query["prefix"] = request.Prefix
|
||||
}
|
||||
|
||||
req := &openapiutilv2.OpenApiRequest{
|
||||
Headers: headers,
|
||||
Query: openapiutil.Query(query),
|
||||
}
|
||||
params := &openapiutilv2.Params{
|
||||
Action: dara.String("ListCustomDomains"),
|
||||
Version: dara.String("2023-03-30"),
|
||||
Protocol: dara.String("HTTPS"),
|
||||
Pathname: dara.String("/2023-03-30/custom-domains"),
|
||||
Method: dara.String("GET"),
|
||||
AuthType: dara.String("AK"),
|
||||
Style: dara.String("ROA"),
|
||||
ReqBodyType: dara.String("json"),
|
||||
BodyType: dara.String("json"),
|
||||
}
|
||||
_result = &alifc.ListCustomDomainsResponse{}
|
||||
_body, _err := client.CallApiWithCtx(ctx, params, req, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_err = dara.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *FcClient) UpdateCustomDomainWithContext(ctx context.Context, domainName *string, request *alifc.UpdateCustomDomainRequest, headers map[string]*string, runtime *dara.RuntimeOptions) (_result *alifc.UpdateCustomDomainResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
@@ -172,6 +215,82 @@ func (client *FcopenClient) GetCustomDomainWithOptions(domainName *string, heade
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *FcopenClient) ListCustomDomains(request *alifcopen.ListCustomDomainsRequest) (_result *alifcopen.ListCustomDomainsResponse, _err error) {
|
||||
runtime := &util.RuntimeOptions{}
|
||||
headers := &alifcopen.ListCustomDomainsHeaders{}
|
||||
_result = &alifcopen.ListCustomDomainsResponse{}
|
||||
_body, _err := client.ListCustomDomainsWithOptions(request, headers, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_result = _body
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *FcopenClient) ListCustomDomainsWithOptions(request *alifcopen.ListCustomDomainsRequest, headers *alifcopen.ListCustomDomainsHeaders, runtime *util.RuntimeOptions) (_result *alifcopen.ListCustomDomainsResponse, _err error) {
|
||||
_err = util.ValidateModel(request)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
query := map[string]interface{}{}
|
||||
|
||||
if !tea.BoolValue(util.IsUnset(request.Limit)) {
|
||||
query["limit"] = request.Limit
|
||||
}
|
||||
|
||||
if !tea.BoolValue(util.IsUnset(request.NextToken)) {
|
||||
query["nextToken"] = request.NextToken
|
||||
}
|
||||
|
||||
if !tea.BoolValue(util.IsUnset(request.Prefix)) {
|
||||
query["prefix"] = request.Prefix
|
||||
}
|
||||
|
||||
if !tea.BoolValue(util.IsUnset(request.StartKey)) {
|
||||
query["startKey"] = request.StartKey
|
||||
}
|
||||
|
||||
realHeaders := make(map[string]*string)
|
||||
if !tea.BoolValue(util.IsUnset(headers.CommonHeaders)) {
|
||||
realHeaders = headers.CommonHeaders
|
||||
}
|
||||
|
||||
if !tea.BoolValue(util.IsUnset(headers.XFcAccountId)) {
|
||||
realHeaders["X-Fc-Account-Id"] = util.ToJSONString(headers.XFcAccountId)
|
||||
}
|
||||
|
||||
if !tea.BoolValue(util.IsUnset(headers.XFcDate)) {
|
||||
realHeaders["X-Fc-Date"] = util.ToJSONString(headers.XFcDate)
|
||||
}
|
||||
|
||||
if !tea.BoolValue(util.IsUnset(headers.XFcTraceId)) {
|
||||
realHeaders["X-Fc-Trace-Id"] = util.ToJSONString(headers.XFcTraceId)
|
||||
}
|
||||
|
||||
req := &openapi.OpenApiRequest{
|
||||
Headers: realHeaders,
|
||||
Query: openapiutil.Query(query),
|
||||
}
|
||||
params := &openapi.Params{
|
||||
Action: tea.String("ListCustomDomains"),
|
||||
Version: tea.String("2021-04-06"),
|
||||
Protocol: tea.String("HTTPS"),
|
||||
Pathname: tea.String("/2021-04-06/custom-domains"),
|
||||
Method: tea.String("GET"),
|
||||
AuthType: tea.String("AK"),
|
||||
Style: tea.String("ROA"),
|
||||
ReqBodyType: tea.String("json"),
|
||||
BodyType: tea.String("json"),
|
||||
}
|
||||
_result = &alifcopen.ListCustomDomainsResponse{}
|
||||
_body, _err := client.CallApi(params, req, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_err = tea.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *FcopenClient) UpdateCustomDomain(domainName *string, request *alifcopen.UpdateCustomDomainRequest) (_result *alifcopen.UpdateCustomDomainResponse, _err error) {
|
||||
runtime := &util.RuntimeOptions{}
|
||||
headers := &alifcopen.UpdateCustomDomainHeaders{}
|
||||
|
||||
@@ -119,8 +119,8 @@ func (d *SSLDeployerProvider) deployToAccelerator(ctx context.Context, cloudCert
|
||||
// 查询 HTTPS 监听列表
|
||||
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlisteners
|
||||
listenerIds := make([]string, 0)
|
||||
listListenersPageNumber := int32(1)
|
||||
listListenersPageSize := int32(50)
|
||||
listListenersPageNumber := 1
|
||||
listListenersPageSize := 50
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -131,8 +131,8 @@ func (d *SSLDeployerProvider) deployToAccelerator(ctx context.Context, cloudCert
|
||||
listListenersReq := &aliga.ListListenersRequest{
|
||||
RegionId: tea.String("cn-hangzhou"),
|
||||
AcceleratorId: tea.String(d.config.AcceleratorId),
|
||||
PageNumber: tea.Int32(listListenersPageNumber),
|
||||
PageSize: tea.Int32(listListenersPageSize),
|
||||
PageNumber: tea.Int32(int32(listListenersPageNumber)),
|
||||
PageSize: tea.Int32(int32(listListenersPageSize)),
|
||||
}
|
||||
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
|
||||
d.logger.Debug("sdk request 'ga.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||
@@ -140,19 +140,21 @@ func (d *SSLDeployerProvider) deployToAccelerator(ctx context.Context, cloudCert
|
||||
return fmt.Errorf("failed to execute sdk request 'ga.ListListeners': %w", err)
|
||||
}
|
||||
|
||||
if listListenersResp.Body.Listeners != nil {
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
if strings.EqualFold(tea.StringValue(listener.Protocol), "https") {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
if listListenersResp.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
if strings.EqualFold(tea.StringValue(listener.Protocol), "https") {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
}
|
||||
|
||||
if len(listListenersResp.Body.Listeners) < int(listListenersPageSize) {
|
||||
if len(listListenersResp.Body.Listeners) < listListenersPageSize {
|
||||
break
|
||||
} else {
|
||||
listListenersPageNumber++
|
||||
}
|
||||
|
||||
listListenersPageNumber++
|
||||
}
|
||||
|
||||
// 遍历更新监听证书
|
||||
@@ -200,9 +202,9 @@ func (d *SSLDeployerProvider) deployToListener(ctx context.Context, cloudCertId
|
||||
func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, cloudAcceleratorId string, cloudListenerId string, cloudCertId string) error {
|
||||
// 查询监听绑定的证书列表
|
||||
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlistenercertificates
|
||||
var listenerDefaultCertificate *aliga.ListListenerCertificatesResponseBodyCertificates
|
||||
var listenerAdditionalCertificates []*aliga.ListListenerCertificatesResponseBodyCertificates = make([]*aliga.ListListenerCertificatesResponseBodyCertificates, 0)
|
||||
var listListenerCertificatesNextToken *string
|
||||
listenerDefaultCertificate := (*aliga.ListListenerCertificatesResponseBodyCertificates)(nil)
|
||||
listenerAdditionalCertificates := make([]*aliga.ListListenerCertificatesResponseBodyCertificates, 0)
|
||||
listListenerCertificatesNextToken := (*string)(nil)
|
||||
for {
|
||||
listListenerCertificatesReq := &aliga.ListListenerCertificatesRequest{
|
||||
RegionId: tea.String("cn-hangzhou"),
|
||||
@@ -217,21 +219,23 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
return fmt.Errorf("failed to execute sdk request 'ga.ListListenerCertificates': %w", err)
|
||||
}
|
||||
|
||||
if listListenerCertificatesResp.Body.Certificates != nil {
|
||||
for _, certificate := range listListenerCertificatesResp.Body.Certificates {
|
||||
if tea.BoolValue(certificate.IsDefault) {
|
||||
listenerDefaultCertificate = certificate
|
||||
} else {
|
||||
listenerAdditionalCertificates = append(listenerAdditionalCertificates, certificate)
|
||||
}
|
||||
if listListenerCertificatesResp.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, certItem := range listListenerCertificatesResp.Body.Certificates {
|
||||
if tea.BoolValue(certItem.IsDefault) {
|
||||
listenerDefaultCertificate = certItem
|
||||
} else {
|
||||
listenerAdditionalCertificates = append(listenerAdditionalCertificates, certItem)
|
||||
}
|
||||
}
|
||||
|
||||
if listListenerCertificatesResp.Body.NextToken == nil {
|
||||
if len(listListenerCertificatesResp.Body.Certificates) == 0 || listListenerCertificatesResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenerCertificatesNextToken = listListenerCertificatesResp.Body.NextToken
|
||||
}
|
||||
|
||||
listListenerCertificatesNextToken = listListenerCertificatesResp.Body.NextToken
|
||||
}
|
||||
|
||||
if d.config.Domain == "" {
|
||||
|
||||
@@ -12,9 +12,12 @@ import (
|
||||
alilive "github.com/alibabacloud-go/live-20161101/v2/client"
|
||||
"github.com/alibabacloud-go/tea/dara"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/aliyun-live/internal"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -26,6 +29,9 @@ type SSLDeployerProviderConfig struct {
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 直播流域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -64,13 +70,141 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// "*.example.com" → ".example.com",适配阿里云 Live 要求的泛域名格式
|
||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||
domains = []string{domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain) ||
|
||||
strings.TrimPrefix(d.config.Domain, "*") == strings.TrimPrefix(domain, "*")
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil ||
|
||||
strings.TrimPrefix(d.config.Domain, "*") == strings.TrimPrefix(domain, "*")
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// "*.example.com" → ".example.com",适配阿里云 Live 要求的泛域名格式
|
||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no live domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found live domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
if err := d.updateDomainCertificate(ctx, domain, certPEM, privkeyPEM); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getAllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询用户名下所有的直播域名
|
||||
// REF: https://help.aliyun.com/zh/live/developer-reference/api-live-2016-11-01-describeliveuserdomains
|
||||
describeUserLiveDomainsPageNumber := 1
|
||||
describeUserLiveDomainsPageSize := 50
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
describeUserLiveDomainsReq := &alilive.DescribeLiveUserDomainsRequest{
|
||||
ResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId),
|
||||
RegionName: tea.String(d.config.Region),
|
||||
DomainStatus: tea.String("online"),
|
||||
PageNumber: tea.Int32(int32(describeUserLiveDomainsPageNumber)),
|
||||
PageSize: tea.Int32(int32(describeUserLiveDomainsPageSize)),
|
||||
}
|
||||
describeUserLiveDomainsResp, err := d.sdkClient.DescribeLiveUserDomainsWithContext(ctx, describeUserLiveDomainsReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'live.DescribeLiveUserDomains'", slog.Any("request", describeUserLiveDomainsReq), slog.Any("response", describeUserLiveDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'live.DescribeLiveUserDomains': %w", err)
|
||||
}
|
||||
|
||||
if describeUserLiveDomainsResp.Body == nil || describeUserLiveDomainsResp.Body.Domains == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, domainItem := range describeUserLiveDomainsResp.Body.Domains.PageData {
|
||||
domains = append(domains, tea.StringValue(domainItem.DomainName))
|
||||
}
|
||||
|
||||
if len(describeUserLiveDomainsResp.Body.Domains.PageData) < describeUserLiveDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
describeUserLiveDomainsPageNumber++
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, certPEM, privkeyPEM string) error {
|
||||
// 设置域名证书
|
||||
// REF: https://help.aliyun.com/zh/live/developer-reference/api-live-2016-11-01-setlivedomaincertificate
|
||||
setLiveDomainSSLCertificateReq := &alilive.SetLiveDomainCertificateRequest{
|
||||
@@ -81,13 +215,13 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
SSLPub: tea.String(certPEM),
|
||||
SSLPri: tea.String(privkeyPEM),
|
||||
}
|
||||
setLiveDomainSSLCertificateResp, err := d.sdkClient.SetLiveDomainCertificateWithContext(context.TODO(), setLiveDomainSSLCertificateReq, &dara.RuntimeOptions{})
|
||||
setLiveDomainSSLCertificateResp, err := d.sdkClient.SetLiveDomainCertificateWithContext(ctx, setLiveDomainSSLCertificateReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'live.SetLiveDomainCertificate'", slog.Any("request", setLiveDomainSSLCertificateReq), slog.Any("response", setLiveDomainSSLCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'live.SetLiveDomainCertificate': %w", err)
|
||||
return fmt.Errorf("failed to execute sdk request 'live.SetLiveDomainCertificate': %w", err)
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, accessKeySecret, region string) (*internal.LiveClient, error) {
|
||||
|
||||
@@ -57,10 +57,11 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package aliyunlive
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -35,6 +35,80 @@ func (client *LiveClient) Init(config *openapiutil.Config) (_err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *LiveClient) DescribeLiveUserDomainsWithContext(ctx context.Context, request *alilive.DescribeLiveUserDomainsRequest, runtime *dara.RuntimeOptions) (_result *alilive.DescribeLiveUserDomainsResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
query := map[string]interface{}{}
|
||||
|
||||
if !dara.IsNil(request.DomainName) {
|
||||
query["DomainName"] = request.DomainName
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainSearchType) {
|
||||
query["DomainSearchType"] = request.DomainSearchType
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainStatus) {
|
||||
query["DomainStatus"] = request.DomainStatus
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.LiveDomainType) {
|
||||
query["LiveDomainType"] = request.LiveDomainType
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.OwnerId) {
|
||||
query["OwnerId"] = request.OwnerId
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.PageNumber) {
|
||||
query["PageNumber"] = request.PageNumber
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.PageSize) {
|
||||
query["PageSize"] = request.PageSize
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.RegionName) {
|
||||
query["RegionName"] = request.RegionName
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.ResourceGroupId) {
|
||||
query["ResourceGroupId"] = request.ResourceGroupId
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.SecurityToken) {
|
||||
query["SecurityToken"] = request.SecurityToken
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.Tag) {
|
||||
query["Tag"] = request.Tag
|
||||
}
|
||||
|
||||
req := &openapiutil.OpenApiRequest{
|
||||
Query: openapiutil.Query(query),
|
||||
}
|
||||
params := &openapiutil.Params{
|
||||
Action: dara.String("DescribeLiveUserDomains"),
|
||||
Version: dara.String("2016-11-01"),
|
||||
Protocol: dara.String("HTTPS"),
|
||||
Pathname: dara.String("/"),
|
||||
Method: dara.String("POST"),
|
||||
AuthType: dara.String("AK"),
|
||||
Style: dara.String("RPC"),
|
||||
ReqBodyType: dara.String("formData"),
|
||||
BodyType: dara.String("json"),
|
||||
}
|
||||
_result = &alilive.DescribeLiveUserDomainsResponse{}
|
||||
_body, _err := client.CallApiWithCtx(ctx, params, req, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_err = dara.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *LiveClient) SetLiveDomainCertificateWithContext(ctx context.Context, request *alilive.SetLiveDomainCertificateRequest, runtime *dara.RuntimeOptions) (_result *alilive.SetLiveDomainCertificateResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
|
||||
@@ -124,7 +124,7 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
getLoadBalancerAttributeReq := &alinlb.GetLoadBalancerAttributeRequest{
|
||||
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
||||
}
|
||||
getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttributeWithContext(context.TODO(), getLoadBalancerAttributeReq, &dara.RuntimeOptions{})
|
||||
getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttributeWithContext(ctx, getLoadBalancerAttributeReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'nlb.GetLoadBalancerAttribute'", slog.Any("request", getLoadBalancerAttributeReq), slog.Any("response", getLoadBalancerAttributeResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'nlb.GetLoadBalancerAttribute': %w", err)
|
||||
@@ -133,8 +133,7 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
// 查询 TCPSSL 监听列表
|
||||
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-listlisteners
|
||||
listenerIds := make([]string, 0)
|
||||
listListenersLimit := int32(100)
|
||||
var listListenersToken *string = nil
|
||||
listListenersToken := (*string)(nil)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -143,28 +142,30 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
}
|
||||
|
||||
listListenersReq := &alinlb.ListListenersRequest{
|
||||
MaxResults: tea.Int32(listListenersLimit),
|
||||
NextToken: listListenersToken,
|
||||
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
||||
MaxResults: tea.Int32(100),
|
||||
LoadBalancerIds: tea.StringSlice([]string{d.config.LoadbalancerId}),
|
||||
ListenerProtocol: tea.String("TCPSSL"),
|
||||
}
|
||||
listListenersResp, err := d.sdkClient.ListListenersWithContext(context.TODO(), listListenersReq, &dara.RuntimeOptions{})
|
||||
listListenersResp, err := d.sdkClient.ListListenersWithContext(ctx, listListenersReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'nlb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'nlb.ListListeners': %w", err)
|
||||
}
|
||||
|
||||
if listListenersResp.Body.Listeners != nil {
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
if listListenersResp.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
|
||||
if len(listListenersResp.Body.Listeners) == 0 || listListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
}
|
||||
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
}
|
||||
|
||||
// 遍历更新监听证书
|
||||
@@ -212,7 +213,7 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
getListenerAttributeReq := &alinlb.GetListenerAttributeRequest{
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
}
|
||||
getListenerAttributeResp, err := d.sdkClient.GetListenerAttributeWithContext(context.TODO(), getListenerAttributeReq, &dara.RuntimeOptions{})
|
||||
getListenerAttributeResp, err := d.sdkClient.GetListenerAttributeWithContext(ctx, getListenerAttributeReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'nlb.GetListenerAttribute'", slog.Any("request", getListenerAttributeReq), slog.Any("response", getListenerAttributeResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'nlb.GetListenerAttribute': %w", err)
|
||||
@@ -224,7 +225,7 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
CertificateIds: []*string{tea.String(cloudCertId)},
|
||||
}
|
||||
updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttributeWithContext(context.TODO(), updateListenerAttributeReq, &dara.RuntimeOptions{})
|
||||
updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttributeWithContext(ctx, updateListenerAttributeReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'nlb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerAttributeResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'nlb.UpdateListenerAttribute': %w", err)
|
||||
|
||||
@@ -82,7 +82,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
},
|
||||
},
|
||||
}
|
||||
putCnameResp, err := d.sdkClient.PutCname(context.TODO(), putCnameReq)
|
||||
putCnameResp, err := d.sdkClient.PutCname(ctx, putCnameReq)
|
||||
d.logger.Debug("sdk request 'oss.PutCname'", slog.Any("request", putCnameReq), slog.Any("response", putCnameResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'oss.PutCname': %w", err)
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/aliyun-vod/internal"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -28,7 +30,10 @@ type SSLDeployerProviderConfig struct {
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 点播加速域名(不支持泛域名)。
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 点播加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
@@ -80,38 +85,155 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no vod domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
d.logger.Info("found vod domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
if err := d.updateDomainCertificate(ctx, domain, certPEM, privkeyPEM); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getAllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询加速域名列表
|
||||
// REF: https://help.aliyun.com/zh/live/developer-reference/api-live-2016-11-01-describeliveuserdomains
|
||||
describeVodUserDomainsPageNumber := 1
|
||||
describeVodUserDomainsPageSize := 50
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
describeVodUserDomainsReq := &alivod.DescribeVodUserDomainsRequest{
|
||||
DomainStatus: tea.String("online"),
|
||||
PageNumber: tea.Int32(int32(describeVodUserDomainsPageNumber)),
|
||||
PageSize: tea.Int32(int32(describeVodUserDomainsPageSize)),
|
||||
}
|
||||
describeVodUserDomainsResp, err := d.sdkClient.DescribeVodUserDomainsWithContext(ctx, describeVodUserDomainsReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'vod.DescribeVodUserDomains'", slog.Any("request", describeVodUserDomainsReq), slog.Any("response", describeVodUserDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'vod.DescribeLiveUserDomains': %w", err)
|
||||
}
|
||||
|
||||
if describeVodUserDomainsResp.Body == nil || describeVodUserDomainsResp.Body.Domains == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, domainItem := range describeVodUserDomainsResp.Body.Domains.PageData {
|
||||
domains = append(domains, tea.StringValue(domainItem.DomainName))
|
||||
}
|
||||
|
||||
if len(describeVodUserDomainsResp.Body.Domains.PageData) < describeVodUserDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
describeVodUserDomainsPageNumber++
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, cloudCertId, cloudCertName string) error {
|
||||
// 设置域名证书
|
||||
// REF: https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-setvoddomainsslcertificate
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
certId, _ := strconv.ParseInt(cloudCertId, 10, 64)
|
||||
setVodDomainSSLCertificateReq := &alivod.SetVodDomainSSLCertificateRequest{
|
||||
DomainName: tea.String(d.config.Domain),
|
||||
DomainName: tea.String(domain),
|
||||
CertType: tea.String("cas"),
|
||||
CertId: tea.Int64(certId),
|
||||
CertName: tea.String(upres.CertName),
|
||||
CertName: tea.String(cloudCertName),
|
||||
CertRegion: lo.
|
||||
If(d.config.Region == "" || strings.HasPrefix(d.config.Region, "cn-"), tea.String("cn-hangzhou")).
|
||||
Else(tea.String("ap-southeast-1")),
|
||||
SSLProtocol: tea.String("on"),
|
||||
}
|
||||
setVodDomainSSLCertificateResp, err := d.sdkClient.SetVodDomainSSLCertificateWithContext(context.TODO(), setVodDomainSSLCertificateReq, &dara.RuntimeOptions{})
|
||||
setVodDomainSSLCertificateResp, err := d.sdkClient.SetVodDomainSSLCertificateWithContext(ctx, setVodDomainSSLCertificateReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'live.SetVodDomainSSLCertificate'", slog.Any("request", setVodDomainSSLCertificateReq), slog.Any("response", setVodDomainSSLCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'live.SetVodDomainSSLCertificate': %w", err)
|
||||
return fmt.Errorf("failed to execute sdk request 'live.SetVodDomainSSLCertificate': %w", err)
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, accessKeySecret, region string) (*internal.VodClient, error) {
|
||||
|
||||
@@ -57,10 +57,11 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package aliyunvod
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -35,6 +35,68 @@ func (client *VodClient) Init(config *openapiutil.Config) (_err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *VodClient) DescribeVodUserDomainsWithContext(ctx context.Context, request *alivod.DescribeVodUserDomainsRequest, runtime *dara.RuntimeOptions) (_result *alivod.DescribeVodUserDomainsResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
query := map[string]interface{}{}
|
||||
|
||||
if !dara.IsNil(request.DomainName) {
|
||||
query["DomainName"] = request.DomainName
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainSearchType) {
|
||||
query["DomainSearchType"] = request.DomainSearchType
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.DomainStatus) {
|
||||
query["DomainStatus"] = request.DomainStatus
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.OwnerId) {
|
||||
query["OwnerId"] = request.OwnerId
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.PageNumber) {
|
||||
query["PageNumber"] = request.PageNumber
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.PageSize) {
|
||||
query["PageSize"] = request.PageSize
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.SecurityToken) {
|
||||
query["SecurityToken"] = request.SecurityToken
|
||||
}
|
||||
|
||||
if !dara.IsNil(request.Tag) {
|
||||
query["Tag"] = request.Tag
|
||||
}
|
||||
|
||||
req := &openapiutil.OpenApiRequest{
|
||||
Query: openapiutil.Query(query),
|
||||
}
|
||||
params := &openapiutil.Params{
|
||||
Action: dara.String("DescribeVodUserDomains"),
|
||||
Version: dara.String("2017-03-21"),
|
||||
Protocol: dara.String("HTTPS"),
|
||||
Pathname: dara.String("/"),
|
||||
Method: dara.String("POST"),
|
||||
AuthType: dara.String("AK"),
|
||||
Style: dara.String("RPC"),
|
||||
ReqBodyType: dara.String("formData"),
|
||||
BodyType: dara.String("json"),
|
||||
}
|
||||
_result = &alivod.DescribeVodUserDomainsResponse{}
|
||||
_body, _err := client.CallApiWithCtx(ctx, params, req, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_err = dara.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *VodClient) SetVodDomainSSLCertificateWithContext(ctx context.Context, request *alivod.SetVodDomainSSLCertificateRequest, runtime *dara.RuntimeOptions) (_result *alivod.SetVodDomainSSLCertificateResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
|
||||
@@ -118,10 +118,10 @@ func (d *SSLDeployerProvider) deployToWAF3(ctx context.Context, certPEM string,
|
||||
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-describedefaulthttps
|
||||
describeDefaultHttpsReq := &aliwaf.DescribeDefaultHttpsRequest{
|
||||
ResourceManagerResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId),
|
||||
InstanceId: tea.String(d.config.InstanceId),
|
||||
RegionId: tea.String(d.config.Region),
|
||||
InstanceId: tea.String(d.config.InstanceId),
|
||||
}
|
||||
describeDefaultHttpsResp, err := d.sdkClient.DescribeDefaultHttpsWithContext(context.TODO(), describeDefaultHttpsReq, &dara.RuntimeOptions{})
|
||||
describeDefaultHttpsResp, err := d.sdkClient.DescribeDefaultHttpsWithContext(ctx, describeDefaultHttpsReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'waf.DescribeDefaultHttps'", slog.Any("request", describeDefaultHttpsReq), slog.Any("response", describeDefaultHttpsResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'waf.DescribeDefaultHttps': %w", err)
|
||||
@@ -131,8 +131,8 @@ func (d *SSLDeployerProvider) deployToWAF3(ctx context.Context, certPEM string,
|
||||
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-modifydefaulthttps
|
||||
modifyDefaultHttpsReq := &aliwaf.ModifyDefaultHttpsRequest{
|
||||
ResourceManagerResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId),
|
||||
InstanceId: tea.String(d.config.InstanceId),
|
||||
RegionId: tea.String(d.config.Region),
|
||||
InstanceId: tea.String(d.config.InstanceId),
|
||||
CertId: tea.String(upres.ExtendedData["CertIdentifier"].(string)),
|
||||
TLSVersion: tea.String("tlsv1"),
|
||||
EnableTLSv3: tea.Bool(true),
|
||||
@@ -145,7 +145,7 @@ func (d *SSLDeployerProvider) deployToWAF3(ctx context.Context, certPEM string,
|
||||
modifyDefaultHttpsReq.EnableTLSv3 = describeDefaultHttpsResp.Body.DefaultHttps.EnableTLSv3
|
||||
}
|
||||
}
|
||||
modifyDefaultHttpsResp, err := d.sdkClient.ModifyDefaultHttpsWithContext(context.TODO(), modifyDefaultHttpsReq, &dara.RuntimeOptions{})
|
||||
modifyDefaultHttpsResp, err := d.sdkClient.ModifyDefaultHttpsWithContext(ctx, modifyDefaultHttpsReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'waf.ModifyDefaultHttps'", slog.Any("request", modifyDefaultHttpsReq), slog.Any("response", modifyDefaultHttpsResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'waf.ModifyDefaultHttps': %w", err)
|
||||
@@ -156,11 +156,11 @@ func (d *SSLDeployerProvider) deployToWAF3(ctx context.Context, certPEM string,
|
||||
// 查询 CNAME 接入详情
|
||||
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-describedomaindetail
|
||||
describeDomainDetailReq := &aliwaf.DescribeDomainDetailRequest{
|
||||
InstanceId: tea.String(d.config.InstanceId),
|
||||
RegionId: tea.String(d.config.Region),
|
||||
InstanceId: tea.String(d.config.InstanceId),
|
||||
Domain: tea.String(d.config.Domain),
|
||||
}
|
||||
describeDomainDetailResp, err := d.sdkClient.DescribeDomainDetailWithContext(context.TODO(), describeDomainDetailReq, &dara.RuntimeOptions{})
|
||||
describeDomainDetailResp, err := d.sdkClient.DescribeDomainDetailWithContext(ctx, describeDomainDetailReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'waf.DescribeDomainDetail'", slog.Any("request", describeDomainDetailReq), slog.Any("response", describeDomainDetailResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'waf.DescribeDomainDetail': %w", err)
|
||||
@@ -169,14 +169,14 @@ func (d *SSLDeployerProvider) deployToWAF3(ctx context.Context, certPEM string,
|
||||
// 修改 CNAME 接入资源
|
||||
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-modifydomain
|
||||
modifyDomainReq := &aliwaf.ModifyDomainRequest{
|
||||
InstanceId: tea.String(d.config.InstanceId),
|
||||
RegionId: tea.String(d.config.Region),
|
||||
InstanceId: tea.String(d.config.InstanceId),
|
||||
Domain: tea.String(d.config.Domain),
|
||||
Listen: &aliwaf.ModifyDomainRequestListen{CertId: tea.String(upres.ExtendedData["CertIdentifier"].(string))},
|
||||
Redirect: &aliwaf.ModifyDomainRequestRedirect{Loadbalance: tea.String("iphash")},
|
||||
}
|
||||
modifyDomainReq = assign(modifyDomainReq, describeDomainDetailResp.Body)
|
||||
modifyDomainResp, err := d.sdkClient.ModifyDomainWithContext(context.TODO(), modifyDomainReq, &dara.RuntimeOptions{})
|
||||
modifyDomainReq = _assign(modifyDomainReq, describeDomainDetailResp.Body)
|
||||
modifyDomainResp, err := d.sdkClient.ModifyDomainWithContext(ctx, modifyDomainReq, &dara.RuntimeOptions{})
|
||||
d.logger.Debug("sdk request 'waf.ModifyDomain'", slog.Any("request", modifyDomainReq), slog.Any("response", modifyDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'waf.ModifyDomain': %w", err)
|
||||
@@ -210,7 +210,7 @@ func createSDKClient(accessKeyId, accessKeySecret, region string) (*internal.Waf
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func assign(source *aliwaf.ModifyDomainRequest, target *aliwaf.DescribeDomainDetailResponseBody) *aliwaf.ModifyDomainRequest {
|
||||
func _assign(source *aliwaf.ModifyDomainRequest, target *aliwaf.DescribeDomainDetailResponseBody) *aliwaf.ModifyDomainRequest {
|
||||
// `ModifyDomain` 中不传的字段表示使用默认值、而非保留原值,
|
||||
// 因此这里需要把原配置中的参数重新赋值回去。
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
CertificateChain: ([]byte)(intermediaCertPEM),
|
||||
PrivateKey: ([]byte)(privkeyPEM),
|
||||
}
|
||||
importCertificateResp, err := d.sdkClient.ImportCertificate(context.TODO(), importCertificateReq)
|
||||
importCertificateResp, err := d.sdkClient.ImportCertificate(ctx, importCertificateReq)
|
||||
d.logger.Debug("sdk request 'acm.ImportCertificate'", slog.Any("request", importCertificateReq), slog.Any("response", importCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'acm.ImportCertificate': %w", err)
|
||||
|
||||
@@ -113,7 +113,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
getDistributionConfigReq := &cloudfront.GetDistributionConfigInput{
|
||||
Id: aws.String(d.config.DistributionId),
|
||||
}
|
||||
getDistributionConfigResp, err := d.sdkClient.GetDistributionConfig(context.TODO(), getDistributionConfigReq)
|
||||
getDistributionConfigResp, err := d.sdkClient.GetDistributionConfig(ctx, getDistributionConfigReq)
|
||||
d.logger.Debug("sdk request 'cloudfront.GetDistributionConfig'", slog.Any("request", getDistributionConfigReq), slog.Any("response", getDistributionConfigResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cloudfront.GetDistributionConfig': %w", err)
|
||||
@@ -145,7 +145,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate.SSLSupportMethod = types.SSLSupportMethodSniOnly
|
||||
}
|
||||
}
|
||||
updateDistributionResp, err := d.sdkClient.UpdateDistribution(context.TODO(), updateDistributionReq)
|
||||
updateDistributionResp, err := d.sdkClient.UpdateDistribution(ctx, updateDistributionReq)
|
||||
d.logger.Debug("sdk request 'cloudfront.UpdateDistribution'", slog.Any("request", updateDistributionReq), slog.Any("response", updateDistributionResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cloudfront.UpdateDistribution': %w", err)
|
||||
|
||||
@@ -106,7 +106,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
} else {
|
||||
// 获取证书
|
||||
// REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/get-certificate/get-certificate
|
||||
getCertificateResp, err := d.sdkClient.GetCertificate(context.TODO(), d.config.CertificateName, "", nil)
|
||||
getCertificateResp, err := d.sdkClient.GetCertificate(ctx, d.config.CertificateName, "", nil)
|
||||
d.logger.Debug("sdk request 'keyvault.GetCertificate'", slog.String("request.certificateName", d.config.CertificateName), slog.Any("response", getCertificateResp))
|
||||
if err != nil {
|
||||
var respErr *azcore.ResponseError
|
||||
@@ -134,7 +134,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
"certimate/cert-sn": to.Ptr(certX509.SerialNumber.Text(16)),
|
||||
},
|
||||
}
|
||||
importCertificateResp, err := d.sdkClient.ImportCertificate(context.TODO(), d.config.CertificateName, importCertificateParams, nil)
|
||||
importCertificateResp, err := d.sdkClient.ImportCertificate(ctx, d.config.CertificateName, importCertificateParams, nil)
|
||||
d.logger.Debug("sdk request 'keyvault.ImportCertificate'", slog.String("request.certificateName", d.config.CertificateName), slog.Any("request.parameters", importCertificateParams), slog.Any("response", importCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'keyvault.ImportCertificate': %w", err)
|
||||
|
||||
@@ -255,7 +255,7 @@ func (d *SSLDeployerProvider) updateHttpsListenerCertificate(ctx context.Context
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'appblb.DescribeAppHTTPSListeners': %w", err)
|
||||
} else if len(describeAppHTTPSListenersResp.ListenerList) == 0 {
|
||||
return fmt.Errorf("listener %s:%d not found", cloudLoadbalancerId, cloudHttpsListenerPort)
|
||||
return fmt.Errorf("cloud not find listener '%s:%d'", cloudLoadbalancerId, cloudHttpsListenerPort)
|
||||
}
|
||||
|
||||
if d.config.Domain == "" {
|
||||
|
||||
@@ -255,7 +255,7 @@ func (d *SSLDeployerProvider) updateHttpsListenerCertificate(ctx context.Context
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'blb.DescribeHTTPSListeners': %w", err)
|
||||
} else if len(describeHTTPSListenersResp.ListenerList) == 0 {
|
||||
return fmt.Errorf("listener %s:%d not found", cloudLoadbalancerId, cloudHttpsListenerPort)
|
||||
return fmt.Errorf("could not find listener '%s:%d'", cloudLoadbalancerId, cloudHttpsListenerPort)
|
||||
}
|
||||
|
||||
if d.config.Domain == "" {
|
||||
|
||||
@@ -5,11 +5,16 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
bcecdn "github.com/baidubce/bce-sdk-go/services/cdn"
|
||||
bcecdnapi "github.com/baidubce/bce-sdk-go/services/cdn/api"
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/samber/lo"
|
||||
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -17,6 +22,9 @@ type SSLDeployerProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 百度智能云 SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -55,14 +63,127 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no cdn domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found cdn domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
if err := d.updateDomainCertificate(ctx, domain, certPEM, privkeyPEM); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getAllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询域名列表
|
||||
// REF: https://cloud.baidu.com/doc/CDN/s/sjwvyewt1
|
||||
listDomainsMarker := ""
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listDomainsRespDomains, listDomainsNextMarker, err := d.sdkClient.ListDomains(listDomainsMarker)
|
||||
d.logger.Debug("sdk request 'cdn.ListDomains'", slog.String("request.marker", listDomainsMarker), slog.Any("response.domains", listDomainsRespDomains))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.ListDomains': %w", err)
|
||||
}
|
||||
|
||||
domains = append(domains, listDomainsRespDomains...)
|
||||
|
||||
if listDomainsNextMarker == "" {
|
||||
break
|
||||
}
|
||||
|
||||
listDomainsMarker = listDomainsNextMarker
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, certPEM, privkeyPEM string) error {
|
||||
// 修改域名证书
|
||||
// REF: https://cloud.baidu.com/doc/CDN/s/qjzuz2hp8
|
||||
putCertResp, err := d.sdkClient.PutCert(
|
||||
d.config.Domain,
|
||||
domain,
|
||||
&bcecdnapi.UserCertificate{
|
||||
CertName: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()),
|
||||
ServerData: certPEM,
|
||||
@@ -70,12 +191,12 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
},
|
||||
"ON",
|
||||
)
|
||||
d.logger.Debug("sdk request 'cdn.PutCert'", slog.String("request.domain", d.config.Domain), slog.Any("response", putCertResp))
|
||||
d.logger.Debug("sdk request 'cdn.PutCert'", slog.String("request.domain", domain), slog.Any("response", putCertResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.PutCert': %w", err)
|
||||
return fmt.Errorf("failed to execute sdk request 'cdn.PutCert': %w", err)
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, secretAccessKey string) (*bcecdn.Client, error) {
|
||||
|
||||
@@ -53,9 +53,10 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package baiducloudcdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -19,6 +19,9 @@ import (
|
||||
type SSLDeployerProviderConfig struct {
|
||||
// 白山云 API Token。
|
||||
ApiToken string `json:"apiToken"`
|
||||
// 域名匹配模式。暂时只支持精确匹配。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
// 证书 ID。
|
||||
@@ -103,7 +106,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'baishan.GetDomainConfig': %w", err)
|
||||
} else if len(getDomainConfigResp.Data) == 0 {
|
||||
return nil, fmt.Errorf("domain %s not found", d.config.Domain)
|
||||
return nil, fmt.Errorf("could not find domain '%s'", d.config.Domain)
|
||||
}
|
||||
|
||||
// 设置域名配置
|
||||
|
||||
@@ -49,8 +49,9 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
ApiToken: fApiToken,
|
||||
Domain: fDomain,
|
||||
ApiToken: fApiToken,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package baishancdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
)
|
||||
@@ -74,48 +74,10 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'bt.PanelGetConfig': %w", err)
|
||||
}
|
||||
|
||||
// 遍历查询网站列表,获取网站 ID
|
||||
var siteId int32
|
||||
datalistGetDataListPage := int32(1)
|
||||
datalistGetDataListLimit := int32(10)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
datalistGetDataListReq := &btsdk.DatalistGetDataListRequest{
|
||||
Table: lo.ToPtr("sites"),
|
||||
SearchString: lo.ToPtr(d.config.SiteName),
|
||||
Page: lo.ToPtr(datalistGetDataListPage),
|
||||
Limit: lo.ToPtr(datalistGetDataListLimit),
|
||||
}
|
||||
datalistGetDataListResp, err := d.sdkClient.DatalistGetDataList(datalistGetDataListReq)
|
||||
d.logger.Debug("sdk request 'bt.DatalistGetDataList'", slog.Any("request", datalistGetDataListReq), slog.Any("response", datalistGetDataListResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'bt.DatalistGetDataList': %w", err)
|
||||
}
|
||||
|
||||
for _, siteInfo := range datalistGetDataListResp.Data {
|
||||
if strings.EqualFold(siteInfo.Name, d.config.SiteName) {
|
||||
siteId = siteInfo.Id
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if siteId != 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if len(datalistGetDataListResp.Data) < int(datalistGetDataListLimit) {
|
||||
break
|
||||
} else {
|
||||
datalistGetDataListPage++
|
||||
}
|
||||
}
|
||||
if siteId == 0 {
|
||||
return nil, errors.New("website not found")
|
||||
// 获取网站 ID
|
||||
siteId, err := d.findSiteIdByName(ctx, d.config.SiteName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if panelGetConfigResp.Site != nil && strings.EqualFold(panelGetConfigResp.Site.WebServer, "iis") {
|
||||
@@ -174,6 +136,45 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) findSiteIdByName(ctx context.Context, siteName string) (int32, error) {
|
||||
// 查询网站列表
|
||||
datalistGetDataListPage := 1
|
||||
datalistGetDataListLimit := 10
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
datalistGetDataListReq := &btsdk.DatalistGetDataListRequest{
|
||||
Table: lo.ToPtr("sites"),
|
||||
SearchString: lo.ToPtr(d.config.SiteName),
|
||||
Page: lo.ToPtr(int32(datalistGetDataListPage)),
|
||||
Limit: lo.ToPtr(int32(datalistGetDataListLimit)),
|
||||
}
|
||||
datalistGetDataListResp, err := d.sdkClient.DatalistGetDataList(datalistGetDataListReq)
|
||||
d.logger.Debug("sdk request 'bt.DatalistGetDataList'", slog.Any("request", datalistGetDataListReq), slog.Any("response", datalistGetDataListResp))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to execute sdk request 'bt.DatalistGetDataList': %w", err)
|
||||
}
|
||||
|
||||
for _, siteItem := range datalistGetDataListResp.Data {
|
||||
if strings.EqualFold(siteItem.Name, d.config.SiteName) {
|
||||
return siteItem.Id, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(datalistGetDataListResp.Data) < datalistGetDataListLimit {
|
||||
break
|
||||
}
|
||||
|
||||
datalistGetDataListPage++
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("could not find site '%s'", siteName)
|
||||
}
|
||||
|
||||
func createSDKClient(serverUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) {
|
||||
client, err := btsdk.NewClient(serverUrl, apiKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -68,46 +68,10 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.config.SitePort = 443
|
||||
}
|
||||
|
||||
// 遍历获取网站列表,获取网站 ID
|
||||
// REF: https://support.huaweicloud.com/api-waf/ListHost.html
|
||||
siteId := ""
|
||||
getSitListPage := int32(1)
|
||||
getSitListPageSize := int32(100)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
getSiteListReq := &btwafsdk.GetSiteListRequest{
|
||||
SiteName: lo.ToPtr(d.config.SiteName),
|
||||
Page: lo.ToPtr(getSitListPage),
|
||||
PageSize: lo.ToPtr(getSitListPageSize),
|
||||
}
|
||||
getSiteListResp, err := d.sdkClient.GetSiteList(getSiteListReq)
|
||||
d.logger.Debug("sdk request 'bt.GetSiteList'", slog.Any("request", getSiteListReq), slog.Any("response", getSiteListResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'bt.GetSiteList': %w", err)
|
||||
}
|
||||
|
||||
if getSiteListResp.Result != nil && getSiteListResp.Result.List != nil {
|
||||
for _, siteItem := range getSiteListResp.Result.List {
|
||||
if siteItem.SiteName == d.config.SiteName {
|
||||
siteId = siteItem.SiteId
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if getSiteListResp.Result == nil || len(getSiteListResp.Result.List) < int(getSitListPageSize) {
|
||||
break
|
||||
} else {
|
||||
getSitListPage++
|
||||
}
|
||||
}
|
||||
if siteId == "" {
|
||||
return nil, errors.New("site not found")
|
||||
// 获取网站 ID
|
||||
siteId, err := d.findSiteIdByName(ctx, d.config.SiteName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 修改站点配置
|
||||
@@ -132,6 +96,48 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) findSiteIdByName(ctx context.Context, siteName string) (string, error) {
|
||||
// 查询网站列表
|
||||
getSiteListPage := 1
|
||||
getSiteListPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
getSiteListReq := &btwafsdk.GetSiteListRequest{
|
||||
SiteName: lo.ToPtr(d.config.SiteName),
|
||||
Page: lo.ToPtr(int32(getSiteListPage)),
|
||||
PageSize: lo.ToPtr(int32(getSiteListPageSize)),
|
||||
}
|
||||
getSiteListResp, err := d.sdkClient.GetSiteList(getSiteListReq)
|
||||
d.logger.Debug("sdk request 'bt.GetSiteList'", slog.Any("request", getSiteListReq), slog.Any("response", getSiteListResp))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to execute sdk request 'bt.GetSiteList': %w", err)
|
||||
}
|
||||
|
||||
if getSiteListResp.Result == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, siteItem := range getSiteListResp.Result.List {
|
||||
if siteItem.SiteName == d.config.SiteName {
|
||||
return siteItem.SiteId, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(getSiteListResp.Result.List) < getSiteListPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
getSiteListPage++
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not find site '%s'", siteName)
|
||||
}
|
||||
|
||||
func createSDKClient(serverUrl, apiKey string, skipTlsVerify bool) (*btwafsdk.Client, error) {
|
||||
client, err := btwafsdk.NewClient(serverUrl, apiKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -16,7 +16,7 @@ type SSLDeployerProviderConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
// Bunny Pull Zone ID。
|
||||
PullZoneId string `json:"pullZoneId"`
|
||||
// Bunny CDN Hostname(支持泛域名)。
|
||||
// Bunny CDN Hostname。
|
||||
Hostname string `json:"hostname"`
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,11 @@ import (
|
||||
"strings"
|
||||
|
||||
bpcdn "github.com/byteplus-sdk/byteplus-sdk-golang/service/cdn"
|
||||
bp "github.com/volcengine/volcengine-go-sdk/volcengine"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/byteplus-cdn"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -18,6 +20,9 @@ type SSLDeployerProviderConfig struct {
|
||||
AccessKey string `json:"accessKey"`
|
||||
// BytePlus SecretKey。
|
||||
SecretKey string `json:"secretKey"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -75,61 +80,63 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 获取待部署的域名列表
|
||||
domains := make([]string, 0)
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
// 获取指定证书可关联的域名
|
||||
// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-describecertconfig-9ea17
|
||||
describeCertConfigReq := &bpcdn.DescribeCertConfigRequest{
|
||||
CertId: upres.CertId,
|
||||
}
|
||||
describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq)
|
||||
d.logger.Debug("sdk request 'cdn.DescribeCertConfig'", slog.Any("request", describeCertConfigReq), slog.Any("response", describeCertConfigResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeCertConfig': %w", err)
|
||||
}
|
||||
|
||||
if describeCertConfigResp.Result.CertNotConfig != nil {
|
||||
for i := range describeCertConfigResp.Result.CertNotConfig {
|
||||
domains = append(domains, describeCertConfigResp.Result.CertNotConfig[i].Domain)
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
if describeCertConfigResp.Result.OtherCertConfig != nil {
|
||||
for i := range describeCertConfigResp.Result.OtherCertConfig {
|
||||
domains = append(domains, describeCertConfigResp.Result.OtherCertConfig[i].Domain)
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
}
|
||||
|
||||
if len(domains) == 0 {
|
||||
if len(describeCertConfigResp.Result.SpecifiedCertConfig) > 0 {
|
||||
// 所有可关联的域名都配置了该证书,跳过部署
|
||||
d.logger.Info("no domains to deploy")
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getMatchedDomainsByWildcard(ctx, d.config.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = domainCandidates
|
||||
} else {
|
||||
return nil, errors.New("domain not found")
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
domains = append(domains, d.config.Domain)
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
domainCandidates, err := d.getMatchedDomainsByCertId(ctx, upres.CertId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = domainCandidates
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
if len(domains) > 0 {
|
||||
// 遍历绑定证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no cdn domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found cdn domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
|
||||
default:
|
||||
// 关联证书与加速域名
|
||||
// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-batchdeploycert
|
||||
batchDeployCertReq := &bpcdn.BatchDeployCertRequest{
|
||||
CertId: upres.CertId,
|
||||
Domain: domain,
|
||||
}
|
||||
batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
|
||||
d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp))
|
||||
if err != nil {
|
||||
if err := d.updateDomainCertificate(ctx, domain, upres.CertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
@@ -142,3 +149,96 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getMatchedDomainsByWildcard(ctx context.Context, wildcardDomain string) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询加速域名列表,获取匹配的域名
|
||||
// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/ListCdnDomains_en-us
|
||||
listCdnDomainsPageNum := 1
|
||||
listCdnDomainsPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listCdnDomainsReq := &bpcdn.ListCdnDomainsRequest{
|
||||
Domain: bp.String(strings.TrimPrefix(wildcardDomain, "*.")),
|
||||
Status: bp.String("online"),
|
||||
PageNum: bp.Int64(int64(listCdnDomainsPageNum)),
|
||||
PageSize: bp.Int64(int64(listCdnDomainsPageSize)),
|
||||
}
|
||||
listCdnDomainsResp, err := d.sdkClient.ListCdnDomains(listCdnDomainsReq)
|
||||
d.logger.Debug("sdk request 'cdn.ListCdnDomains'", slog.Any("request", listCdnDomainsReq), slog.Any("response", listCdnDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.ListCdnDomains': %w", err)
|
||||
}
|
||||
|
||||
for _, domainItem := range listCdnDomainsResp.Result.Data {
|
||||
if xcerthostname.IsMatch(wildcardDomain, domainItem.Domain) {
|
||||
domains = append(domains, domainItem.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if len(listCdnDomainsResp.Result.Data) < listCdnDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
listCdnDomainsPageSize++
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getMatchedDomainsByCertId(ctx context.Context, cloudCertId string) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 获取指定证书可关联的域名
|
||||
// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-describecertconfig-9ea17
|
||||
describeCertConfigReq := &bpcdn.DescribeCertConfigRequest{
|
||||
CertId: cloudCertId,
|
||||
}
|
||||
describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq)
|
||||
d.logger.Debug("sdk request 'cdn.DescribeCertConfig'", slog.Any("request", describeCertConfigReq), slog.Any("response", describeCertConfigResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeCertConfig': %w", err)
|
||||
}
|
||||
|
||||
if describeCertConfigResp.Result.CertNotConfig != nil {
|
||||
for i := range describeCertConfigResp.Result.CertNotConfig {
|
||||
domains = append(domains, describeCertConfigResp.Result.CertNotConfig[i].Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if describeCertConfigResp.Result.OtherCertConfig != nil {
|
||||
for i := range describeCertConfigResp.Result.OtherCertConfig {
|
||||
domains = append(domains, describeCertConfigResp.Result.OtherCertConfig[i].Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if len(domains) == 0 {
|
||||
if len(describeCertConfigResp.Result.SpecifiedCertConfig) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, cloudCertId string) error {
|
||||
// 关联证书与加速域名
|
||||
// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-batchdeploycert
|
||||
batchDeployCertReq := &bpcdn.BatchDeployCertRequest{
|
||||
CertId: cloudCertId,
|
||||
Domain: domain,
|
||||
}
|
||||
batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
|
||||
d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'cdn.BatchDeployCert': %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package bytepluscdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package ctcccloudao
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -6,12 +6,15 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/ctcccloud-ao"
|
||||
ctyunao "github.com/certimate-go/certimate/pkg/sdk3rd/ctyun/ao"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -19,6 +22,9 @@ type SSLDeployerProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 天翼云 SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -67,10 +73,6 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
@@ -79,22 +81,156 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no accessone domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found accessone domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
if err := d.updateDomainCertificate(ctx, domain, upres.CertName); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getAllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询域名列表
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=113&api=13816&data=174&isNormal=1&vid=167
|
||||
queryDomainsPage := 1
|
||||
queryDomainsPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
queryDomainsReq := &ctyunao.QueryDomainsRequest{
|
||||
Page: lo.ToPtr(int32(queryDomainsPage)),
|
||||
PageSize: lo.ToPtr(int32(queryDomainsPageSize)),
|
||||
ProductCode: lo.ToPtr("020"),
|
||||
}
|
||||
queryDomainsResp, err := d.sdkClient.QueryDomains(queryDomainsReq)
|
||||
d.logger.Debug("sdk request 'cdn.QueryDomains'", slog.Any("request", queryDomainsReq), slog.Any("response", queryDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.QueryDomains': %w", err)
|
||||
}
|
||||
|
||||
if queryDomainsResp.ReturnObj == nil {
|
||||
break
|
||||
}
|
||||
|
||||
ignoredStatuses := []int32{1, 5, 6, 7, 8, 9, 11, 12}
|
||||
for _, domainItem := range queryDomainsResp.ReturnObj.Results {
|
||||
if lo.Contains(ignoredStatuses, domainItem.Status) {
|
||||
continue
|
||||
}
|
||||
|
||||
domains = append(domains, domainItem.Domain)
|
||||
}
|
||||
|
||||
if len(queryDomainsResp.ReturnObj.Results) < queryDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
queryDomainsPage++
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, cloudCertName string) error {
|
||||
// 域名基础及加速配置查询
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=113&api=13412&data=174&isNormal=1&vid=167
|
||||
getDomainConfigReq := &ctyunao.GetDomainConfigRequest{
|
||||
Domain: lo.ToPtr(d.config.Domain),
|
||||
Domain: lo.ToPtr(domain),
|
||||
ProductCode: lo.ToPtr("020"),
|
||||
}
|
||||
getDomainConfigResp, err := d.sdkClient.GetDomainConfig(getDomainConfigReq)
|
||||
d.logger.Debug("sdk request 'cdn.GetDomainConfig'", slog.Any("request", getDomainConfigReq), slog.Any("response", getDomainConfigResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetDomainConfig': %w", err)
|
||||
return fmt.Errorf("failed to execute sdk request 'cdn.GetDomainConfig': %w", err)
|
||||
}
|
||||
|
||||
// 域名基础及加速配置修改
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=113&api=13413&data=174&isNormal=1&vid=167
|
||||
modifyDomainConfigReq := &ctyunao.ModifyDomainConfigRequest{
|
||||
Domain: lo.ToPtr(d.config.Domain),
|
||||
Domain: lo.ToPtr(domain),
|
||||
ProductCode: lo.ToPtr(getDomainConfigResp.ReturnObj.ProductCode),
|
||||
Origin: lo.Map(getDomainConfigResp.ReturnObj.Origin, func(item *ctyunao.DomainOriginConfigWithWeight, _ int) *ctyunao.DomainOriginConfig {
|
||||
weight := item.Weight
|
||||
@@ -108,15 +244,15 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
}
|
||||
}),
|
||||
HttpsStatus: lo.ToPtr("on"),
|
||||
CertName: lo.ToPtr(upres.CertName),
|
||||
CertName: lo.ToPtr(cloudCertName),
|
||||
}
|
||||
modifyDomainConfigResp, err := d.sdkClient.ModifyDomainConfig(modifyDomainConfigReq)
|
||||
d.logger.Debug("sdk request 'cdn.ModifyDomainConfig'", slog.Any("request", modifyDomainConfigReq), slog.Any("response", modifyDomainConfigResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.ModifyDomainConfig': %w", err)
|
||||
return fmt.Errorf("failed to execute sdk request 'cdn.ModifyDomainConfig': %w", err)
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, secretAccessKey string) (*ctyunao.Client, error) {
|
||||
|
||||
@@ -53,9 +53,10 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package ctcccloudcdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -5,12 +5,15 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/ctcccloud-cdn"
|
||||
ctyuncdn "github.com/certimate-go/certimate/pkg/sdk3rd/ctyun/cdn"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -18,6 +21,9 @@ type SSLDeployerProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 天翼云 SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -66,10 +72,6 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
@@ -78,33 +80,171 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 查询域名配置信息
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=11304&data=161&isNormal=1&vid=154
|
||||
queryDomainDetailReq := &ctyuncdn.QueryDomainDetailRequest{
|
||||
Domain: lo.ToPtr(d.config.Domain),
|
||||
}
|
||||
queryDomainDetailResp, err := d.sdkClient.QueryDomainDetail(queryDomainDetailReq)
|
||||
d.logger.Debug("sdk request 'cdn.QueryDomainDetail'", slog.Any("request", queryDomainDetailReq), slog.Any("response", queryDomainDetailResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.QueryDomainDetail': %w", err)
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 修改域名配置
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=11308&data=161&isNormal=1&vid=154
|
||||
updateDomainReq := &ctyuncdn.UpdateDomainRequest{
|
||||
Domain: lo.ToPtr(d.config.Domain),
|
||||
HttpsStatus: lo.ToPtr("on"),
|
||||
CertName: lo.ToPtr(upres.CertName),
|
||||
}
|
||||
updateDomainResp, err := d.sdkClient.UpdateDomain(updateDomainReq)
|
||||
d.logger.Debug("sdk request 'cdn.UpdateDomain'", slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.UpdateDomain': %w", err)
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no cdn domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found cdn domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
if err := d.updateDomainCertificate(ctx, domain, upres.CertName); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getAllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询域名列表
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=11307&data=161&isNormal=1&vid=154
|
||||
queryDomainListPage := 1
|
||||
queryDomainListPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
queryDomainListReq := &ctyuncdn.QueryDomainListRequest{
|
||||
Page: lo.ToPtr(int32(queryDomainListPage)),
|
||||
PageSize: lo.ToPtr(int32(queryDomainListPageSize)),
|
||||
ProductCode: lo.ToPtr("020"),
|
||||
}
|
||||
queryDomainListResp, err := d.sdkClient.QueryDomainList(queryDomainListReq)
|
||||
d.logger.Debug("sdk request 'cdn.QueryDomainList'", slog.Any("request", queryDomainListReq), slog.Any("response", queryDomainListResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.QueryDomainList': %w", err)
|
||||
}
|
||||
|
||||
if queryDomainListResp.ReturnObj == nil {
|
||||
break
|
||||
}
|
||||
|
||||
filteredProductCodes := []string{"001", "003", "004", "008"}
|
||||
ignoredStatuses := []int32{1, 5, 6, 7, 8, 9, 11, 12}
|
||||
for _, domainItem := range queryDomainListResp.ReturnObj.Results {
|
||||
if !lo.Contains(filteredProductCodes, domainItem.ProductCode) {
|
||||
continue
|
||||
}
|
||||
if lo.Contains(ignoredStatuses, domainItem.Status) {
|
||||
continue
|
||||
}
|
||||
|
||||
domains = append(domains, domainItem.Domain)
|
||||
}
|
||||
|
||||
if len(queryDomainListResp.ReturnObj.Results) < queryDomainListPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
queryDomainListPage++
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, cloudCertName string) error {
|
||||
// 查询域名配置信息
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=11304&data=161&isNormal=1&vid=154
|
||||
queryDomainDetailReq := &ctyuncdn.QueryDomainDetailRequest{
|
||||
Domain: lo.ToPtr(domain),
|
||||
}
|
||||
queryDomainDetailResp, err := d.sdkClient.QueryDomainDetail(queryDomainDetailReq)
|
||||
d.logger.Debug("sdk request 'cdn.QueryDomainDetail'", slog.Any("request", queryDomainDetailReq), slog.Any("response", queryDomainDetailResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'cdn.QueryDomainDetail': %w", err)
|
||||
}
|
||||
|
||||
// 修改域名配置
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=11308&data=161&isNormal=1&vid=154
|
||||
updateDomainReq := &ctyuncdn.UpdateDomainRequest{
|
||||
Domain: lo.ToPtr(domain),
|
||||
HttpsStatus: lo.ToPtr("on"),
|
||||
CertName: lo.ToPtr(cloudCertName),
|
||||
}
|
||||
updateDomainResp, err := d.sdkClient.UpdateDomain(updateDomainReq)
|
||||
d.logger.Debug("sdk request 'cdn.UpdateDomain'", slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'cdn.UpdateDomain': %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, secretAccessKey string) (*ctyuncdn.Client, error) {
|
||||
return ctyuncdn.NewClient(accessKeyId, secretAccessKey)
|
||||
}
|
||||
|
||||
@@ -53,9 +53,10 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -111,13 +111,7 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
// 查询监听列表
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=24&api=5654&data=88&isNormal=1&vid=82
|
||||
listenerIds := make([]string, 0)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
{
|
||||
listListenersReq := &ctyunelb.ListListenersRequest{
|
||||
RegionID: lo.ToPtr(d.config.RegionId),
|
||||
LoadBalancerID: lo.ToPtr(d.config.LoadbalancerId),
|
||||
@@ -133,8 +127,6 @@ func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCer
|
||||
listenerIds = append(listenerIds, listener.ID)
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// 遍历更新监听证书
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package ctcccloudicdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
@@ -5,12 +5,15 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/ctcccloud-icdn"
|
||||
ctyunicdn "github.com/certimate-go/certimate/pkg/sdk3rd/ctyun/icdn"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -18,6 +21,9 @@ type SSLDeployerProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 天翼云 SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -66,10 +72,6 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
@@ -78,33 +80,167 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 查询域名配置信息
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=112&api=10849&data=173&isNormal=1&vid=166
|
||||
queryDomainDetailReq := &ctyunicdn.QueryDomainDetailRequest{
|
||||
Domain: lo.ToPtr(d.config.Domain),
|
||||
}
|
||||
queryDomainDetailResp, err := d.sdkClient.QueryDomainDetail(queryDomainDetailReq)
|
||||
d.logger.Debug("sdk request 'icdn.QueryDomainDetail'", slog.Any("request", queryDomainDetailReq), slog.Any("response", queryDomainDetailResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'icdn.QueryDomainDetail': %w", err)
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
case "", DOMAIN_MATCH_PATTERN_EXACT:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return xcerthostname.IsMatch(d.config.Domain, domain)
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by wildcard")
|
||||
}
|
||||
} else {
|
||||
domains = []string{d.config.Domain}
|
||||
}
|
||||
}
|
||||
|
||||
case DOMAIN_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomains(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("could not find any domains matched by certificate")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 修改域名配置
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=112&api=10853&data=173&isNormal=1&vid=166
|
||||
updateDomainReq := &ctyunicdn.UpdateDomainRequest{
|
||||
Domain: lo.ToPtr(d.config.Domain),
|
||||
HttpsStatus: lo.ToPtr("on"),
|
||||
CertName: lo.ToPtr(upres.CertName),
|
||||
}
|
||||
updateDomainResp, err := d.sdkClient.UpdateDomain(updateDomainReq)
|
||||
d.logger.Debug("sdk request 'icdn.UpdateDomain'", slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'icdn.UpdateDomain': %w", err)
|
||||
// 遍历更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no icdn domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found icdn domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
if err := d.updateDomainCertificate(ctx, domain, upres.CertName); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getAllDomains(ctx context.Context) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 查询域名列表
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=112&api=10852&data=173&isNormal=1&vid=166
|
||||
queryDomainsPage := 1
|
||||
queryDomainsPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
queryDomainListReq := &ctyunicdn.QueryDomainListRequest{
|
||||
Page: lo.ToPtr(int32(queryDomainsPage)),
|
||||
PageSize: lo.ToPtr(int32(queryDomainsPageSize)),
|
||||
ProductCode: lo.ToPtr("006"),
|
||||
}
|
||||
queryDomainListResp, err := d.sdkClient.QueryDomainList(queryDomainListReq)
|
||||
d.logger.Debug("sdk request 'cdn.QueryDomainList'", slog.Any("request", queryDomainListReq), slog.Any("response", queryDomainListResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.QueryDomainList': %w", err)
|
||||
}
|
||||
|
||||
if queryDomainListResp.ReturnObj == nil {
|
||||
break
|
||||
}
|
||||
|
||||
ignoredStatuses := []int32{1, 5, 6, 7, 8, 9, 11, 12}
|
||||
for _, domainItem := range queryDomainListResp.ReturnObj.Results {
|
||||
if lo.Contains(ignoredStatuses, domainItem.Status) {
|
||||
continue
|
||||
}
|
||||
|
||||
domains = append(domains, domainItem.Domain)
|
||||
}
|
||||
|
||||
if len(queryDomainListResp.ReturnObj.Results) < queryDomainsPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
queryDomainsPage++
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateDomainCertificate(ctx context.Context, domain string, cloudCertName string) error {
|
||||
// 查询域名配置信息
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=112&api=10849&data=173&isNormal=1&vid=166
|
||||
queryDomainDetailReq := &ctyunicdn.QueryDomainDetailRequest{
|
||||
Domain: lo.ToPtr(domain),
|
||||
}
|
||||
queryDomainDetailResp, err := d.sdkClient.QueryDomainDetail(queryDomainDetailReq)
|
||||
d.logger.Debug("sdk request 'icdn.QueryDomainDetail'", slog.Any("request", queryDomainDetailReq), slog.Any("response", queryDomainDetailResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'icdn.QueryDomainDetail': %w", err)
|
||||
}
|
||||
|
||||
// 修改域名配置
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=112&api=10853&data=173&isNormal=1&vid=166
|
||||
updateDomainReq := &ctyunicdn.UpdateDomainRequest{
|
||||
Domain: lo.ToPtr(domain),
|
||||
HttpsStatus: lo.ToPtr("on"),
|
||||
CertName: lo.ToPtr(cloudCertName),
|
||||
}
|
||||
updateDomainResp, err := d.sdkClient.UpdateDomain(updateDomainReq)
|
||||
d.logger.Debug("sdk request 'icdn.UpdateDomain'", slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'icdn.UpdateDomain': %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, secretAccessKey string) (*ctyunicdn.Client, error) {
|
||||
return ctyunicdn.NewClient(accessKeyId, secretAccessKey)
|
||||
}
|
||||
|
||||
@@ -53,9 +53,10 @@ func TestDeploy(t *testing.T) {
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
Domain: fDomain,
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package ctcccloudlvdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
DOMAIN_MATCH_PATTERN_EXACT = "exact"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user