mirror of
https://github.com/usual2970/certimate.git
synced 2025-07-01 00:29:57 +00:00
Merge branch 'fudiwei-feat/cloud-load-balance'
This commit is contained in:
commit
1238508bdb
33
README.md
33
README.md
@ -71,22 +71,22 @@ make local.run
|
||||
|
||||
## 三、支持的服务商列表
|
||||
|
||||
| 服务商 | 支持申请证书 | 支持部署证书 | 备注 |
|
||||
| :--------: | :----------: | :----------: | ------------------------------------------------------------ |
|
||||
| 阿里云 | √ | √ | 可签发在阿里云注册的域名;可部署到阿里云 OSS、CDN |
|
||||
| 腾讯云 | √ | √ | 可签发在腾讯云注册的域名;可部署到腾讯云 COS、CDN、ECDN、CLB、TEO |
|
||||
| 华为云 | √ | √ | 可签发在华为云注册的域名;可部署到华为云 CDN、ELB |
|
||||
| 七牛云 | | √ | 可部署到七牛云 CDN |
|
||||
| AWS | √ | | 可签发在 AWS Route53 托管的域名 |
|
||||
| CloudFlare | √ | | 可签发在 CloudFlare 注册的域名;CloudFlare 服务自带 SSL 证书 |
|
||||
| GoDaddy | √ | | 可签发在 GoDaddy 注册的域名 |
|
||||
| Namesilo | √ | | 可签发在 Namesilo 注册的域名 |
|
||||
| PowerDNS | √ | | 可签发在 PowerDNS 托管的域名 |
|
||||
| HTTP 请求 | √ | | 可签发允许通过 HTTP 请求修改 DNS 的域名 |
|
||||
| 本地部署 | | √ | 可部署到本地服务器 |
|
||||
| SSH | | √ | 可部署到 SSH 服务器 |
|
||||
| Webhook | | √ | 可部署时回调到 Webhook |
|
||||
| Kubernetes | | √ | 可部署到 Kubernetes Secret |
|
||||
| 服务商 | 支持申请证书 | 支持部署证书 | 备注 |
|
||||
| :--------: | :----------: | :----------: | ----------------------------------------------------------------- |
|
||||
| 阿里云 | √ | √ | 可签发在阿里云注册的域名;可部署到阿里云 OSS、CDN、SLB |
|
||||
| 腾讯云 | √ | √ | 可签发在腾讯云注册的域名;可部署到腾讯云 COS、CDN、ECDN、CLB、TEO |
|
||||
| 华为云 | √ | √ | 可签发在华为云注册的域名;可部署到华为云 CDN、ELB |
|
||||
| 七牛云 | | √ | 可部署到七牛云 CDN |
|
||||
| AWS | √ | | 可签发在 AWS Route53 托管的域名 |
|
||||
| CloudFlare | √ | | 可签发在 CloudFlare 注册的域名;CloudFlare 服务自带 SSL 证书 |
|
||||
| GoDaddy | √ | | 可签发在 GoDaddy 注册的域名 |
|
||||
| Namesilo | √ | | 可签发在 Namesilo 注册的域名 |
|
||||
| PowerDNS | √ | | 可签发在 PowerDNS 托管的域名 |
|
||||
| HTTP 请求 | √ | | 可签发允许通过 HTTP 请求修改 DNS 的域名 |
|
||||
| 本地部署 | | √ | 可部署到本地服务器 |
|
||||
| SSH | | √ | 可部署到 SSH 服务器 |
|
||||
| Webhook | | √ | 可部署时回调到 Webhook |
|
||||
| Kubernetes | | √ | 可部署到 Kubernetes Secret |
|
||||
|
||||
## 四、系统截图
|
||||
|
||||
@ -180,3 +180,4 @@ Certimate 是一个免费且开源的项目,采用 [MIT 开源协议](LICENSE.
|
||||
## 九、Star 趋势图
|
||||
|
||||
[](https://starchart.cc/usual2970/certimate)
|
||||
|
||||
|
@ -59,7 +59,7 @@ make local.run
|
||||
|
||||
## Usage
|
||||
|
||||
After completing the installation steps above, you can access the Certimate management page by visiting http://127.0.0.1:8090 in your browser.
|
||||
After completing the installation steps above, you can access the Certimate management page by visiting <http://127.0.0.1:8090> in your browser.
|
||||
|
||||
```bash
|
||||
username:admin@certimate.fun
|
||||
@ -72,7 +72,7 @@ password:1234567890
|
||||
|
||||
| Provider | Registration | Deployment | Remarks |
|
||||
| :-----------: | :----------: | :--------: | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| Alibaba Cloud | √ | √ | Supports domains registered on Alibaba Cloud; supports deployment to Alibaba Cloud OSS, CDN |
|
||||
| Alibaba Cloud | √ | √ | Supports domains registered on Alibaba Cloud; supports deployment to Alibaba Cloud OSS, CDN,SLB |
|
||||
| Tencent Cloud | √ | √ | Supports domains registered on Tencent Cloud; supports deployment to Tencent Cloud COS, CDN, ECDN, CLB, TEO |
|
||||
| Huawei Cloud | √ | √ | Supports domains registered on Huawei Cloud; supports deployment to Huawei Cloud CDN, ELB |
|
||||
| Qiniu Cloud | | √ | Supports deployment to Qiniu Cloud CDN |
|
||||
|
7
go.mod
7
go.mod
@ -5,9 +5,12 @@ go 1.22.0
|
||||
toolchain go1.23.2
|
||||
|
||||
require (
|
||||
github.com/alibabacloud-go/alb-20200616/v2 v2.2.1
|
||||
github.com/alibabacloud-go/cas-20200407/v3 v3.0.1
|
||||
github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10
|
||||
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3
|
||||
github.com/alibabacloud-go/slb-20140515/v4 v4.0.9
|
||||
github.com/alibabacloud-go/tea v1.2.2
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.6
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
@ -24,6 +27,7 @@ require (
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1030
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992
|
||||
golang.org/x/crypto v0.28.0
|
||||
k8s.io/api v0.31.1
|
||||
k8s.io/apimachinery v0.31.1
|
||||
k8s.io/client-go v0.31.1
|
||||
)
|
||||
@ -59,7 +63,6 @@ require (
|
||||
go.mongodb.org/mongo-driver v1.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/api v0.31.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
@ -152,7 +155,7 @@ require (
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/term v0.25.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
|
9
go.sum
9
go.sum
@ -29,6 +29,8 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
|
||||
github.com/alibabacloud-go/alb-20200616/v2 v2.2.1 h1:b8ixnrkFhWrmJQd+iEE1UWPD5vdyC3d9l7G0uvkfi2s=
|
||||
github.com/alibabacloud-go/alb-20200616/v2 v2.2.1/go.mod h1:cPdZwovbqpv+5nM/HnMwZpG5q0/gBuX31hu2H1VoyrM=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
||||
@ -45,6 +47,8 @@ github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F
|
||||
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
|
||||
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.5/go.mod h1:kUe8JqFmoVU7lfBauaDD5taFaW7mBI+xVsyHutYtabg=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.7/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak=
|
||||
@ -61,11 +65,15 @@ github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA
|
||||
github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
|
||||
github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
|
||||
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
|
||||
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3 h1:LtyUVlgBEKyzWgQJurzXM6MXCt84sQr9cE5OKqYymko=
|
||||
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3/go.mod h1:4a/RcBYeAhYowHzX+LMgnouz7NradnSKPKl14KS3B1U=
|
||||
github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
|
||||
github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY=
|
||||
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
|
||||
github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1 h1:L0TIjr9Qh/SLVc1yPhFkcB9+9SbCNK/jPq4ZKB5zmnc=
|
||||
github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1/go.mod h1:EKxBRDLcMzwl4VLF/1WJwlByZZECJawPXUvinKMsTTs=
|
||||
github.com/alibabacloud-go/slb-20140515/v4 v4.0.9 h1:nrf9gQth7fONUj7V8i78Yb98eb9NdKl0VdeSjmeYugI=
|
||||
github.com/alibabacloud-go/slb-20140515/v4 v4.0.9/go.mod h1:PEMEsQoxhkMvykMFP5ZXg6SWI9vmAiZ6lK3Pu4mTKB0=
|
||||
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
|
||||
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
|
||||
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
|
||||
@ -89,6 +97,7 @@ github.com/alibabacloud-go/tea-utils v1.3.6/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQ
|
||||
github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA=
|
||||
github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.4/go.mod h1:sj1PbjPodAVTqGTA3olprfeeqqmwD0A5OQz94o9EuXQ=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.6 h1:ZkmUlhlQbaDC+Eba/GARMPy6hKdCLiSke5RsN5LcyQ0=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
|
||||
|
@ -98,7 +98,7 @@ func newApplyUser(ca, email string) (*ApplyUser, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyStr, err := x509.PrivateKeyToPEM(privateKey)
|
||||
keyStr, err := x509.ConvertECPrivateKeyToPEM(privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -122,7 +122,7 @@ func (u ApplyUser) GetRegistration() *registration.Resource {
|
||||
}
|
||||
|
||||
func (u *ApplyUser) GetPrivateKey() crypto.PrivateKey {
|
||||
rs, _ := x509.ParsePrivateKeyFromPEM(u.key)
|
||||
rs, _ := x509.ParseECPrivateKeyFromPEM(u.key)
|
||||
return rs
|
||||
}
|
||||
|
||||
|
265
internal/deployer/aliyun_alb.go
Normal file
265
internal/deployer/aliyun_alb.go
Normal file
@ -0,0 +1,265 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
alb20200616 "github.com/alibabacloud-go/alb-20200616/v2/client"
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||
)
|
||||
|
||||
type AliyunALBDeployer struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
|
||||
sdkClient *alb20200616.Client
|
||||
sslUploader uploader.Uploader
|
||||
}
|
||||
|
||||
func NewAliyunALBDeployer(option *DeployerOption) (Deployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
|
||||
client, err := (&AliyunALBDeployer{}).createSdkClient(
|
||||
access.AccessKeyId,
|
||||
access.AccessKeySecret,
|
||||
option.DeployConfig.GetConfigAsString("region"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uploader, err := uploader.NewAliyunCASUploader(&uploader.AliyunCASUploaderConfig{
|
||||
AccessKeyId: access.AccessKeyId,
|
||||
AccessKeySecret: access.AccessKeySecret,
|
||||
Region: option.DeployConfig.GetConfigAsString("region"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AliyunALBDeployer{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
sdkClient: client,
|
||||
sslUploader: uploader,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *AliyunALBDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *AliyunALBDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (d *AliyunALBDeployer) Deploy(ctx context.Context) error {
|
||||
switch d.option.DeployConfig.GetConfigAsString("resourceType") {
|
||||
case "loadbalancer":
|
||||
if err := d.deployToLoadbalancer(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
case "listener":
|
||||
if err := d.deployToListener(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("unsupported resource type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunALBDeployer) createSdkClient(accessKeyId, accessKeySecret, region string) (*alb20200616.Client, error) {
|
||||
if region == "" {
|
||||
region = "cn-hangzhou" // ALB 服务默认区域:华东一杭州
|
||||
}
|
||||
|
||||
aConfig := &openapi.Config{
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
}
|
||||
|
||||
var endpoint string
|
||||
switch region {
|
||||
case "cn-hangzhou-finance":
|
||||
endpoint = "alb.cn-hangzhou.aliyuncs.com"
|
||||
default:
|
||||
endpoint = fmt.Sprintf("alb.%s.aliyuncs.com", region)
|
||||
}
|
||||
aConfig.Endpoint = tea.String(endpoint)
|
||||
|
||||
client, err := alb20200616.NewClient(aConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (d *AliyunALBDeployer) deployToLoadbalancer(ctx context.Context) error {
|
||||
aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
|
||||
if aliLoadbalancerId == "" {
|
||||
return errors.New("`loadbalancerId` is required")
|
||||
}
|
||||
|
||||
aliListenerIds := make([]string, 0)
|
||||
|
||||
// 查询负载均衡实例的详细信息
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getloadbalancerattribute
|
||||
getLoadBalancerAttributeReq := &alb20200616.GetLoadBalancerAttributeRequest{
|
||||
LoadBalancerId: tea.String(aliLoadbalancerId),
|
||||
}
|
||||
getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.GetLoadBalancerAttribute': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 ALB 负载均衡实例", getLoadBalancerAttributeResp))
|
||||
|
||||
// 查询 HTTPS 监听列表
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
||||
listListenersPage := 1
|
||||
listListenersLimit := int32(100)
|
||||
var listListenersToken *string = nil
|
||||
for {
|
||||
listListenersReq := &alb20200616.ListListenersRequest{
|
||||
MaxResults: tea.Int32(listListenersLimit),
|
||||
NextToken: listListenersToken,
|
||||
LoadBalancerIds: []*string{tea.String(aliLoadbalancerId)},
|
||||
ListenerProtocol: tea.String("HTTPS"),
|
||||
}
|
||||
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
|
||||
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 {
|
||||
aliListenerIds = append(aliListenerIds, *listener.ListenerId)
|
||||
}
|
||||
}
|
||||
|
||||
if listListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
listListenersPage += 1
|
||||
}
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 ALB 负载均衡实例下的全部 HTTPS 监听", aliListenerIds))
|
||||
|
||||
// 查询 QUIC 监听列表
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
||||
listListenersPage = 1
|
||||
listListenersToken = nil
|
||||
for {
|
||||
listListenersReq := &alb20200616.ListListenersRequest{
|
||||
MaxResults: tea.Int32(listListenersLimit),
|
||||
NextToken: listListenersToken,
|
||||
LoadBalancerIds: []*string{tea.String(aliLoadbalancerId)},
|
||||
ListenerProtocol: tea.String("QUIC"),
|
||||
}
|
||||
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
|
||||
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 {
|
||||
aliListenerIds = append(aliListenerIds, *listener.ListenerId)
|
||||
}
|
||||
}
|
||||
|
||||
if listListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
listListenersPage += 1
|
||||
}
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 ALB 负载均衡实例下的全部 QUIC 监听", aliListenerIds))
|
||||
|
||||
// 上传证书到 SSL
|
||||
uploadResult, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已上传证书", uploadResult))
|
||||
|
||||
// 批量更新监听证书
|
||||
var errs []error
|
||||
for _, aliListenerId := range aliListenerIds {
|
||||
if err := d.updateListenerCertificate(ctx, aliListenerId, uploadResult.CertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunALBDeployer) deployToListener(ctx context.Context) error {
|
||||
aliListenerId := d.option.DeployConfig.GetConfigAsString("listenerId")
|
||||
if aliListenerId == "" {
|
||||
return errors.New("`listenerId` is required")
|
||||
}
|
||||
|
||||
// 上传证书到 SSL
|
||||
uploadResult, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已上传证书", uploadResult))
|
||||
|
||||
// 更新监听
|
||||
if err := d.updateListenerCertificate(ctx, aliListenerId, uploadResult.CertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunALBDeployer) updateListenerCertificate(ctx context.Context, aliListenerId string, aliCertId string) error {
|
||||
// 查询监听的属性
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getlistenerattribute
|
||||
getListenerAttributeReq := &alb20200616.GetListenerAttributeRequest{
|
||||
ListenerId: tea.String(aliListenerId),
|
||||
}
|
||||
getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.GetListenerAttribute': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 ALB 监听配置", getListenerAttributeResp))
|
||||
|
||||
// 修改监听的属性
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-updatelistenerattribute
|
||||
updateListenerAttributeReq := &alb20200616.UpdateListenerAttributeRequest{
|
||||
ListenerId: tea.String(aliListenerId),
|
||||
Certificates: []*alb20200616.UpdateListenerAttributeRequestCertificates{{
|
||||
CertificateId: tea.String(aliCertId),
|
||||
}},
|
||||
}
|
||||
updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.UpdateListenerAttribute': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已更新 ALB 监听配置", updateListenerAttributeResp))
|
||||
|
||||
return nil
|
||||
}
|
282
internal/deployer/aliyun_clb.go
Normal file
282
internal/deployer/aliyun_clb.go
Normal file
@ -0,0 +1,282 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
slb20140515 "github.com/alibabacloud-go/slb-20140515/v4/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||
)
|
||||
|
||||
type AliyunCLBDeployer struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
|
||||
sdkClient *slb20140515.Client
|
||||
sslUploader uploader.Uploader
|
||||
}
|
||||
|
||||
func NewAliyunCLBDeployer(option *DeployerOption) (Deployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
|
||||
client, err := (&AliyunCLBDeployer{}).createSdkClient(
|
||||
access.AccessKeyId,
|
||||
access.AccessKeySecret,
|
||||
option.DeployConfig.GetConfigAsString("region"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uploader, err := uploader.NewAliyunSLBUploader(&uploader.AliyunSLBUploaderConfig{
|
||||
AccessKeyId: access.AccessKeyId,
|
||||
AccessKeySecret: access.AccessKeySecret,
|
||||
Region: option.DeployConfig.GetConfigAsString("region"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AliyunCLBDeployer{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
sdkClient: client,
|
||||
sslUploader: uploader,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *AliyunCLBDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *AliyunCLBDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (d *AliyunCLBDeployer) Deploy(ctx context.Context) error {
|
||||
switch d.option.DeployConfig.GetConfigAsString("resourceType") {
|
||||
case "loadbalancer":
|
||||
if err := d.deployToLoadbalancer(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
case "listener":
|
||||
if err := d.deployToListener(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("unsupported resource type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunCLBDeployer) createSdkClient(accessKeyId, accessKeySecret, region string) (*slb20140515.Client, error) {
|
||||
if region == "" {
|
||||
region = "cn-hangzhou" // CLB(SLB) 服务默认区域:华东一杭州
|
||||
}
|
||||
|
||||
aConfig := &openapi.Config{
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
}
|
||||
|
||||
var endpoint string
|
||||
switch region {
|
||||
case "cn-hangzhou":
|
||||
case "cn-hangzhou-finance":
|
||||
case "cn-shanghai-finance-1":
|
||||
case "cn-shenzhen-finance-1":
|
||||
endpoint = "slb.aliyuncs.com"
|
||||
default:
|
||||
endpoint = fmt.Sprintf("slb.%s.aliyuncs.com", region)
|
||||
}
|
||||
aConfig.Endpoint = tea.String(endpoint)
|
||||
|
||||
client, err := slb20140515.NewClient(aConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (d *AliyunCLBDeployer) deployToLoadbalancer(ctx context.Context) error {
|
||||
aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
|
||||
if aliLoadbalancerId == "" {
|
||||
return errors.New("`loadbalancerId` is required")
|
||||
}
|
||||
|
||||
aliListenerPorts := make([]int32, 0)
|
||||
|
||||
// 查询负载均衡实例的详细信息
|
||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerattribute
|
||||
describeLoadBalancerAttributeReq := &slb20140515.DescribeLoadBalancerAttributeRequest{
|
||||
RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")),
|
||||
LoadBalancerId: tea.String(aliLoadbalancerId),
|
||||
}
|
||||
describeLoadBalancerAttributeResp, err := d.sdkClient.DescribeLoadBalancerAttribute(describeLoadBalancerAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerAttribute': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 CLB 负载均衡实例", describeLoadBalancerAttributeResp))
|
||||
|
||||
// 查询 HTTPS 监听列表
|
||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerlisteners
|
||||
listListenersPage := 1
|
||||
listListenersLimit := int32(100)
|
||||
var listListenersToken *string = nil
|
||||
for {
|
||||
describeLoadBalancerListenersReq := &slb20140515.DescribeLoadBalancerListenersRequest{
|
||||
RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")),
|
||||
MaxResults: tea.Int32(listListenersLimit),
|
||||
NextToken: listListenersToken,
|
||||
LoadBalancerId: []*string{tea.String(aliLoadbalancerId)},
|
||||
ListenerProtocol: tea.String("https"),
|
||||
}
|
||||
describeLoadBalancerListenersResp, err := d.sdkClient.DescribeLoadBalancerListeners(describeLoadBalancerListenersReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerListeners': %w", err)
|
||||
}
|
||||
|
||||
if describeLoadBalancerListenersResp.Body.Listeners != nil {
|
||||
for _, listener := range describeLoadBalancerListenersResp.Body.Listeners {
|
||||
aliListenerPorts = append(aliListenerPorts, *listener.ListenerPort)
|
||||
}
|
||||
}
|
||||
|
||||
if describeLoadBalancerListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenersToken = describeLoadBalancerListenersResp.Body.NextToken
|
||||
listListenersPage += 1
|
||||
}
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 CLB 负载均衡实例下的全部 HTTPS 监听", aliListenerPorts))
|
||||
|
||||
// 上传证书到 SLB
|
||||
uploadResult, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已上传证书", uploadResult))
|
||||
|
||||
// 批量更新监听证书
|
||||
var errs []error
|
||||
for _, aliListenerPort := range aliListenerPorts {
|
||||
if err := d.updateListenerCertificate(ctx, aliLoadbalancerId, aliListenerPort, uploadResult.CertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunCLBDeployer) deployToListener(ctx context.Context) error {
|
||||
aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
|
||||
if aliLoadbalancerId == "" {
|
||||
return errors.New("`loadbalancerId` is required")
|
||||
}
|
||||
|
||||
aliListenerPort := d.option.DeployConfig.GetConfigAsInt32("listenerPort")
|
||||
if aliListenerPort == 0 {
|
||||
return errors.New("`listenerPort` is required")
|
||||
}
|
||||
|
||||
// 上传证书到 SLB
|
||||
uploadResult, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已上传证书", uploadResult))
|
||||
|
||||
// 更新监听
|
||||
if err := d.updateListenerCertificate(ctx, aliLoadbalancerId, aliListenerPort, uploadResult.CertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunCLBDeployer) updateListenerCertificate(ctx context.Context, aliLoadbalancerId string, aliListenerPort int32, aliCertId string) error {
|
||||
// 查询监听配置
|
||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerhttpslistenerattribute
|
||||
describeLoadBalancerHTTPSListenerAttributeReq := &slb20140515.DescribeLoadBalancerHTTPSListenerAttributeRequest{
|
||||
LoadBalancerId: tea.String(aliLoadbalancerId),
|
||||
ListenerPort: tea.Int32(aliListenerPort),
|
||||
}
|
||||
describeLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.DescribeLoadBalancerHTTPSListenerAttribute(describeLoadBalancerHTTPSListenerAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 CLB HTTPS 监听配置", describeLoadBalancerHTTPSListenerAttributeResp))
|
||||
|
||||
// 查询扩展域名
|
||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describedomainextensions
|
||||
describeDomainExtensionsReq := &slb20140515.DescribeDomainExtensionsRequest{
|
||||
RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")),
|
||||
LoadBalancerId: tea.String(aliLoadbalancerId),
|
||||
ListenerPort: tea.Int32(aliListenerPort),
|
||||
}
|
||||
describeDomainExtensionsResp, err := d.sdkClient.DescribeDomainExtensions(describeDomainExtensionsReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'slb.DescribeDomainExtensions': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 CLB 扩展域名", describeDomainExtensionsResp))
|
||||
|
||||
// 遍历修改扩展域名
|
||||
// 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 {
|
||||
for _, domainExtension := range describeDomainExtensionsResp.Body.DomainExtensions.DomainExtension {
|
||||
if *domainExtension.ServerCertificateId == *describeLoadBalancerHTTPSListenerAttributeResp.Body.ServerCertificateId {
|
||||
break
|
||||
}
|
||||
|
||||
setDomainExtensionAttributeReq := &slb20140515.SetDomainExtensionAttributeRequest{
|
||||
RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")),
|
||||
DomainExtensionId: tea.String(*domainExtension.DomainExtensionId),
|
||||
ServerCertificateId: tea.String(aliCertId),
|
||||
}
|
||||
_, err := d.sdkClient.SetDomainExtensionAttribute(setDomainExtensionAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'slb.SetDomainExtensionAttribute': %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 修改监听配置
|
||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setloadbalancerhttpslistenerattribute
|
||||
//
|
||||
// 注意修改监听配置要放在修改扩展域名之后
|
||||
setLoadBalancerHTTPSListenerAttributeReq := &slb20140515.SetLoadBalancerHTTPSListenerAttributeRequest{
|
||||
RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")),
|
||||
LoadBalancerId: tea.String(aliLoadbalancerId),
|
||||
ListenerPort: tea.Int32(aliListenerPort),
|
||||
ServerCertificateId: tea.String(aliCertId),
|
||||
}
|
||||
setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.SetLoadBalancerHTTPSListenerAttribute(setLoadBalancerHTTPSListenerAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已更新 CLB HTTPS 监听配置", setLoadBalancerHTTPSListenerAttributeResp))
|
||||
|
||||
return nil
|
||||
}
|
229
internal/deployer/aliyun_nlb.go
Normal file
229
internal/deployer/aliyun_nlb.go
Normal file
@ -0,0 +1,229 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
nlb20220430 "github.com/alibabacloud-go/nlb-20220430/v2/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||
)
|
||||
|
||||
type AliyunNLBDeployer struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
|
||||
sdkClient *nlb20220430.Client
|
||||
sslUploader uploader.Uploader
|
||||
}
|
||||
|
||||
func NewAliyunNLBDeployer(option *DeployerOption) (Deployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
|
||||
client, err := (&AliyunNLBDeployer{}).createSdkClient(
|
||||
access.AccessKeyId,
|
||||
access.AccessKeySecret,
|
||||
option.DeployConfig.GetConfigAsString("region"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uploader, err := uploader.NewAliyunCASUploader(&uploader.AliyunCASUploaderConfig{
|
||||
AccessKeyId: access.AccessKeyId,
|
||||
AccessKeySecret: access.AccessKeySecret,
|
||||
Region: option.DeployConfig.GetConfigAsString("region"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AliyunNLBDeployer{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
sdkClient: client,
|
||||
sslUploader: uploader,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *AliyunNLBDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *AliyunNLBDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (d *AliyunNLBDeployer) Deploy(ctx context.Context) error {
|
||||
switch d.option.DeployConfig.GetConfigAsString("resourceType") {
|
||||
case "loadbalancer":
|
||||
if err := d.deployToLoadbalancer(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
case "listener":
|
||||
if err := d.deployToListener(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("unsupported resource type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunNLBDeployer) createSdkClient(accessKeyId, accessKeySecret, region string) (*nlb20220430.Client, error) {
|
||||
if region == "" {
|
||||
region = "cn-hangzhou" // NLB 服务默认区域:华东一杭州
|
||||
}
|
||||
|
||||
aConfig := &openapi.Config{
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
}
|
||||
|
||||
var endpoint string
|
||||
switch region {
|
||||
default:
|
||||
endpoint = fmt.Sprintf("nlb.%s.aliyuncs.com", region)
|
||||
}
|
||||
aConfig.Endpoint = tea.String(endpoint)
|
||||
|
||||
client, err := nlb20220430.NewClient(aConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (d *AliyunNLBDeployer) deployToLoadbalancer(ctx context.Context) error {
|
||||
aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
|
||||
if aliLoadbalancerId == "" {
|
||||
return errors.New("`loadbalancerId` is required")
|
||||
}
|
||||
|
||||
aliListenerIds := make([]string, 0)
|
||||
|
||||
// 查询负载均衡实例的详细信息
|
||||
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getloadbalancerattribute
|
||||
getLoadBalancerAttributeReq := &nlb20220430.GetLoadBalancerAttributeRequest{
|
||||
LoadBalancerId: tea.String(aliLoadbalancerId),
|
||||
}
|
||||
getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'nlb.GetLoadBalancerAttribute': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 NLB 负载均衡实例", getLoadBalancerAttributeResp))
|
||||
|
||||
// 查询 TCPSSL 监听列表
|
||||
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-listlisteners
|
||||
listListenersPage := 1
|
||||
listListenersLimit := int32(100)
|
||||
var listListenersToken *string = nil
|
||||
for {
|
||||
listListenersReq := &nlb20220430.ListListenersRequest{
|
||||
MaxResults: tea.Int32(listListenersLimit),
|
||||
NextToken: listListenersToken,
|
||||
LoadBalancerIds: []*string{tea.String(aliLoadbalancerId)},
|
||||
ListenerProtocol: tea.String("TCPSSL"),
|
||||
}
|
||||
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
|
||||
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 {
|
||||
aliListenerIds = append(aliListenerIds, *listener.ListenerId)
|
||||
}
|
||||
}
|
||||
|
||||
if listListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
listListenersPage += 1
|
||||
}
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 NLB 负载均衡实例下的全部 TCPSSL 监听", aliListenerIds))
|
||||
|
||||
// 上传证书到 SSL
|
||||
uploadResult, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已上传证书", uploadResult))
|
||||
|
||||
// 批量更新监听证书
|
||||
var errs []error
|
||||
for _, aliListenerId := range aliListenerIds {
|
||||
if err := d.updateListenerCertificate(ctx, aliListenerId, uploadResult.CertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunNLBDeployer) deployToListener(ctx context.Context) error {
|
||||
aliListenerId := d.option.DeployConfig.GetConfigAsString("listenerId")
|
||||
if aliListenerId == "" {
|
||||
return errors.New("`listenerId` is required")
|
||||
}
|
||||
|
||||
// 上传证书到 SSL
|
||||
uploadResult, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已上传证书", uploadResult))
|
||||
|
||||
// 更新监听
|
||||
if err := d.updateListenerCertificate(ctx, aliListenerId, uploadResult.CertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunNLBDeployer) updateListenerCertificate(ctx context.Context, aliListenerId string, aliCertId string) error {
|
||||
// 查询监听的属性
|
||||
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getlistenerattribute
|
||||
getListenerAttributeReq := &nlb20220430.GetListenerAttributeRequest{
|
||||
ListenerId: tea.String(aliListenerId),
|
||||
}
|
||||
getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'nlb.GetListenerAttribute': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到 NLB 监听配置", getListenerAttributeResp))
|
||||
|
||||
// 修改监听的属性
|
||||
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-updatelistenerattribute
|
||||
updateListenerAttributeReq := &nlb20220430.UpdateListenerAttributeRequest{
|
||||
ListenerId: tea.String(aliListenerId),
|
||||
CertificateIds: []*string{tea.String(aliCertId)},
|
||||
}
|
||||
updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'nlb.UpdateListenerAttribute': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已更新 NLB 监听配置", updateListenerAttributeResp))
|
||||
|
||||
return nil
|
||||
}
|
@ -18,6 +18,9 @@ const (
|
||||
targetAliyunOSS = "aliyun-oss"
|
||||
targetAliyunCDN = "aliyun-cdn"
|
||||
targetAliyunESA = "aliyun-dcdn"
|
||||
targetAliyunCLB = "aliyun-clb"
|
||||
targetAliyunALB = "aliyun-alb"
|
||||
targetAliyunNLB = "aliyun-nlb"
|
||||
targetTencentCDN = "tencent-cdn"
|
||||
targetTencentECDN = "tencent-ecdn"
|
||||
targetTencentCLB = "tencent-clb"
|
||||
@ -108,6 +111,12 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
|
||||
return NewAliyunCDNDeployer(option)
|
||||
case targetAliyunESA:
|
||||
return NewAliyunESADeployer(option)
|
||||
case targetAliyunCLB:
|
||||
return NewAliyunCLBDeployer(option)
|
||||
case targetAliyunALB:
|
||||
return NewAliyunALBDeployer(option)
|
||||
case targetAliyunNLB:
|
||||
return NewAliyunNLBDeployer(option)
|
||||
case targetTencentCDN:
|
||||
return NewTencentCDNDeployer(option)
|
||||
case targetTencentECDN:
|
||||
@ -133,7 +142,7 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
|
||||
case targetK8sSecret:
|
||||
return NewK8sSecretDeployer(option)
|
||||
}
|
||||
return nil, errors.New("not implemented")
|
||||
return nil, errors.New("unsupported deploy target")
|
||||
}
|
||||
|
||||
func getProduct(t string) string {
|
||||
|
@ -41,9 +41,9 @@ func NewHuaweiCloudCDNDeployer(option *DeployerOption) (Deployer, error) {
|
||||
|
||||
// TODO: SCM 服务与 DNS 服务所支持的区域可能不一致,这里暂时不传而是使用默认值,仅支持华为云国内版
|
||||
uploader, err := uploader.NewHuaweiCloudSCMUploader(&uploader.HuaweiCloudSCMUploaderConfig{
|
||||
Region: "",
|
||||
AccessKeyId: access.AccessKeyId,
|
||||
SecretAccessKey: access.SecretAccessKey,
|
||||
Region: "",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -46,9 +46,9 @@ func NewHuaweiCloudELBDeployer(option *DeployerOption) (Deployer, error) {
|
||||
}
|
||||
|
||||
uploader, err := uploader.NewHuaweiCloudELBUploader(&uploader.HuaweiCloudELBUploaderConfig{
|
||||
Region: option.DeployConfig.GetConfigAsString("region"),
|
||||
AccessKeyId: access.AccessKeyId,
|
||||
SecretAccessKey: access.SecretAccessKey,
|
||||
Region: option.DeployConfig.GetConfigAsString("region"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -176,10 +176,15 @@ func (u *HuaweiCloudELBDeployer) getSdkProjectId(accessKeyId, secretAccessKey, r
|
||||
}
|
||||
|
||||
func (d *HuaweiCloudELBDeployer) deployToCertificate(ctx context.Context) error {
|
||||
hcCertId := d.option.DeployConfig.GetConfigAsString("certificateId")
|
||||
if hcCertId == "" {
|
||||
return errors.New("`certificateId` is required")
|
||||
}
|
||||
|
||||
// 更新证书
|
||||
// REF: https://support.huaweicloud.com/api-elb/UpdateCertificate.html
|
||||
updateCertificateReq := &hcElbModel.UpdateCertificateRequest{
|
||||
CertificateId: d.option.DeployConfig.GetConfigAsString("certificateId"),
|
||||
CertificateId: hcCertId,
|
||||
Body: &hcElbModel.UpdateCertificateRequestBody{
|
||||
Certificate: &hcElbModel.UpdateCertificateOption{
|
||||
Certificate: cast.StringPtr(d.option.Certificate.Certificate),
|
||||
@ -198,21 +203,27 @@ func (d *HuaweiCloudELBDeployer) deployToCertificate(ctx context.Context) error
|
||||
}
|
||||
|
||||
func (d *HuaweiCloudELBDeployer) deployToLoadbalancer(ctx context.Context) error {
|
||||
hcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
|
||||
if hcLoadbalancerId == "" {
|
||||
return errors.New("`loadbalancerId` is required")
|
||||
}
|
||||
|
||||
hcListenerIds := make([]string, 0)
|
||||
|
||||
// 查询负载均衡器详情
|
||||
// REF: https://support.huaweicloud.com/api-elb/ShowLoadBalancer.html
|
||||
showLoadBalancerReq := &hcElbModel.ShowLoadBalancerRequest{
|
||||
LoadbalancerId: d.option.DeployConfig.GetConfigAsString("loadbalancerId"),
|
||||
LoadbalancerId: hcLoadbalancerId,
|
||||
}
|
||||
showLoadBalancerResp, err := d.sdkClient.ShowLoadBalancer(showLoadBalancerReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'elb.ShowLoadBalancer': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到到 ELB 负载均衡器", showLoadBalancerResp))
|
||||
d.infos = append(d.infos, toStr("已查询到 ELB 负载均衡器", showLoadBalancerResp))
|
||||
|
||||
// 查询监听器列表
|
||||
// REF: https://support.huaweicloud.com/api-elb/ListListeners.html
|
||||
listenerIds := make([]string, 0)
|
||||
listListenersLimit := int32(2000)
|
||||
var listListenersMarker *string = nil
|
||||
for {
|
||||
@ -229,7 +240,7 @@ func (d *HuaweiCloudELBDeployer) deployToLoadbalancer(ctx context.Context) error
|
||||
|
||||
if listListenersResp.Listeners != nil {
|
||||
for _, listener := range *listListenersResp.Listeners {
|
||||
listenerIds = append(listenerIds, listener.Id)
|
||||
hcListenerIds = append(hcListenerIds, listener.Id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +251,7 @@ func (d *HuaweiCloudELBDeployer) deployToLoadbalancer(ctx context.Context) error
|
||||
}
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到到 ELB 负载均衡器下的监听器", listenerIds))
|
||||
d.infos = append(d.infos, toStr("已查询到 ELB 负载均衡器下的监听器", hcListenerIds))
|
||||
|
||||
// 上传证书到 SCM
|
||||
uploadResult, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
|
||||
@ -252,8 +263,8 @@ func (d *HuaweiCloudELBDeployer) deployToLoadbalancer(ctx context.Context) error
|
||||
|
||||
// 批量更新监听器证书
|
||||
var errs []error
|
||||
for _, listenerId := range listenerIds {
|
||||
if err := d.updateListenerCertificate(ctx, listenerId, uploadResult.CertId); err != nil {
|
||||
for _, hcListenerId := range hcListenerIds {
|
||||
if err := d.updateListenerCertificate(ctx, hcListenerId, uploadResult.CertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
@ -265,6 +276,11 @@ func (d *HuaweiCloudELBDeployer) deployToLoadbalancer(ctx context.Context) error
|
||||
}
|
||||
|
||||
func (d *HuaweiCloudELBDeployer) deployToListener(ctx context.Context) error {
|
||||
hcListenerId := d.option.DeployConfig.GetConfigAsString("listenerId")
|
||||
if hcListenerId == "" {
|
||||
return errors.New("`listenerId` is required")
|
||||
}
|
||||
|
||||
// 上传证书到 SCM
|
||||
uploadResult, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
@ -274,7 +290,7 @@ func (d *HuaweiCloudELBDeployer) deployToListener(ctx context.Context) error {
|
||||
d.infos = append(d.infos, toStr("已上传证书", uploadResult))
|
||||
|
||||
// 更新监听器证书
|
||||
if err := d.updateListenerCertificate(ctx, d.option.DeployConfig.GetConfigAsString("listenerId"), uploadResult.CertId); err != nil {
|
||||
if err := d.updateListenerCertificate(ctx, hcListenerId, uploadResult.CertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -292,7 +308,7 @@ func (d *HuaweiCloudELBDeployer) updateListenerCertificate(ctx context.Context,
|
||||
return fmt.Errorf("failed to execute sdk request 'elb.ShowListener': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已查询到到 ELB 监听器", showListenerResp))
|
||||
d.infos = append(d.infos, toStr("已查询到 ELB 监听器", showListenerResp))
|
||||
|
||||
// 更新监听器
|
||||
// REF: https://support.huaweicloud.com/api-elb/UpdateListener.html
|
||||
@ -359,7 +375,7 @@ func (d *HuaweiCloudELBDeployer) updateListenerCertificate(ctx context.Context,
|
||||
return fmt.Errorf("failed to execute sdk request 'elb.UpdateListener': %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("已更新监听器", updateListenerResp))
|
||||
d.infos = append(d.infos, toStr("已更新 ELB 监听器", updateListenerResp))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func (d *TencentTEODeployer) deploy(certId string) error {
|
||||
|
||||
request.ZoneId = common.StringPtr(getDeployString(d.option.DeployConfig, "zoneId"))
|
||||
request.Mode = common.StringPtr("sslcert")
|
||||
request.ServerCertInfo = []*teo.ServerCertInfo{&teo.ServerCertInfo{
|
||||
request.ServerCertInfo = []*teo.ServerCertInfo{{
|
||||
CertId: common.StringPtr(certId),
|
||||
}}
|
||||
|
||||
|
@ -18,7 +18,6 @@ type DeployConfig struct {
|
||||
Config map[string]any `json:"config"`
|
||||
}
|
||||
|
||||
|
||||
// 以字符串形式获取配置项。
|
||||
//
|
||||
// 入参:
|
||||
@ -52,6 +51,39 @@ func (dc *DeployConfig) GetConfigOrDefaultAsString(key string, defaultValue stri
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// 以 32 位整数形式获取配置项。
|
||||
//
|
||||
// 入参:
|
||||
// - key: 配置项的键。
|
||||
//
|
||||
// 出参:
|
||||
// - 配置项的值。如果配置项不存在或者类型不是 32 位整数,则返回 0。
|
||||
func (dc *DeployConfig) GetConfigAsInt32(key string) int32 {
|
||||
return dc.GetConfigOrDefaultAsInt32(key, 0)
|
||||
}
|
||||
|
||||
// 以 32 位整数形式获取配置项。
|
||||
//
|
||||
// 入参:
|
||||
// - key: 配置项的键。
|
||||
// - defaultValue: 默认值。
|
||||
//
|
||||
// 出参:
|
||||
// - 配置项的值。如果配置项不存在或者类型不是 32 位整数,则返回默认值。
|
||||
func (dc *DeployConfig) GetConfigOrDefaultAsInt32(key string, defaultValue int32) int32 {
|
||||
if dc.Config == nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
if value, ok := dc.Config[key]; ok {
|
||||
if result, ok := value.(int32); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// 以布尔形式获取配置项。
|
||||
//
|
||||
// 入参:
|
||||
|
@ -15,9 +15,9 @@ import (
|
||||
)
|
||||
|
||||
type AliyunCASUploaderConfig struct {
|
||||
Region string `json:"region"`
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type AliyunCASUploader struct {
|
||||
@ -28,9 +28,9 @@ type AliyunCASUploader struct {
|
||||
|
||||
func NewAliyunCASUploader(config *AliyunCASUploaderConfig) (Uploader, error) {
|
||||
client, err := (&AliyunCASUploader{}).createSdkClient(
|
||||
config.Region,
|
||||
config.AccessKeyId,
|
||||
config.AccessKeySecret,
|
||||
config.Region,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||
@ -81,12 +81,12 @@ func (u *AliyunCASUploader) Upload(ctx context.Context, certPem string, privkeyP
|
||||
if *getUserCertificateDetailResp.Body.Cert == certPem {
|
||||
isSameCert = true
|
||||
} else {
|
||||
cert, err := x509.ParseCertificateFromPEM(*getUserCertificateDetailResp.Body.Cert)
|
||||
oldCertX509, err := x509.ParseCertificateFromPEM(*getUserCertificateDetailResp.Body.Cert)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
isSameCert = x509.EqualCertificate(certX509, cert)
|
||||
isSameCert = x509.EqualCertificate(certX509, oldCertX509)
|
||||
}
|
||||
|
||||
// 如果已存在相同证书,直接返回已有的证书信息
|
||||
@ -133,7 +133,7 @@ func (u *AliyunCASUploader) Upload(ctx context.Context, certPem string, privkeyP
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *AliyunCASUploader) createSdkClient(region, accessKeyId, accessKeySecret string) (*cas20200407.Client, error) {
|
||||
func (u *AliyunCASUploader) createSdkClient(accessKeyId, accessKeySecret, region string) (*cas20200407.Client, error) {
|
||||
if region == "" {
|
||||
region = "cn-hangzhou" // CAS 服务默认区域:华东一杭州
|
||||
}
|
||||
@ -147,10 +147,6 @@ func (u *AliyunCASUploader) createSdkClient(region, accessKeyId, accessKeySecret
|
||||
switch region {
|
||||
case "cn-hangzhou":
|
||||
endpoint = "cas.aliyuncs.com"
|
||||
case "ap-southeast-1":
|
||||
endpoint = "cas.ap-southeast-1.aliyuncs.com"
|
||||
case "eu-central-1":
|
||||
endpoint = "cas.eu-central-1.aliyuncs.com"
|
||||
default:
|
||||
endpoint = fmt.Sprintf("cas.%s.aliyuncs.com", region)
|
||||
}
|
||||
|
134
internal/pkg/core/uploader/uploader_aliyun_slb.go
Normal file
134
internal/pkg/core/uploader/uploader_aliyun_slb.go
Normal file
@ -0,0 +1,134 @@
|
||||
package uploader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
slb20140515 "github.com/alibabacloud-go/slb-20140515/v4/client"
|
||||
util "github.com/alibabacloud-go/tea-utils/v2/service"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/utils/x509"
|
||||
)
|
||||
|
||||
type AliyunSLBUploaderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type AliyunSLBUploader struct {
|
||||
config *AliyunSLBUploaderConfig
|
||||
sdkClient *slb20140515.Client
|
||||
sdkRuntime *util.RuntimeOptions
|
||||
}
|
||||
|
||||
func NewAliyunSLBUploader(config *AliyunSLBUploaderConfig) (Uploader, error) {
|
||||
client, err := (&AliyunSLBUploader{}).createSdkClient(
|
||||
config.AccessKeyId,
|
||||
config.AccessKeySecret,
|
||||
config.Region,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||
}
|
||||
|
||||
return &AliyunSLBUploader{
|
||||
config: config,
|
||||
sdkClient: client,
|
||||
sdkRuntime: &util.RuntimeOptions{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *AliyunSLBUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *UploadResult, err error) {
|
||||
// 解析证书内容
|
||||
certX509, err := x509.ParseCertificateFromPEM(certPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 查询证书列表,避免重复上传
|
||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeservercertificates
|
||||
describeServerCertificatesReq := &slb20140515.DescribeServerCertificatesRequest{
|
||||
RegionId: tea.String(u.config.Region),
|
||||
}
|
||||
describeServerCertificatesResp, err := u.sdkClient.DescribeServerCertificatesWithOptions(describeServerCertificatesReq, u.sdkRuntime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'slb.DescribeServerCertificates': %w", err)
|
||||
}
|
||||
|
||||
if describeServerCertificatesResp.Body.ServerCertificates != nil && describeServerCertificatesResp.Body.ServerCertificates.ServerCertificate != nil {
|
||||
fingerprint := sha256.Sum256(certX509.Raw)
|
||||
fingerprintHex := hex.EncodeToString(fingerprint[:])
|
||||
for _, certDetail := range describeServerCertificatesResp.Body.ServerCertificates.ServerCertificate {
|
||||
isSameCert := *certDetail.IsAliCloudCertificate == 0 &&
|
||||
strings.EqualFold(fingerprintHex, strings.ReplaceAll(*certDetail.Fingerprint, ":", "")) &&
|
||||
strings.EqualFold(certX509.Subject.CommonName, *certDetail.CommonName)
|
||||
// 如果已存在相同证书,直接返回已有的证书信息
|
||||
if isSameCert {
|
||||
return &UploadResult{
|
||||
CertId: *certDetail.ServerCertificateId,
|
||||
CertName: *certDetail.ServerCertificateName,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 生成新证书名(需符合阿里云命名规则)
|
||||
var certId, certName string
|
||||
certName = fmt.Sprintf("certimate_%d", time.Now().UnixMilli())
|
||||
|
||||
// 上传新证书
|
||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-uploadservercertificate
|
||||
uploadServerCertificateReq := &slb20140515.UploadServerCertificateRequest{
|
||||
RegionId: tea.String(u.config.Region),
|
||||
ServerCertificateName: tea.String(certName),
|
||||
ServerCertificate: tea.String(certPem),
|
||||
PrivateKey: tea.String(privkeyPem),
|
||||
}
|
||||
uploadServerCertificateResp, err := u.sdkClient.UploadServerCertificateWithOptions(uploadServerCertificateReq, u.sdkRuntime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'slb.UploadServerCertificate': %w", err)
|
||||
}
|
||||
|
||||
certId = *uploadServerCertificateResp.Body.ServerCertificateId
|
||||
return &UploadResult{
|
||||
CertId: certId,
|
||||
CertName: certName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *AliyunSLBUploader) createSdkClient(accessKeyId, accessKeySecret, region string) (*slb20140515.Client, error) {
|
||||
if region == "" {
|
||||
region = "cn-hangzhou" // SLB 服务默认区域:华东一杭州
|
||||
}
|
||||
|
||||
aConfig := &openapi.Config{
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
}
|
||||
|
||||
var endpoint string
|
||||
switch region {
|
||||
case "cn-hangzhou":
|
||||
case "cn-hangzhou-finance":
|
||||
case "cn-shanghai-finance-1":
|
||||
case "cn-shenzhen-finance-1":
|
||||
endpoint = "slb.aliyuncs.com"
|
||||
default:
|
||||
endpoint = fmt.Sprintf("slb.%s.aliyuncs.com", region)
|
||||
}
|
||||
aConfig.Endpoint = tea.String(endpoint)
|
||||
|
||||
client, err := slb20140515.NewClient(aConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
@ -7,6 +7,23 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 比较两个 x509.Certificate 对象,判断它们是否是同一张证书。
|
||||
// 注意,这不是精确比较,而只是基于证书序列号和数字签名的快速判断,但对于权威 CA 签发的证书来说不会存在误判。
|
||||
//
|
||||
// 入参:
|
||||
// - a: 待比较的第一个 x509.Certificate 对象。
|
||||
// - b: 待比较的第二个 x509.Certificate 对象。
|
||||
//
|
||||
// 出参:
|
||||
// - 是否相同。
|
||||
func EqualCertificate(a, b *x509.Certificate) bool {
|
||||
return string(a.Signature) == string(b.Signature) &&
|
||||
a.SignatureAlgorithm == b.SignatureAlgorithm &&
|
||||
a.SerialNumber.String() == b.SerialNumber.String() &&
|
||||
a.Issuer.SerialNumber == b.Issuer.SerialNumber &&
|
||||
a.Subject.SerialNumber == b.Subject.SerialNumber
|
||||
}
|
||||
|
||||
// 从 PEM 编码的证书字符串解析并返回一个 x509.Certificate 对象。
|
||||
//
|
||||
// 入参:
|
||||
@ -31,26 +48,40 @@ func ParseCertificateFromPEM(certPem string) (cert *x509.Certificate, err error)
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// 比较两个 x509.Certificate 对象,判断它们是否是同一张证书。
|
||||
// 注意,这不是精确比较,而只是基于证书序列号和数字签名的快速判断,但对于权威 CA 签发的证书来说不会存在误判。
|
||||
// 从 PEM 编码的私钥字符串解析并返回一个 ECDSA 私钥对象。
|
||||
//
|
||||
// 入参:
|
||||
// - a: 待比较的第一个 x509.Certificate 对象。
|
||||
// - b: 待比较的第二个 x509.Certificate 对象。
|
||||
// - privkeyPem: 私钥 PEM 内容。
|
||||
//
|
||||
// 出参:
|
||||
// - 是否相同。
|
||||
func EqualCertificate(a, b *x509.Certificate) bool {
|
||||
return string(a.Signature) == string(b.Signature) &&
|
||||
a.SignatureAlgorithm == b.SignatureAlgorithm &&
|
||||
a.SerialNumber.String() == b.SerialNumber.String() &&
|
||||
a.Issuer.SerialNumber == b.Issuer.SerialNumber &&
|
||||
a.Subject.SerialNumber == b.Subject.SerialNumber
|
||||
// - privkey: ecdsa.PrivateKey 对象。
|
||||
// - err: 错误。
|
||||
func ParseECPrivateKeyFromPEM(privkeyPem string) (privkey *ecdsa.PrivateKey, err error) {
|
||||
pemData := []byte(privkeyPem)
|
||||
|
||||
block, _ := pem.Decode(pemData)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
|
||||
privkey, err = x509.ParseECPrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse private key: %w", err)
|
||||
}
|
||||
|
||||
return privkey, nil
|
||||
}
|
||||
|
||||
// 将 ECDSA 私钥转换为 PEM 格式的字符串。
|
||||
func PrivateKeyToPEM(privateKey *ecdsa.PrivateKey) (string, error) {
|
||||
data, err := x509.MarshalECPrivateKey(privateKey)
|
||||
// 将 ECDSA 私钥转换为 PEM 编码的字符串。
|
||||
//
|
||||
// 入参:
|
||||
// - privkey: ecdsa.PrivateKey 对象。
|
||||
//
|
||||
// 出参:
|
||||
// - privkeyPem: 私钥 PEM 内容。
|
||||
// - err: 错误。
|
||||
func ConvertECPrivateKeyToPEM(privkey *ecdsa.PrivateKey) (privkeyPem string, err error) {
|
||||
data, err := x509.MarshalECPrivateKey(privkey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal EC private key: %w", err)
|
||||
}
|
||||
@ -62,20 +93,3 @@ func PrivateKeyToPEM(privateKey *ecdsa.PrivateKey) (string, error) {
|
||||
|
||||
return string(pem.EncodeToMemory(block)), nil
|
||||
}
|
||||
|
||||
// 从 PEM 编码的私钥字符串解析并返回一个 ECDSA 私钥对象。
|
||||
func ParsePrivateKeyFromPEM(privateKeyPem string) (*ecdsa.PrivateKey, error) {
|
||||
pemData := []byte(privateKeyPem)
|
||||
|
||||
block, _ := pem.Decode(pemData)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParseECPrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse private key: %w", err)
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ import AccessEditDialog from "./AccessEditDialog";
|
||||
import { Context as DeployEditContext } from "./DeployEdit";
|
||||
import DeployToAliyunOSS from "./DeployToAliyunOSS";
|
||||
import DeployToAliyunCDN from "./DeployToAliyunCDN";
|
||||
import DeployToAliyunCLB from "./DeployToAliyunCLB";
|
||||
import DeployToAliyunALB from "./DeployToAliyunALB";
|
||||
import DeployToAliyunNLB from "./DeployToAliyunNLB";
|
||||
import DeployToTencentCDN from "./DeployToTencentCDN";
|
||||
import DeployToTencentCLB from "./DeployToTencentCLB";
|
||||
import DeployToTencentCOS from "./DeployToTencentCOS";
|
||||
@ -119,6 +122,15 @@ const DeployEditDialog = ({ trigger, deployConfig, onSave }: DeployEditDialogPro
|
||||
case "aliyun-dcdn":
|
||||
childComponent = <DeployToAliyunCDN />;
|
||||
break;
|
||||
case "aliyun-clb":
|
||||
childComponent = <DeployToAliyunCLB />;
|
||||
break;
|
||||
case "aliyun-alb":
|
||||
childComponent = <DeployToAliyunALB />;
|
||||
break;
|
||||
case "aliyun-nlb":
|
||||
childComponent = <DeployToAliyunNLB />;
|
||||
break;
|
||||
case "tencent-cdn":
|
||||
case "tencent-ecdn":
|
||||
childComponent = <DeployToTencentCDN />;
|
||||
|
162
ui/src/components/certimate/DeployToAliyunALB.tsx
Normal file
162
ui/src/components/certimate/DeployToAliyunALB.tsx
Normal file
@ -0,0 +1,162 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
const DeployToAliyunALB = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { deploy: data, setDeploy, error, setError } = useDeployEditContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!data.id) {
|
||||
setDeploy({
|
||||
...data,
|
||||
config: {
|
||||
region: "cn-hangzhou",
|
||||
resourceType: "",
|
||||
loadbalancerId: "",
|
||||
listenerId: "",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setError({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
region: z.string().min(1, t("domain.deployment.form.aliyun_alb_region.placeholder")),
|
||||
resourceType: z.union([z.literal("loadbalancer"), z.literal("listener")], {
|
||||
message: t("domain.deployment.form.aliyun_alb_resource_type.placeholder"),
|
||||
}),
|
||||
loadbalancerId: z.string().optional(),
|
||||
listenerId: z.string().optional(),
|
||||
})
|
||||
.refine((data) => (data.resourceType === "loadbalancer" ? !!data.loadbalancerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_alb_loadbalancer_id.placeholder"),
|
||||
path: ["loadbalancerId"],
|
||||
})
|
||||
.refine((data) => (data.resourceType === "listener" ? !!data.listenerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_alb_listener_id.placeholder"),
|
||||
path: ["listenerId"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(data.config);
|
||||
if (!res.success) {
|
||||
setError({
|
||||
...error,
|
||||
region: res.error.errors.find((e) => e.path[0] === "region")?.message,
|
||||
resourceType: res.error.errors.find((e) => e.path[0] === "resourceType")?.message,
|
||||
loadbalancerId: res.error.errors.find((e) => e.path[0] === "loadbalancerId")?.message,
|
||||
listenerId: res.error.errors.find((e) => e.path[0] === "listenerId")?.message,
|
||||
});
|
||||
} else {
|
||||
setError({
|
||||
...error,
|
||||
region: undefined,
|
||||
resourceType: undefined,
|
||||
loadbalancerId: undefined,
|
||||
listenerId: undefined,
|
||||
});
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_alb_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_alb_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.region}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_alb_resource_type.label")}</Label>
|
||||
<Select
|
||||
value={data?.config?.resourceType}
|
||||
onValueChange={(value) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.resourceType = value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.aliyun_alb_resource_type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="loadbalancer">{t("domain.deployment.form.aliyun_alb_resource_type.option.loadbalancer.label")}</SelectItem>
|
||||
<SelectItem value="listener">{t("domain.deployment.form.aliyun_alb_resource_type.option.listener.label")}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.resourceType}</div>
|
||||
</div>
|
||||
|
||||
{data?.config?.resourceType === "loadbalancer" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_alb_loadbalancer_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_alb_loadbalancer_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.loadbalancerId}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.loadbalancerId = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.loadbalancerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{data?.config?.resourceType === "listener" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_alb_listener_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_alb_listener_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.listenerId}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.listenerId = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.listenerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToAliyunALB;
|
158
ui/src/components/certimate/DeployToAliyunCLB.tsx
Normal file
158
ui/src/components/certimate/DeployToAliyunCLB.tsx
Normal file
@ -0,0 +1,158 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
const DeployToAliyunCLB = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { deploy: data, setDeploy, error, setError } = useDeployEditContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!data.id) {
|
||||
setDeploy({
|
||||
...data,
|
||||
config: {
|
||||
region: "cn-hangzhou",
|
||||
resourceType: "",
|
||||
loadbalancerId: "",
|
||||
listenerPort: "443",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setError({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
region: z.string().min(1, t("domain.deployment.form.aliyun_clb_region.placeholder")),
|
||||
resourceType: z.union([z.literal("certificate"), z.literal("loadbalancer"), z.literal("listener")], {
|
||||
message: t("domain.deployment.form.aliyun_clb_resource_type.placeholder"),
|
||||
}),
|
||||
loadbalancerId: z.string().optional(),
|
||||
listenerPort: z.string().optional(),
|
||||
})
|
||||
.refine((data) => (data.resourceType === "loadbalancer" || data.resourceType === "listener" ? !!data.loadbalancerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_clb_loadbalancer_id.placeholder"),
|
||||
path: ["loadbalancerId"],
|
||||
})
|
||||
.refine((data) => (data.resourceType === "listener" ? +data.listenerPort! > 0 && +data.listenerPort! < 65535 : true), {
|
||||
message: t("domain.deployment.form.aliyun_clb_listener_port.placeholder"),
|
||||
path: ["listenerPort"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(data.config);
|
||||
if (!res.success) {
|
||||
setError({
|
||||
...error,
|
||||
region: res.error.errors.find((e) => e.path[0] === "region")?.message,
|
||||
resourceType: res.error.errors.find((e) => e.path[0] === "resourceType")?.message,
|
||||
loadbalancerId: res.error.errors.find((e) => e.path[0] === "loadbalancerId")?.message,
|
||||
listenerPort: res.error.errors.find((e) => e.path[0] === "listenerPort")?.message,
|
||||
});
|
||||
} else {
|
||||
setError({
|
||||
...error,
|
||||
region: undefined,
|
||||
resourceType: undefined,
|
||||
loadbalancerId: undefined,
|
||||
listenerPort: undefined,
|
||||
});
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_clb_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_clb_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.region}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_clb_resource_type.label")}</Label>
|
||||
<Select
|
||||
value={data?.config?.resourceType}
|
||||
onValueChange={(value) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.resourceType = value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.aliyun_clb_resource_type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="loadbalancer">{t("domain.deployment.form.aliyun_clb_resource_type.option.loadbalancer.label")}</SelectItem>
|
||||
<SelectItem value="listener">{t("domain.deployment.form.aliyun_clb_resource_type.option.listener.label")}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.resourceType}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_clb_loadbalancer_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_clb_loadbalancer_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.loadbalancerId}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.loadbalancerId = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.loadbalancerId}</div>
|
||||
</div>
|
||||
|
||||
{data?.config?.resourceType === "listener" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_clb_listener_port.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_clb_listener_port.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.listenerPort}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.listenerPort = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.listenerPort}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToAliyunCLB;
|
162
ui/src/components/certimate/DeployToAliyunNLB.tsx
Normal file
162
ui/src/components/certimate/DeployToAliyunNLB.tsx
Normal file
@ -0,0 +1,162 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { produce } from "immer";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
|
||||
const DeployToAliyunNLB = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { deploy: data, setDeploy, error, setError } = useDeployEditContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!data.id) {
|
||||
setDeploy({
|
||||
...data,
|
||||
config: {
|
||||
region: "cn-hangzhou",
|
||||
resourceType: "",
|
||||
loadbalancerId: "",
|
||||
listenerId: "",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setError({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
region: z.string().min(1, t("domain.deployment.form.aliyun_nlb_region.placeholder")),
|
||||
resourceType: z.union([z.literal("loadbalancer"), z.literal("listener")], {
|
||||
message: t("domain.deployment.form.aliyun_nlb_resource_type.placeholder"),
|
||||
}),
|
||||
loadbalancerId: z.string().optional(),
|
||||
listenerId: z.string().optional(),
|
||||
})
|
||||
.refine((data) => (data.resourceType === "loadbalancer" ? !!data.loadbalancerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_nlb_loadbalancer_id.placeholder"),
|
||||
path: ["loadbalancerId"],
|
||||
})
|
||||
.refine((data) => (data.resourceType === "listener" ? !!data.listenerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.aliyun_nlb_listener_id.placeholder"),
|
||||
path: ["listenerId"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(data.config);
|
||||
if (!res.success) {
|
||||
setError({
|
||||
...error,
|
||||
region: res.error.errors.find((e) => e.path[0] === "region")?.message,
|
||||
resourceType: res.error.errors.find((e) => e.path[0] === "resourceType")?.message,
|
||||
loadbalancerId: res.error.errors.find((e) => e.path[0] === "loadbalancerId")?.message,
|
||||
listenerId: res.error.errors.find((e) => e.path[0] === "listenerId")?.message,
|
||||
});
|
||||
} else {
|
||||
setError({
|
||||
...error,
|
||||
region: undefined,
|
||||
resourceType: undefined,
|
||||
loadbalancerId: undefined,
|
||||
listenerId: undefined,
|
||||
});
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_nlb_region.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_nlb_region.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.region}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.region = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.region}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_nlb_resource_type.label")}</Label>
|
||||
<Select
|
||||
value={data?.config?.resourceType}
|
||||
onValueChange={(value) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.resourceType = value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.aliyun_nlb_resource_type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="loadbalancer">{t("domain.deployment.form.aliyun_nlb_resource_type.option.loadbalancer.label")}</SelectItem>
|
||||
<SelectItem value="listener">{t("domain.deployment.form.aliyun_nlb_resource_type.option.listener.label")}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.resourceType}</div>
|
||||
</div>
|
||||
|
||||
{data?.config?.resourceType === "loadbalancer" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_nlb_loadbalancer_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_nlb_loadbalancer_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.loadbalancerId}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.loadbalancerId = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.loadbalancerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{data?.config?.resourceType === "listener" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.aliyun_nlb_listener_id.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.aliyun_nlb_listener_id.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.listenerId}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.listenerId = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.listenerId}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployToAliyunNLB;
|
@ -44,7 +44,7 @@ const DeployToHuaweiCloudCDN = () => {
|
||||
message: t("domain.deployment.form.huaweicloud_elb_certificate_id.placeholder"),
|
||||
path: ["certificateId"],
|
||||
})
|
||||
.refine((data) => (data.resourceType === "loadbalancer" ? !!data.certificateId?.trim() : true), {
|
||||
.refine((data) => (data.resourceType === "loadbalancer" ? !!data.loadbalancerId?.trim() : true), {
|
||||
message: t("domain.deployment.form.huaweicloud_elb_loadbalancer_id.placeholder"),
|
||||
path: ["loadbalancerId"],
|
||||
})
|
||||
|
@ -75,6 +75,9 @@ export const deployTargetsMap: Map<DeployTarget["type"], DeployTarget> = new Map
|
||||
["aliyun-oss", "common.provider.aliyun.oss", "/imgs/providers/aliyun.svg"],
|
||||
["aliyun-cdn", "common.provider.aliyun.cdn", "/imgs/providers/aliyun.svg"],
|
||||
["aliyun-dcdn", "common.provider.aliyun.dcdn", "/imgs/providers/aliyun.svg"],
|
||||
["aliyun-clb", "common.provider.aliyun.clb", "/imgs/providers/aliyun.svg"],
|
||||
["aliyun-alb", "common.provider.aliyun.alb", "/imgs/providers/aliyun.svg"],
|
||||
["aliyun-nlb", "common.provider.aliyun.nlb", "/imgs/providers/aliyun.svg"],
|
||||
["tencent-cdn", "common.provider.tencent.cdn", "/imgs/providers/tencent.svg"],
|
||||
["tencent-ecdn", "common.provider.tencent.ecdn", "/imgs/providers/tencent.svg"],
|
||||
["tencent-clb", "common.provider.tencent.clb", "/imgs/providers/tencent.svg"],
|
||||
|
@ -57,6 +57,9 @@
|
||||
"common.provider.aliyun.oss": "Alibaba Cloud - OSS",
|
||||
"common.provider.aliyun.cdn": "Alibaba Cloud - CDN",
|
||||
"common.provider.aliyun.dcdn": "Alibaba Cloud - DCDN",
|
||||
"common.provider.aliyun.clb": "Alibaba Cloud - CLB",
|
||||
"common.provider.aliyun.alb": "Alibaba Cloud - ALB",
|
||||
"common.provider.aliyun.nlb": "Alibaba Cloud - NLB",
|
||||
"common.provider.tencent": "Tencent Cloud",
|
||||
"common.provider.tencent.cdn": "Tencent Cloud - CDN",
|
||||
"common.provider.tencent.ecdn": "Tencent Cloud - ECDN",
|
||||
|
@ -61,6 +61,36 @@
|
||||
"domain.deployment.form.aliyun_oss_endpoint.placeholder": "Please enter endpoint",
|
||||
"domain.deployment.form.aliyun_oss_bucket.label": "Bucket",
|
||||
"domain.deployment.form.aliyun_oss_bucket.placeholder": "Please enter bucket",
|
||||
"domain.deployment.form.aliyun_clb_region.label": "Region",
|
||||
"domain.deployment.form.aliyun_clb_region.placeholder": "Please enter region (e.g. cn-hangzhou)",
|
||||
"domain.deployment.form.aliyun_clb_resource_type.label": "Resource Type",
|
||||
"domain.deployment.form.aliyun_clb_resource_type.placeholder": "Please select CLB resource type",
|
||||
"domain.deployment.form.aliyun_clb_resource_type.option.loadbalancer.label": "CLB LoadBalancer",
|
||||
"domain.deployment.form.aliyun_clb_resource_type.option.listener.label": "CLB Listener",
|
||||
"domain.deployment.form.aliyun_clb_loadbalancer_id.label": "LoadBalancer ID",
|
||||
"domain.deployment.form.aliyun_clb_loadbalancer_id.placeholder": "Please enter CLB loadbalancer ID",
|
||||
"domain.deployment.form.aliyun_clb_listener_port.label": "Listener Port",
|
||||
"domain.deployment.form.aliyun_clb_listener_port.placeholder": "Please enter CLB listener port",
|
||||
"domain.deployment.form.aliyun_alb_region.label": "Region",
|
||||
"domain.deployment.form.aliyun_alb_region.placeholder": "Please enter region (e.g. cn-hangzhou)",
|
||||
"domain.deployment.form.aliyun_alb_resource_type.label": "Resource Type",
|
||||
"domain.deployment.form.aliyun_alb_resource_type.placeholder": "Please select ALB resource type",
|
||||
"domain.deployment.form.aliyun_alb_resource_type.option.loadbalancer.label": "ALB LoadBalancer",
|
||||
"domain.deployment.form.aliyun_alb_resource_type.option.listener.label": "ALB Listener",
|
||||
"domain.deployment.form.aliyun_alb_loadbalancer_id.label": "LoadBalancer ID",
|
||||
"domain.deployment.form.aliyun_alb_loadbalancer_id.placeholder": "Please enter ALB loadbalancer ID",
|
||||
"domain.deployment.form.aliyun_alb_listener_id.label": "Listener ID",
|
||||
"domain.deployment.form.aliyun_alb_listener_id.placeholder": "Please enter ALB listener ID",
|
||||
"domain.deployment.form.aliyun_nlb_region.label": "Region",
|
||||
"domain.deployment.form.aliyun_nlb_region.placeholder": "Please enter region (e.g. cn-hangzhou)",
|
||||
"domain.deployment.form.aliyun_nlb_resource_type.label": "Resource Type",
|
||||
"domain.deployment.form.aliyun_nlb_resource_type.placeholder": "Please select NLB resource type",
|
||||
"domain.deployment.form.aliyun_nlb_resource_type.option.loadbalancer.label": "NLB LoadBalancer",
|
||||
"domain.deployment.form.aliyun_nlb_resource_type.option.listener.label": "NLB Listener",
|
||||
"domain.deployment.form.aliyun_nlb_loadbalancer_id.label": "LoadBalancer ID",
|
||||
"domain.deployment.form.aliyun_nlb_loadbalancer_id.placeholder": "Please enter NLB loadbalancer ID",
|
||||
"domain.deployment.form.aliyun_nlb_listener_id.label": "Listener ID",
|
||||
"domain.deployment.form.aliyun_nlb_listener_id.placeholder": "Please enter NLB listener ID",
|
||||
"domain.deployment.form.tencent_cos_region.label": "Region",
|
||||
"domain.deployment.form.tencent_cos_region.placeholder": "Please enter region (e.g. ap-guangzhou)",
|
||||
"domain.deployment.form.tencent_cos_bucket.label": "Bucket",
|
||||
|
@ -54,20 +54,23 @@
|
||||
"common.errmsg.zoneid_invalid": "请输入正确的 Zone ID",
|
||||
|
||||
"common.provider.aliyun": "阿里云",
|
||||
"common.provider.aliyun.oss": "阿里云 - OSS",
|
||||
"common.provider.aliyun.cdn": "阿里云 - CDN",
|
||||
"common.provider.aliyun.dcdn": "阿里云 - DCDN",
|
||||
"common.provider.aliyun.oss": "阿里云 - 对象存储 OSS",
|
||||
"common.provider.aliyun.cdn": "阿里云 - 内容分发网络 CDN",
|
||||
"common.provider.aliyun.dcdn": "阿里云 - 全站加速 DCDN",
|
||||
"common.provider.aliyun.clb": "阿里云 - 传统型负载均衡 CLB",
|
||||
"common.provider.aliyun.alb": "阿里云 - 应用型负载均衡 ALB",
|
||||
"common.provider.aliyun.nlb": "阿里云 - 网络型负载均衡 NLB",
|
||||
"common.provider.tencent": "腾讯云",
|
||||
"common.provider.tencent.cos": "腾讯云 - COS",
|
||||
"common.provider.tencent.cdn": "腾讯云 - CDN",
|
||||
"common.provider.tencent.ecdn": "腾讯云 - ECDN",
|
||||
"common.provider.tencent.clb": "腾讯云 - CLB",
|
||||
"common.provider.tencent.teo": "腾讯云 - TEO",
|
||||
"common.provider.tencent.cos": "腾讯云 - 对象存储 COS",
|
||||
"common.provider.tencent.cdn": "腾讯云 - 内容分发网络 CDN",
|
||||
"common.provider.tencent.ecdn": "腾讯云 - 全站加速网络 ECDN",
|
||||
"common.provider.tencent.clb": "腾讯云 - 负载均衡 CLB",
|
||||
"common.provider.tencent.teo": "腾讯云 - 边缘安全加速平台 EO",
|
||||
"common.provider.huaweicloud": "华为云",
|
||||
"common.provider.huaweicloud.cdn": "华为云 - CDN",
|
||||
"common.provider.huaweicloud.elb": "华为云 - ELB",
|
||||
"common.provider.huaweicloud.cdn": "华为云 - 内容分发网络 CDN",
|
||||
"common.provider.huaweicloud.elb": "华为云 - 弹性负载均衡 ELB",
|
||||
"common.provider.qiniu": "七牛云",
|
||||
"common.provider.qiniu.cdn": "七牛云 - CDN",
|
||||
"common.provider.qiniu.cdn": "七牛云 - 内容分发网络 CDN",
|
||||
"common.provider.aws": "AWS",
|
||||
"common.provider.cloudflare": "Cloudflare",
|
||||
"common.provider.namesilo": "Namesilo",
|
||||
|
@ -61,6 +61,36 @@
|
||||
"domain.deployment.form.aliyun_oss_endpoint.placeholder": "请输入 Endpoint",
|
||||
"domain.deployment.form.aliyun_oss_bucket.label": "存储桶",
|
||||
"domain.deployment.form.aliyun_oss_bucket.placeholder": "请输入存储桶名",
|
||||
"domain.deployment.form.aliyun_clb_region.label": "地域",
|
||||
"domain.deployment.form.aliyun_clb_region.placeholder": "请输入地域(如 cn-hangzhou)",
|
||||
"domain.deployment.form.aliyun_clb_resource_type.label": "替换方式",
|
||||
"domain.deployment.form.aliyun_clb_resource_type.placeholder": "请选择替换方式",
|
||||
"domain.deployment.form.aliyun_clb_resource_type.option.loadbalancer.label": "替换指定负载均衡器的全部监听的证书(仅支持 HTTPS 监听)",
|
||||
"domain.deployment.form.aliyun_clb_resource_type.option.listener.label": "替换指定负载均衡监听的证书",
|
||||
"domain.deployment.form.aliyun_clb_loadbalancer_id.label": "负载均衡器 ID",
|
||||
"domain.deployment.form.aliyun_clb_loadbalancer_id.placeholder": "请输入负载均衡器 ID",
|
||||
"domain.deployment.form.aliyun_clb_listener_port.label": "监听端口",
|
||||
"domain.deployment.form.aliyun_clb_listener_port.placeholder": "请输入监听端口",
|
||||
"domain.deployment.form.aliyun_alb_region.label": "地域",
|
||||
"domain.deployment.form.aliyun_alb_region.placeholder": "请输入地域(如 cn-hangzhou)",
|
||||
"domain.deployment.form.aliyun_alb_resource_type.label": "替换方式",
|
||||
"domain.deployment.form.aliyun_alb_resource_type.placeholder": "请选择替换方式",
|
||||
"domain.deployment.form.aliyun_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器的全部监听的证书(仅支持 HTTPS/QUIC 监听)",
|
||||
"domain.deployment.form.aliyun_alb_resource_type.option.listener.label": "替换指定监听器的证书",
|
||||
"domain.deployment.form.aliyun_alb_loadbalancer_id.label": "负载均衡器 ID",
|
||||
"domain.deployment.form.aliyun_alb_loadbalancer_id.placeholder": "请输入负载均衡器 ID",
|
||||
"domain.deployment.form.aliyun_alb_listener_id.label": "监听器 ID",
|
||||
"domain.deployment.form.aliyun_alb_listener_id.placeholder": "请输入监听器 ID",
|
||||
"domain.deployment.form.aliyun_nlb_region.label": "地域",
|
||||
"domain.deployment.form.aliyun_nlb_region.placeholder": "请输入地域(如 cn-hangzhou)",
|
||||
"domain.deployment.form.aliyun_nlb_resource_type.label": "替换方式",
|
||||
"domain.deployment.form.aliyun_nlb_resource_type.placeholder": "请选择替换方式",
|
||||
"domain.deployment.form.aliyun_nlb_resource_type.option.loadbalancer.label": "替换指定负载均衡器的全部监听的证书(仅支持 TCPSSL 监听)",
|
||||
"domain.deployment.form.aliyun_nlb_resource_type.option.listener.label": "替换指定监听器的证书",
|
||||
"domain.deployment.form.aliyun_nlb_loadbalancer_id.label": "负载均衡器 ID",
|
||||
"domain.deployment.form.aliyun_nlb_loadbalancer_id.placeholder": "请输入负载均衡器 ID",
|
||||
"domain.deployment.form.aliyun_nlb_listener_id.label": "监听器 ID",
|
||||
"domain.deployment.form.aliyun_nlb_listener_id.placeholder": "请输入监听器 ID",
|
||||
"domain.deployment.form.tencent_cos_region.label": "地域",
|
||||
"domain.deployment.form.tencent_cos_region.placeholder": "请输入地域(如 ap-guangzhou)",
|
||||
"domain.deployment.form.tencent_cos_bucket.label": "存储桶",
|
||||
@ -79,17 +109,17 @@
|
||||
"domain.deployment.form.tencent_teo_domain.placeholder": "请输入部署到的域名",
|
||||
"domain.deployment.form.huaweicloud_elb_region.label": "地域",
|
||||
"domain.deployment.form.huaweicloud_elb_region.placeholder": "请输入地域(如 cn-north-1)",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.label": "资源类型替换方式",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.placeholder": "请选择资源类型替换方式",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.option.certificate.label": "按证书替换",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.option.loadbalancer.label": "按负载均衡器替换",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.option.listener.label": "按监听器替换",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.label": "替换方式",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.placeholder": "请选择替换方式",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.option.certificate.label": "替换指定证书",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.option.loadbalancer.label": "替换指定负载均衡器的全部监听器的证书(仅支持 HTTPS 监听)",
|
||||
"domain.deployment.form.huaweicloud_elb_resource_type.option.listener.label": "替换指定监听器",
|
||||
"domain.deployment.form.huaweicloud_elb_certificate_id.label": "证书 ID",
|
||||
"domain.deployment.form.huaweicloud_elb_certificate_id.placeholder": "请输入证书 ID(可从华为云控制面板获取)",
|
||||
"domain.deployment.form.huaweicloud_elb_certificate_id.placeholder": "请输入证书 ID",
|
||||
"domain.deployment.form.huaweicloud_elb_loadbalancer_id.label": "负载均衡器 ID",
|
||||
"domain.deployment.form.huaweicloud_elb_loadbalancer_id.placeholder": "请输入负载均衡器 ID(可从华为云控制面板获取)",
|
||||
"domain.deployment.form.huaweicloud_elb_loadbalancer_id.placeholder": "请输入负载均衡器 ID",
|
||||
"domain.deployment.form.huaweicloud_elb_listener_id.label": "监听器 ID",
|
||||
"domain.deployment.form.huaweicloud_elb_listener_id.placeholder": "请输入监听器 ID(可从华为云控制面板获取)",
|
||||
"domain.deployment.form.huaweicloud_elb_listener_id.placeholder": "请输入监听器 ID",
|
||||
"domain.deployment.form.ssh_key_path.label": "私钥保存路径",
|
||||
"domain.deployment.form.ssh_key_path.placeholder": "请输入私钥保存路径",
|
||||
"domain.deployment.form.ssh_cert_path.label": "证书保存路径",
|
||||
|
Loading…
x
Reference in New Issue
Block a user