feat(provider): new deployment provider: aws clb

This commit is contained in:
Fu Diwei 2026-06-04 18:50:05 +08:00 committed by RHQYZ
parent ed9f5b3322
commit fc7975972d
19 changed files with 434 additions and 38 deletions

13
go.mod
View File

@ -30,11 +30,12 @@ require (
github.com/alibabacloud-go/vod-20170321/v4 v4.11.3
github.com/alibabacloud-go/waf-openapi-20211001/v7 v7.7.0
github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.5.1
github.com/aws/aws-sdk-go-v2 v1.41.8
github.com/aws/aws-sdk-go-v2 v1.41.11
github.com/aws/aws-sdk-go-v2/config v1.32.19
github.com/aws/aws-sdk-go-v2/credentials v1.19.18
github.com/aws/aws-sdk-go-v2/service/acm v1.38.3
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.63.0
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.34.2
github.com/aws/aws-sdk-go-v2/service/iam v1.53.10
github.com/aws/aws-sdk-go-v2/service/lightsail v1.54.1
github.com/baidubce/bce-sdk-go v0.9.267
@ -84,8 +85,10 @@ require (
gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1
gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0
golang.org/x/crypto v0.52.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sync v0.20.0
golang.org/x/sys v0.45.0
google.golang.org/api v0.280.0
google.golang.org/protobuf v1.36.11
k8s.io/api v0.35.3
k8s.io/apimachinery v0.35.3
@ -186,8 +189,8 @@ require (
github.com/aliyun/credentials-go v1.4.12 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.24 // indirect
@ -195,7 +198,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.30.18 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.42.2 // indirect
github.com/aws/smithy-go v1.25.1 // indirect
github.com/aws/smithy-go v1.27.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/bodgit/gssapi v0.0.3 // indirect
github.com/bodgit/tsig v1.3.1 // indirect
@ -277,12 +280,10 @@ require (
golang.org/x/image v0.40.0 // indirect
golang.org/x/mod v0.36.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/term v0.43.0 // indirect
golang.org/x/text v0.37.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.44.0 // indirect
google.golang.org/api v0.280.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect
google.golang.org/grpc v1.81.1 // indirect
gopkg.in/ini.v1 v1.67.2 // indirect

18
go.sum
View File

@ -203,18 +203,18 @@ github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.41.8 h1:sRs7nG6/RiEBZ/K5UO2sNw0w40U02Nmz1VtARloTZXk=
github.com/aws/aws-sdk-go-v2 v1.41.8/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc=
github.com/aws/aws-sdk-go-v2 v1.41.11 h1:9PRf7jyTMEUM6fuNRAJa2mO/skJfrF50rENJwf2LXqw=
github.com/aws/aws-sdk-go-v2 v1.41.11/go.mod h1:iiUX27gOXRuYaoeUVXhUpPwjJHzISfPAjjcuhUbLSVs=
github.com/aws/aws-sdk-go-v2/config v1.32.19 h1:qRhIJMbevHUvIE7X4TK8N8zye5+5AhapcslPrvB+qKE=
github.com/aws/aws-sdk-go-v2/config v1.32.19/go.mod h1:RbJ24nfoya63+Mf5VI+CGCGk9vEdv28xPeii+gojRYs=
github.com/aws/aws-sdk-go-v2/credentials v1.19.18 h1:GcXQz2M/0ZvMo0v5DakUqbDBeBM1ZNaivkolEF4Esgw=
github.com/aws/aws-sdk-go-v2/credentials v1.19.18/go.mod h1:sHJ06tMGcD3ZpmMyJqV+VBsGilhSIZPIN+ZFy5Dg0C4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24 h1:FQm5ApnyzkuJdXLGskPce83CK1CQKC4RUnIHKVe4BU4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24/go.mod h1:JsC7dqQc55MlZ5mvNsDMMge71u8pVcSzU3RNz2h/5yQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.24 h1:u6kJU2i0va1AgtJsH3RdWKWqHULlTh7zHwb35Womf74=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.24/go.mod h1:7GY+xLcXOFUpCkNwDReft9qOAVg54A4/AnjHIU7sSAY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.24 h1:Xhbcf3KugX6vX7SDyUK205Oicyfg7EGuvoVNyP5L6DM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.24/go.mod h1:rwDgb2HNOGZsnTHylOUedM7Vnl+bCfnXDqUNPsFWYfk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27 h1:8sPbKi1/KRHwl5oR3qN9mUXestCeHuaRutxylnr/eVY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27/go.mod h1:QV9IVIopJ1dpQUno0f9VYDUwOEjj8u0iEJ4JiZVre3Y=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27 h1:9d8AoASQY9UwrOSmiJ7uSM0MGUPFhnenwSvpaFfat2c=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27/go.mod h1:x0rldpsnUQaQIs4Rh+Vwm9Z/0vI6BxadGtsgJfZFb8s=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25 h1:54CTMmlJ71Rk2dYvM9qZOob+39wjlVja2zDLxCu69Ew=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25/go.mod h1:BZaHqxsS9vN1fvV5EfEl0OBLOk5+AajWsMu6MjqnZB4=
github.com/aws/aws-sdk-go-v2/service/acm v1.38.3 h1:Fzab84hCu3rw9R9Y3mH7SHfr/cSEHnCB0Mq1JCdr9t0=
@ -222,6 +222,8 @@ github.com/aws/aws-sdk-go-v2/service/acm v1.38.3/go.mod h1:yCteizCNPaHt0SnNusoGG
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.63.0 h1:YNpvY2mAGhoTHPEvsqPuDb8K5JBjorbVMaitP8i4OXI=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.63.0/go.mod h1:brhMG/gR2xEB5lezxL2Cx+hqsEzGUn4LhNUtu7+ePFE=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.34.2 h1:N69Uy8towDrupiVOPgH3ADlstEQEhXv5QCXOqEfvmJ0=
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.34.2/go.mod h1:1Yr+5r8qPhVQbDlN/kgxzjc++aNkxcKQZpMckhk4+94=
github.com/aws/aws-sdk-go-v2/service/iam v1.53.10 h1:kcN3I3llO7VwIY5w3Pc5FmEonpsr23Ou7Cwk4qf7dik=
github.com/aws/aws-sdk-go-v2/service/iam v1.53.10/go.mod h1:1vkJzjCYC3byO0kIrBqLPzvZpuvYhPXkuyARs6E7tM4=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA=
@ -241,8 +243,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.1/go.mod h1:er0SFJfdV89Rit5hI
github.com/aws/aws-sdk-go-v2/service/sts v1.42.2 h1:XKnxlM4KZH1gktcsh3zSWc7GW4KivEv/OkifmHOhCUY=
github.com/aws/aws-sdk-go-v2/service/sts v1.42.2/go.mod h1:KJYmkQaFB3SUW2j3aBkPsxNmAb4ZsSOvbvCpuxzHJA0=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI=
github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/aws/smithy-go v1.27.0 h1:ZoFioDKJxkSIW2otF9T0aPtNlUwhdVCcuZh/rzH9Hus=
github.com/aws/smithy-go v1.27.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/baidubce/bce-sdk-go v0.9.267 h1:0l3UipFvWVsEv+FcontxWRs6ny8xvBeRISLbdNtO/bw=

View File

@ -0,0 +1,29 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/aws-clb"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeAWSCLB, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForAWS{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
AccessKeyId: credentials.AccessKeyId,
SecretAccessKey: credentials.SecretAccessKey,
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
LoadbalancerName: xmaps.GetString(options.ProviderExtendedConfig, "loadbalancerName"),
LoadbalancerPort: xmaps.GetOrDefaultInt32(options.ProviderExtendedConfig, "loadbalancerPort", 443),
CertificateSource: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "certificateSource", dplyimpl.CERTIFICATE_SOURCE_ACM),
})
return provider, err
})
}

View File

@ -21,7 +21,7 @@ func init() {
SecretAccessKey: credentials.SecretAccessKey,
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
DistributionId: xmaps.GetString(options.ProviderExtendedConfig, "distributionId"),
CertificateSource: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "certificateSource", "ACM"),
CertificateSource: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "certificateSource", dplyimpl.CERTIFICATE_SOURCE_ACM),
})
return provider, err
})

View File

@ -326,6 +326,7 @@ const (
DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf")
DeploymentProviderTypeAPISIX = DeploymentProviderType(AccessProviderTypeAPISIX)
DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm")
DeploymentProviderTypeAWSCLB = DeploymentProviderType(AccessProviderTypeAWS + "-clb")
DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront")
DeploymentProviderTypeAWSIAM = DeploymentProviderType(AccessProviderTypeAWS + "-iam")
DeploymentProviderTypeAzureKeyVault = DeploymentProviderType(AccessProviderTypeAzure + "-keyvault")

View File

@ -77,8 +77,8 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo
}
// 获取证书列表,避免重复上传
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListCertificates.html
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_GetCertificate.html
// REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_ListCertificates.html
// REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_GetCertificate.html
listCertificatesNextToken := (*string)(nil)
for {
select {
@ -127,7 +127,10 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo
// 如果以上信息都一致,则视为已存在相同证书,直接返回
c.logger.Info("ssl certificate already exists")
return &UploadResult{
CertId: *certItem.CertificateArn,
CertId: aws.ToString(certItem.CertificateArn),
ExtendedData: map[string]any{
"Arn": aws.ToString(certItem.CertificateArn),
},
}, nil
}
@ -139,7 +142,7 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo
}
// 导入证书
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ImportCertificate.html
// REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_ImportCertificate.html
importCertificateReq := &awsacm.ImportCertificateInput{
Certificate: ([]byte)(serverCertPEM),
CertificateChain: ([]byte)(intermediaCertPEM),
@ -153,6 +156,9 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo
return &UploadResult{
CertId: aws.ToString(importCertificateResp.CertificateArn),
ExtendedData: map[string]any{
"Arn": aws.ToString(importCertificateResp.CertificateArn),
},
}, nil
}
@ -164,7 +170,7 @@ func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, pri
}
// 导入证书
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ImportCertificate.html
// REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_ImportCertificate.html
importCertificateReq := &awsacm.ImportCertificateInput{
CertificateArn: aws.String(certIdOrName),
Certificate: ([]byte)(serverCertPEM),

View File

@ -1,6 +1,7 @@
package awsiam
import (
"cmp"
"context"
"fmt"
"log/slog"
@ -80,8 +81,8 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo
}
// 获取证书列表,避免重复上传
// REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_ListServerCertificates.html
// REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_GetServerCertificate.html
// REF: https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListServerCertificates.html
// REF: https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetServerCertificate.html
listServerCertificatesMarker := (*string)(nil)
for {
select {
@ -132,6 +133,10 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo
return &UploadResult{
CertId: aws.ToString(certItem.ServerCertificateId),
CertName: aws.ToString(certItem.ServerCertificateName),
ExtendedData: map[string]any{
"Arn": aws.ToString(certItem.Arn),
"Path": aws.ToString(certItem.Path),
},
}, nil
}
@ -146,17 +151,14 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
// 导入证书
// REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_UploadServerCertificate.html
// REF: https://docs.aws.amazon.com/IAM/latest/APIReference/API_UploadServerCertificate.html
uploadServerCertificateReq := &awsiam.UploadServerCertificateInput{
ServerCertificateName: aws.String(certName),
Path: aws.String(c.config.CertificatePath),
Path: aws.String(cmp.Or(c.config.CertificatePath, "/")),
CertificateBody: aws.String(serverCertPEM),
CertificateChain: aws.String(intermediaCertPEM),
PrivateKey: aws.String(privkeyPEM),
}
if c.config.CertificatePath == "" {
uploadServerCertificateReq.Path = aws.String("/")
}
uploadServerCertificateResp, err := c.sdkClient.UploadServerCertificate(ctx, uploadServerCertificateReq)
c.logger.Debug("sdk request 'iam.UploadServerCertificate'", slog.Any("request", uploadServerCertificateReq), slog.Any("response", uploadServerCertificateResp))
if err != nil {
@ -165,7 +167,11 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo
return &UploadResult{
CertId: aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.ServerCertificateId),
CertName: certName,
CertName: aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.ServerCertificateName),
ExtendedData: map[string]any{
"Arn": aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.Arn),
"Path": aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.Path),
},
}, nil
}

View File

@ -0,0 +1,146 @@
package awsclb
import (
"context"
"fmt"
"log/slog"
aws "github.com/aws/aws-sdk-go-v2/aws"
awscfg "github.com/aws/aws-sdk-go-v2/config"
awscred "github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
"github.com/certimate-go/certimate/pkg/core"
cmgrimplacm "github.com/certimate-go/certimate/pkg/core/certmgr/providers/aws-acm"
cmgrimpliam "github.com/certimate-go/certimate/pkg/core/certmgr/providers/aws-iam"
)
type (
Provider = core.Deployer
DeployResult = core.DeployerDeployResult
)
type DeployerConfig struct {
// AWS AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// AWS SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// AWS 区域。
Region string `json:"region"`
// AWS CLB 负载均衡器名称。
LoadbalancerName string `json:"loadbalancerName"`
// AWS CLB 负载均衡器端口。
LoadbalancerPort int32 `json:"loadbalancerPort"`
// AWS CLB 证书来源。
// 可取值 "ACM"、"IAM"。
CertificateSource string `json:"certificateSource"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *elasticloadbalancing.Client
sdkCertmgr core.Certmgr
}
var _ Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.SecretAccessKey, config.Region)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
var pcertmgr core.Certmgr
switch config.CertificateSource {
case CERTIFICATE_SOURCE_ACM:
pcertmgr, err = cmgrimplacm.NewCertmgr(&cmgrimplacm.CertmgrConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
Region: config.Region,
})
if err != nil {
return nil, fmt.Errorf("could not create certmgr: %w", err)
}
case CERTIFICATE_SOURCE_IAM:
pcertmgr, err = cmgrimpliam.NewCertmgr(&cmgrimpliam.CertmgrConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
Region: config.Region,
CertificatePath: "/elb/",
})
if err != nil {
return nil, fmt.Errorf("could not create certmgr: %w", err)
}
default:
return nil, fmt.Errorf("unsupported certificate source: '%s'", config.CertificateSource)
}
return &Deployer{
config: config,
logger: slog.Default(),
sdkClient: client,
sdkCertmgr: pcertmgr,
}, nil
}
func (d *Deployer) SetLogger(logger *slog.Logger) {
if logger == nil {
d.logger = slog.New(slog.DiscardHandler)
} else {
d.logger = logger
}
d.sdkCertmgr.SetLogger(logger)
}
func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*DeployResult, error) {
if d.config.LoadbalancerName == "" {
return nil, fmt.Errorf("config `loadbalancerName` is required")
}
if d.config.LoadbalancerPort == 0 {
return nil, fmt.Errorf("config `loadbalancerPort` is required")
}
// 上传证书
upres, err := d.sdkCertmgr.Upload(ctx, certPEM, privkeyPEM)
if err != nil {
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 替换 HTTPS 侦听器 SSL 证书
// REF: https://docs.aws.amazon.com/elasticloadbalancing/2012-06-01/APIReference/API_SetLoadBalancerListenerSSLCertificate.html
setLoadBalancerListenerSSLCertificateReq := &elasticloadbalancing.SetLoadBalancerListenerSSLCertificateInput{
LoadBalancerName: aws.String(d.config.LoadbalancerName),
LoadBalancerPort: d.config.LoadbalancerPort,
SSLCertificateId: aws.String(upres.ExtendedData["Arn"].(string)),
}
setLoadBalancerListenerSSLCertificateResp, err := d.sdkClient.SetLoadBalancerListenerSSLCertificate(ctx, setLoadBalancerListenerSSLCertificateReq)
d.logger.Debug("sdk request 'elasticloadbalancing.SetLoadBalancerListenerSSLCertificate'", slog.Any("request", setLoadBalancerListenerSSLCertificateReq), slog.Any("response", setLoadBalancerListenerSSLCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'elasticloadbalancing.SetLoadBalancerListenerSSLCertificate': %w", err)
}
return &DeployResult{}, nil
}
func createSDKClient(accessKeyId, secretAccessKey, region string) (*elasticloadbalancing.Client, error) {
cfg, err := awscfg.LoadDefaultConfig(context.Background())
if err != nil {
return nil, err
}
client := elasticloadbalancing.NewFromConfig(cfg, func(o *elasticloadbalancing.Options) {
o.Region = region
o.Credentials = aws.NewCredentialsCache(awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, ""))
})
return client, nil
}

View File

@ -0,0 +1,62 @@
package awsclb_test
import (
"testing"
"github.com/certimate-go/certimate/pkg/core/deployer/internal/tester"
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/aws-clb"
)
var (
fp = tester.Args("AWSCLB_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fSecretAccessKey string
fRegion string
fLoadbalancerName string
fLoadbalancerPort int
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
fp.DefineString(&fRegion, "REGION")
fp.DefineString(&fLoadbalancerName, "LOADBALANCERNAME")
fp.DefineInt(&fLoadbalancerPort, "LOADBALANCERPORT")
}
/*
Shell command to run this test:
go test -v ./aws_clb_test.go -args \
--AWSCLB_TESTCERTPATH="/path/to/your-test-cert.pem" \
--AWSCLB_TESTKEYPATH="/path/to/your-test-key.pem" \
--AWSCLB_ACCESSKEYID="your-access-key-id" \
--AWSCLB_SECRETACCESSKEY="your-secret-access-id" \
--AWSCLB_REGION="us-east-1" \
--AWSCLB_LOADBALANCERNAME="your-loadbalancer-name" \
--AWSCLB_LOADBALANCERPORT=443
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Deploy", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
Region: fRegion,
LoadbalancerName: fLoadbalancerName,
LoadbalancerPort: int32(fLoadbalancerPort),
CertificateSource: impl.CERTIFICATE_SOURCE_ACM,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}

View File

@ -0,0 +1,6 @@
package awsclb
const (
CERTIFICATE_SOURCE_ACM = "ACM"
CERTIFICATE_SOURCE_IAM = "IAM"
)

View File

@ -101,7 +101,7 @@ func (d *Deployer) SetLogger(logger *slog.Logger) {
func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*DeployResult, error) {
if d.config.DistributionId == "" {
return nil, fmt.Errorf("config `distribuitionId` is required")
return nil, fmt.Errorf("config `distributionId` is required")
}
// 上传证书
@ -113,7 +113,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
}
// 获取分配配置
// REF: https://docs.aws.amazon.com/en_us/cloudfront/latest/APIReference/API_GetDistributionConfig.html
// REF: https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_GetDistributionConfig.html
getDistributionConfigReq := &cloudfront.GetDistributionConfigInput{
Id: aws.String(d.config.DistributionId),
}
@ -124,7 +124,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
}
// 更新分配配置
// REF: https://docs.aws.amazon.com/zh_cn/cloudfront/latest/APIReference/API_UpdateDistribution.html
// REF: https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html
updateDistributionReq := &cloudfront.UpdateDistributionInput{
Id: aws.String(d.config.DistributionId),
DistributionConfig: getDistributionConfigResp.DistributionConfig,

View File

@ -14,7 +14,7 @@ var (
fAccessKeyId string
fSecretAccessKey string
fRegion string
fDistribuitionId string
fDistributionId string
)
func init() {
@ -23,7 +23,7 @@ func init() {
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
fp.DefineString(&fRegion, "REGION")
fp.DefineString(&fDistribuitionId, "DISTRIBUTIONID")
fp.DefineString(&fDistributionId, "DISTRIBUTIONID")
}
/*
@ -42,10 +42,11 @@ func TestProvider(t *testing.T) {
t.Run("Deploy", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
Region: fRegion,
DistributionId: fDistribuitionId,
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
Region: fRegion,
DistributionId: fDistributionId,
CertificateSource: impl.CERTIFICATE_SOURCE_ACM,
})
if err != nil {
t.Errorf("err: %+v", err)

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { DEPLOYMENT_PROVIDERS, type DeploymentProviderType } from "@/domain/provider";
@ -23,6 +23,7 @@ import BizDeployNodeConfigFieldsProviderAliyunVOD from "./BizDeployNodeConfigFie
import BizDeployNodeConfigFieldsProviderAliyunWAF from "./BizDeployNodeConfigFieldsProviderAliyunWAF";
import BizDeployNodeConfigFieldsProviderAPISIX from "./BizDeployNodeConfigFieldsProviderAPISIX";
import BizDeployNodeConfigFieldsProviderAWSACM from "./BizDeployNodeConfigFieldsProviderAWSACM";
import BizDeployNodeConfigFieldsProviderAWSCLB from "./BizDeployNodeConfigFieldsProviderAWSCLB";
import BizDeployNodeConfigFieldsProviderAWSCloudFront from "./BizDeployNodeConfigFieldsProviderAWSCloudFront";
import BizDeployNodeConfigFieldsProviderAWSIAM from "./BizDeployNodeConfigFieldsProviderAWSIAM";
import BizDeployNodeConfigFieldsProviderAzureKeyVault from "./BizDeployNodeConfigFieldsProviderAzureKeyVault";
@ -157,6 +158,7 @@ const providerComponentMap: Partial<Record<DeploymentProviderType, React.Compone
[DEPLOYMENT_PROVIDERS.ALIYUN_WAF]: BizDeployNodeConfigFieldsProviderAliyunWAF,
[DEPLOYMENT_PROVIDERS.APISIX]: BizDeployNodeConfigFieldsProviderAPISIX,
[DEPLOYMENT_PROVIDERS.AWS_ACM]: BizDeployNodeConfigFieldsProviderAWSACM,
[DEPLOYMENT_PROVIDERS.AWS_CLB]: BizDeployNodeConfigFieldsProviderAWSCLB,
[DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT]: BizDeployNodeConfigFieldsProviderAWSCloudFront,
[DEPLOYMENT_PROVIDERS.AWS_IAM]: BizDeployNodeConfigFieldsProviderAWSIAM,
[DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT]: BizDeployNodeConfigFieldsProviderAzureKeyVault,

View File

@ -0,0 +1,92 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { isPortNumber } from "@/utils/validator";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFieldsProviderAWSCLB = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const initialValues = getInitialValues();
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aws_clb_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_clb_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aws_clb_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "loadbalancerName"]}
initialValue={initialValues.loadbalancerName}
label={t("workflow_node.deploy.form.aws_clb_loadbalancer_name.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_clb_loadbalancer_name.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aws_clb_loadbalancer_name.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "loadbalancerPort"]}
initialValue={initialValues.loadbalancerPort}
label={t("workflow_node.deploy.form.aws_clb_loadbalancer_port.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_clb_loadbalancer_port.tooltip") }}></span>}
>
<Input type="number" min={1} max={65535} placeholder={t("workflow_node.deploy.form.aws_clb_loadbalancer_port.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "certificateSource"]}
initialValue={initialValues.certificateSource}
label={t("workflow_node.deploy.form.aws_clb_certificate_source.label")}
rules={[formRule]}
>
<Select
options={["ACM", "IAM"].map((s) => ({ label: s, value: s }))}
placeholder={t("workflow_node.deploy.form.aws_clb_certificate_source.placeholder")}
/>
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
loadbalancerName: "",
loadbalancerPort: 443,
certificateSource: "ACM",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(),
loadbalancerName: z.string().nonempty(),
loadbalancerPort: z.coerce.number().refine((v) => isPortNumber(v), t("common.errmsg.port_invalid")),
certificateSource: z.string().nonempty(),
});
};
const _default = Object.assign(BizDeployNodeConfigFieldsProviderAWSCLB, {
getInitialValues,
getSchema,
});
export default _default;

View File

@ -611,6 +611,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
ALIYUN_WAF: `${ACCESS_PROVIDERS.ALIYUN}-waf`,
APISIX: `${ACCESS_PROVIDERS.APISIX}`,
AWS_ACM: `${ACCESS_PROVIDERS.AWS}-acm`,
AWS_CLB: `${ACCESS_PROVIDERS.AWS}-clb`,
AWS_CLOUDFRONT: `${ACCESS_PROVIDERS.AWS}-cloudfront`,
AWS_IAM: `${ACCESS_PROVIDERS.AWS}-iam`,
AZURE_KEYVAULT: `${ACCESS_PROVIDERS.AZURE}-keyvault`,
@ -859,6 +860,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
[DEPLOYMENT_PROVIDERS.RAINYUN_SSLCENTER, "provider.rainyun_sslcenter_upload", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST, "provider.unicloud_webhost", DEPLOYMENT_CATEGORIES.WEBSITE],
[DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT, "provider.aws_cloudfront", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.AWS_CLB, "provider.aws_clb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.AWS_ACM, "provider.aws_acm", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.AWS_IAM, "provider.aws_iam", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT, "provider.azure_keyvault", DEPLOYMENT_CATEGORIES.SSL],

View File

@ -60,6 +60,7 @@
"arvancloud": "ArvanCloud",
"aws": "AWS",
"aws_acm": "AWS - ACM (Amazon Certificate Manager)",
"aws_clb": "AWS - CLB (Classic Load Balancer)",
"aws_cloudfront": "AWS - CloudFront",
"aws_iam": "AWS - IAM (Identity and Access Management)",
"aws_lightsail": "AWS - Lightsail",

View File

@ -969,6 +969,25 @@
"label": "AWS CloudFront certificate source",
"placeholder": "Please select AWS CloudFront certificate source"
},
"aws_clb_region": {
"label": "AWS Region",
"placeholder": "Please enter AWS CLB region (e.g. us-east-1)",
"tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/general/latest/gr/rande.html#regional-endpoints\" target=\"_blank\">https://docs.aws.amazon.com/en_us/general/latest/gr/rande.html#regional-endpoints</a>"
},
"aws_clb_loadbalancer_name": {
"label": "AWS CLB load balancer name",
"placeholder": "Please enter AWS CLB load balancer name",
"tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html</a>"
},
"aws_clb_loadbalancer_port": {
"label": "AWS CLB load balancer port",
"placeholder": "Please enter AWS CLB load balancer port",
"tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html</a>"
},
"aws_clb_certificate_source": {
"label": "AWS CLB certificate source",
"placeholder": "Please select AWS CLB certificate source"
},
"aws_iam_region": {
"label": "AWS Region",
"placeholder": "Please enter AWS IAM region (e.g. us-east-1)",

View File

@ -60,6 +60,7 @@
"arvancloud": "ArvanCloud",
"aws": "AWS",
"aws_acm": "AWS - ACM (Amazon Certificate Manager)",
"aws_clb": "AWS - CLB (Classic Load Balancer)",
"aws_cloudfront": "AWS - CloudFront",
"aws_iam": "AWS - IAM (Identity and Access Management)",
"aws_lightsail": "AWS - Lightsail",

View File

@ -968,6 +968,25 @@
"label": "AWS CloudFront 证书来源",
"placeholder": "请选择 AWS CloudFront 证书来源"
},
"aws_clb_region": {
"label": "AWS 服务区域",
"placeholder": "请输入 AWS CLB 服务区域例如us-east-1",
"tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints</a>"
},
"aws_clb_loadbalancer_name": {
"label": "AWS CLB 负载均衡器名称",
"placeholder": "请输入 AWS CLB 负载均衡器名称",
"tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html</a>"
},
"aws_clb_loadbalancer_port": {
"label": "AWS CLB 负载均衡器侦听端口",
"placeholder": "请输入 AWS CLB 负载均衡器侦听端口",
"tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html</a>"
},
"aws_clb_certificate_source": {
"label": "AWS CLB 证书来源",
"placeholder": "请选择 AWS CLB 证书来源"
},
"aws_iam_region": {
"label": "AWS 服务区域",
"placeholder": "请输入 AWS IAM 服务区域例如us-east-1",