mirror of
https://github.com/certimate-go/certimate.git
synced 2026-10-16 07:05:59 +08:00
feat(provider): new purge provider: aws acm
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package purgers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
pgrimpl "github.com/certimate-go/certimate/pkg/core/purger/providers/aws-acm"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Registries.MustRegister(domain.PurgeProviderTypeAWSACM, func(options *ProviderFactoryOptions) (core.Purger, 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 := pgrimpl.NewPurger(&pgrimpl.PurgerConfig{
|
||||
AuthMethod: credentials.AuthMethod,
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
}
|
||||
@@ -495,6 +495,7 @@ const (
|
||||
PurgeProviderTypeAliyunCAS = PurgeProviderType(AccessProviderTypeAliyun + "-cas")
|
||||
PurgeProviderTypeAliyunCLB = PurgeProviderType(AccessProviderTypeAliyun + "-clb")
|
||||
PurgeProviderTypeAliyunESA = PurgeProviderType(AccessProviderTypeAliyun + "-esa")
|
||||
PurgeProviderTypeAWSACM = PurgeProviderType(AccessProviderTypeAWS + "-acm")
|
||||
PurgeProviderTypeAWSIAM = PurgeProviderType(AccessProviderTypeAWS + "-iam")
|
||||
PurgeProviderTypeAzureKeyVault = PurgeProviderType(AccessProviderTypeAzure + "-keyvault")
|
||||
PurgeProviderTypeBaiduCloudCert = PurgeProviderType(AccessProviderTypeBaiduCloud + "-cert")
|
||||
|
||||
@@ -2,19 +2,26 @@ package awsalb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
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/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/acm"
|
||||
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
|
||||
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/iam"
|
||||
"github.com/aws/smithy-go"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"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"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -47,18 +54,34 @@ type DeployerConfig struct {
|
||||
type Deployer struct {
|
||||
config *DeployerConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *elasticloadbalancingv2.Client
|
||||
sdkClients *wSDKClients
|
||||
sdkCertmgr core.Certmgr
|
||||
}
|
||||
|
||||
var _ Provider = (*Deployer)(nil)
|
||||
|
||||
type wSDKClients struct {
|
||||
ACM *acm.Client
|
||||
IAM *iam.Client
|
||||
ELB *elasticloadbalancingv2.Client
|
||||
}
|
||||
|
||||
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.AuthMethod, config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
clientELB, err := createSDKClientELB(config.AuthMethod, config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
clientACM, err := createSDKClientACM(config.AuthMethod, config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
clientIAM, err := createSDKClientIAM(config.AuthMethod, config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
@@ -95,7 +118,7 @@ func NewDeployer(config *DeployerConfig) (*Deployer, error) {
|
||||
return &Deployer{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
sdkClients: &wSDKClients{ELB: clientELB, ACM: clientACM, IAM: clientIAM},
|
||||
sdkCertmgr: pcertmgr,
|
||||
}, nil
|
||||
}
|
||||
@@ -118,6 +141,12 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
return nil, fmt.Errorf("config `listenerArn` is required")
|
||||
}
|
||||
|
||||
// 解析证书内容
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sdkCertmgr.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
@@ -131,7 +160,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
describeLoadBalancersReq := &elasticloadbalancingv2.DescribeLoadBalancersInput{
|
||||
LoadBalancerArns: []string{d.config.LoadbalancerArn},
|
||||
}
|
||||
describeLoadBalancersResp, err := d.sdkClient.DescribeLoadBalancers(ctx, describeLoadBalancersReq)
|
||||
describeLoadBalancersResp, err := d.sdkClients.ELB.DescribeLoadBalancers(ctx, describeLoadBalancersReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.DescribeLoadBalancers'", slog.Any("request", describeLoadBalancersReq), slog.Any("response", describeLoadBalancersResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.DescribeLoadBalancers': %w", err)
|
||||
@@ -145,7 +174,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
LoadBalancerArn: aws.String(d.config.LoadbalancerArn),
|
||||
ListenerArns: []string{d.config.ListenerArn},
|
||||
}
|
||||
describeListenersResp, err := d.sdkClient.DescribeListeners(ctx, describeListenersReq)
|
||||
describeListenersResp, err := d.sdkClients.ELB.DescribeListeners(ctx, describeListenersReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.DescribeListeners'", slog.Any("request", describeListenersReq), slog.Any("response", describeListenersResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.DescribeListeners': %w", err)
|
||||
@@ -182,6 +211,10 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
if err := d.updateListenerSniCertificate(ctx, *listenerInfo.ListenerArn, certArn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := d.pruneListenerSniCertificate(ctx, *listenerInfo.ListenerArn, certArn, certX509.DNSNames); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &DeployResult{}, nil
|
||||
@@ -198,7 +231,7 @@ func (d *Deployer) updateListenerDefaultCertificate(ctx context.Context, cloudLi
|
||||
},
|
||||
},
|
||||
}
|
||||
modifyListenerResp, err := d.sdkClient.ModifyListener(ctx, modifyListenerReq)
|
||||
modifyListenerResp, err := d.sdkClients.ELB.ModifyListener(ctx, modifyListenerReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.ModifyListener'", slog.Any("request", modifyListenerReq), slog.Any("response", modifyListenerResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.ModifyListener': %w", err)
|
||||
@@ -218,7 +251,7 @@ func (d *Deployer) updateListenerSniCertificate(ctx context.Context, cloudListen
|
||||
},
|
||||
},
|
||||
}
|
||||
addListenerCertificatesResp, err := d.sdkClient.AddListenerCertificates(ctx, addListenerCertificatesReq)
|
||||
addListenerCertificatesResp, err := d.sdkClients.ELB.AddListenerCertificates(ctx, addListenerCertificatesReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.AddListenerCertificates'", slog.Any("request", addListenerCertificatesReq), slog.Any("response", addListenerCertificatesResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.AddListenerCertificates': %w", err)
|
||||
@@ -227,7 +260,132 @@ func (d *Deployer) updateListenerSniCertificate(ctx context.Context, cloudListen
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(authMethod, accessKeyId, secretAccessKey, region string) (*elasticloadbalancingv2.Client, error) {
|
||||
func (d *Deployer) pruneListenerSniCertificate(ctx context.Context, cloudListenerArn string, cloudCertArn string, cloudCertSANs []string) error {
|
||||
const acmArnPrefix = "arn:aws:acm:"
|
||||
const iamArnPrefix = "arn:aws:iam:"
|
||||
|
||||
certArnsToDelete := make([]string, 0)
|
||||
|
||||
// 查询侦听器证书列表,并找出需要删除绑定的同域名证书
|
||||
// REF: https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_DescribeListenerCertificates.html
|
||||
// REF: https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetServerCertificate.html
|
||||
// REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_GetCertificate.html
|
||||
describeListenerCertificatesMarker := (*string)(nil)
|
||||
describeListenerCertificatesPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
describeListenerCertificatesReq := &elasticloadbalancingv2.DescribeListenerCertificatesInput{
|
||||
ListenerArn: aws.String(cloudListenerArn),
|
||||
Marker: describeListenerCertificatesMarker,
|
||||
PageSize: aws.Int32(int32(describeListenerCertificatesPageSize)),
|
||||
}
|
||||
describeListenerCertificatesResp, err := d.sdkClients.ELB.DescribeListenerCertificates(ctx, describeListenerCertificatesReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.DescribeListenerCertificates'", slog.Any("request", describeListenerCertificatesReq), slog.Any("response", describeListenerCertificatesResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.DescribeListenerCertificates': %w", err)
|
||||
}
|
||||
|
||||
for _, certItem := range describeListenerCertificatesResp.Certificates {
|
||||
if aws.ToString(certItem.CertificateArn) == cloudCertArn {
|
||||
continue
|
||||
}
|
||||
if aws.ToBool(certItem.IsDefault) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(aws.ToString(certItem.CertificateArn), acmArnPrefix):
|
||||
{
|
||||
getCertificateReq := &acm.GetCertificateInput{
|
||||
CertificateArn: certItem.CertificateArn,
|
||||
}
|
||||
getCertificateResp, err := d.sdkClients.ACM.GetCertificate(ctx, getCertificateReq)
|
||||
if err != nil {
|
||||
var sdkerr smithy.APIError
|
||||
if errors.As(err, &sdkerr) {
|
||||
if sdkErrCode := sdkerr.ErrorCode(); sdkErrCode == "InvalidArnException" || sdkErrCode == "ResourceNotFoundException" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to execute sdk request 'acm.GetCertificate': %w", err)
|
||||
}
|
||||
|
||||
certX509InACM, err := xcert.ParseCertificateFromPEM(aws.ToString(getCertificateResp.Certificate))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if lo.ElementsMatch(cloudCertSANs, certX509InACM.DNSNames) {
|
||||
certArnsToDelete = append(certArnsToDelete, aws.ToString(certItem.CertificateArn))
|
||||
}
|
||||
}
|
||||
|
||||
case strings.HasPrefix(aws.ToString(certItem.CertificateArn), iamArnPrefix):
|
||||
{
|
||||
getServerCertificateReq := &iam.GetServerCertificateInput{
|
||||
ServerCertificateName: certItem.CertificateArn,
|
||||
}
|
||||
getServerCertificateResp, err := d.sdkClients.IAM.GetServerCertificate(ctx, getServerCertificateReq)
|
||||
if err != nil {
|
||||
var sdkerr smithy.APIError
|
||||
if errors.As(err, &sdkerr) {
|
||||
if sdkerr.ErrorCode() == "NoSuchEntity" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to execute sdk request 'iam.GetServerCertificate': %w", err)
|
||||
}
|
||||
|
||||
certX509InIAM, err := xcert.ParseCertificateFromPEM(aws.ToString(getServerCertificateResp.ServerCertificate.CertificateBody))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if lo.ElementsMatch(cloudCertSANs, certX509InIAM.DNSNames) {
|
||||
certArnsToDelete = append(certArnsToDelete, aws.ToString(certItem.CertificateArn))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(describeListenerCertificatesResp.Certificates) < describeListenerCertificatesPageSize || describeListenerCertificatesResp.NextMarker == nil {
|
||||
break
|
||||
}
|
||||
|
||||
describeListenerCertificatesMarker = describeListenerCertificatesResp.NextMarker
|
||||
}
|
||||
|
||||
// 从证书列表删除证书
|
||||
// REF: https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_RemoveListenerCertificates.html
|
||||
if len(certArnsToDelete) > 0 {
|
||||
d.logger.Info("found alb listener certificates to delete", slog.Any("certificateArns", certArnsToDelete))
|
||||
|
||||
removeListenerCertificatesReq := &elasticloadbalancingv2.RemoveListenerCertificatesInput{
|
||||
ListenerArn: aws.String(d.config.ListenerArn),
|
||||
Certificates: lo.Map(certArnsToDelete, func(certArn string, _ int) types.Certificate {
|
||||
return types.Certificate{
|
||||
CertificateArn: aws.String(certArn),
|
||||
}
|
||||
}),
|
||||
}
|
||||
removeListenerCertificatesResp, err := d.sdkClients.ELB.RemoveListenerCertificates(ctx, removeListenerCertificatesReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.RemoveListenerCertificates'", slog.Any("request", removeListenerCertificatesReq), slog.Any("response", removeListenerCertificatesResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.RemoveListenerCertificates': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClientELB(authMethod, accessKeyId, secretAccessKey, region string) (*elasticloadbalancingv2.Client, error) {
|
||||
opts := []func(options *awscfg.LoadOptions) error{
|
||||
awscfg.WithRegion(region),
|
||||
}
|
||||
@@ -255,3 +413,61 @@ func createSDKClient(authMethod, accessKeyId, secretAccessKey, region string) (*
|
||||
client := elasticloadbalancingv2.NewFromConfig(cfg)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func createSDKClientACM(authMethod, accessKeyId, secretAccessKey, region string) (*acm.Client, error) {
|
||||
opts := []func(options *awscfg.LoadOptions) error{
|
||||
awscfg.WithRegion(region),
|
||||
}
|
||||
|
||||
staticCredsProvider := awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, "")
|
||||
imdsCredsProvider := aws.NewCredentialsCache(ec2rolecreds.New())
|
||||
switch authMethod {
|
||||
case "":
|
||||
if accessKeyId != "" && secretAccessKey != "" {
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
}
|
||||
case AUTH_METHOD_ACCESSKEY:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
case AUTH_METHOD_IMDS:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(imdsCredsProvider))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported auth method '%s'", authMethod)
|
||||
}
|
||||
|
||||
cfg, err := awscfg.LoadDefaultConfig(context.Background(), opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := acm.NewFromConfig(cfg)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func createSDKClientIAM(authMethod, accessKeyId, secretAccessKey, region string) (*iam.Client, error) {
|
||||
opts := []func(options *awscfg.LoadOptions) error{
|
||||
awscfg.WithRegion(region),
|
||||
}
|
||||
|
||||
staticCredsProvider := awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, "")
|
||||
imdsCredsProvider := aws.NewCredentialsCache(ec2rolecreds.New())
|
||||
switch authMethod {
|
||||
case "":
|
||||
if accessKeyId != "" && secretAccessKey != "" {
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
}
|
||||
case AUTH_METHOD_ACCESSKEY:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
case AUTH_METHOD_IMDS:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(imdsCredsProvider))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported auth method '%s'", authMethod)
|
||||
}
|
||||
|
||||
cfg, err := awscfg.LoadDefaultConfig(context.Background(), opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := iam.NewFromConfig(cfg)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -2,19 +2,26 @@ package awsnlb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
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/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/acm"
|
||||
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
|
||||
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/iam"
|
||||
"github.com/aws/smithy-go"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"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"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -47,18 +54,34 @@ type DeployerConfig struct {
|
||||
type Deployer struct {
|
||||
config *DeployerConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *elasticloadbalancingv2.Client
|
||||
sdkClients *wSDKClients
|
||||
sdkCertmgr core.Certmgr
|
||||
}
|
||||
|
||||
var _ Provider = (*Deployer)(nil)
|
||||
|
||||
type wSDKClients struct {
|
||||
ACM *acm.Client
|
||||
IAM *iam.Client
|
||||
ELB *elasticloadbalancingv2.Client
|
||||
}
|
||||
|
||||
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.AuthMethod, config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
clientELB, err := createSDKClientELB(config.AuthMethod, config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
clientACM, err := createSDKClientACM(config.AuthMethod, config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
clientIAM, err := createSDKClientIAM(config.AuthMethod, config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
@@ -95,7 +118,7 @@ func NewDeployer(config *DeployerConfig) (*Deployer, error) {
|
||||
return &Deployer{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
sdkClients: &wSDKClients{ELB: clientELB, ACM: clientACM, IAM: clientIAM},
|
||||
sdkCertmgr: pcertmgr,
|
||||
}, nil
|
||||
}
|
||||
@@ -118,6 +141,12 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
return nil, fmt.Errorf("config `listenerArn` is required")
|
||||
}
|
||||
|
||||
// 解析证书内容
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sdkCertmgr.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
@@ -131,7 +160,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
describeLoadBalancersReq := &elasticloadbalancingv2.DescribeLoadBalancersInput{
|
||||
LoadBalancerArns: []string{d.config.LoadbalancerArn},
|
||||
}
|
||||
describeLoadBalancersResp, err := d.sdkClient.DescribeLoadBalancers(ctx, describeLoadBalancersReq)
|
||||
describeLoadBalancersResp, err := d.sdkClients.ELB.DescribeLoadBalancers(ctx, describeLoadBalancersReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.DescribeLoadBalancers'", slog.Any("request", describeLoadBalancersReq), slog.Any("response", describeLoadBalancersResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.DescribeLoadBalancers': %w", err)
|
||||
@@ -145,7 +174,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
LoadBalancerArn: aws.String(d.config.LoadbalancerArn),
|
||||
ListenerArns: []string{d.config.ListenerArn},
|
||||
}
|
||||
describeListenersResp, err := d.sdkClient.DescribeListeners(ctx, describeListenersReq)
|
||||
describeListenersResp, err := d.sdkClients.ELB.DescribeListeners(ctx, describeListenersReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.DescribeListeners'", slog.Any("request", describeListenersReq), slog.Any("response", describeListenersResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.DescribeListeners': %w", err)
|
||||
@@ -182,6 +211,10 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
if err := d.updateListenerSniCertificate(ctx, *listenerInfo.ListenerArn, certArn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := d.pruneListenerSniCertificates(ctx, *listenerInfo.ListenerArn, certArn, certX509.DNSNames); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &DeployResult{}, nil
|
||||
@@ -198,7 +231,7 @@ func (d *Deployer) updateListenerDefaultCertificate(ctx context.Context, cloudLi
|
||||
},
|
||||
},
|
||||
}
|
||||
modifyListenerResp, err := d.sdkClient.ModifyListener(ctx, modifyListenerReq)
|
||||
modifyListenerResp, err := d.sdkClients.ELB.ModifyListener(ctx, modifyListenerReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.ModifyListener'", slog.Any("request", modifyListenerReq), slog.Any("response", modifyListenerResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.ModifyListener': %w", err)
|
||||
@@ -218,7 +251,7 @@ func (d *Deployer) updateListenerSniCertificate(ctx context.Context, cloudListen
|
||||
},
|
||||
},
|
||||
}
|
||||
addListenerCertificatesResp, err := d.sdkClient.AddListenerCertificates(ctx, addListenerCertificatesReq)
|
||||
addListenerCertificatesResp, err := d.sdkClients.ELB.AddListenerCertificates(ctx, addListenerCertificatesReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.AddListenerCertificates'", slog.Any("request", addListenerCertificatesReq), slog.Any("response", addListenerCertificatesResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.AddListenerCertificates': %w", err)
|
||||
@@ -227,7 +260,132 @@ func (d *Deployer) updateListenerSniCertificate(ctx context.Context, cloudListen
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(authMethod, accessKeyId, secretAccessKey, region string) (*elasticloadbalancingv2.Client, error) {
|
||||
func (d *Deployer) pruneListenerSniCertificates(ctx context.Context, cloudListenerArn string, cloudCertArn string, cloudCertSANs []string) error {
|
||||
const acmArnPrefix = "arn:aws:acm:"
|
||||
const iamArnPrefix = "arn:aws:iam:"
|
||||
|
||||
certArnsToDelete := make([]string, 0)
|
||||
|
||||
// 查询侦听器证书列表,并找出需要删除绑定的同域名证书
|
||||
// REF: https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_DescribeListenerCertificates.html
|
||||
// REF: https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetServerCertificate.html
|
||||
// REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_GetCertificate.html
|
||||
describeListenerCertificatesMarker := (*string)(nil)
|
||||
describeListenerCertificatesPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
describeListenerCertificatesReq := &elasticloadbalancingv2.DescribeListenerCertificatesInput{
|
||||
ListenerArn: aws.String(cloudListenerArn),
|
||||
Marker: describeListenerCertificatesMarker,
|
||||
PageSize: aws.Int32(int32(describeListenerCertificatesPageSize)),
|
||||
}
|
||||
describeListenerCertificatesResp, err := d.sdkClients.ELB.DescribeListenerCertificates(ctx, describeListenerCertificatesReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.DescribeListenerCertificates'", slog.Any("request", describeListenerCertificatesReq), slog.Any("response", describeListenerCertificatesResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.DescribeListenerCertificates': %w", err)
|
||||
}
|
||||
|
||||
for _, certItem := range describeListenerCertificatesResp.Certificates {
|
||||
if aws.ToString(certItem.CertificateArn) == cloudCertArn {
|
||||
continue
|
||||
}
|
||||
if aws.ToBool(certItem.IsDefault) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(aws.ToString(certItem.CertificateArn), acmArnPrefix):
|
||||
{
|
||||
getCertificateReq := &acm.GetCertificateInput{
|
||||
CertificateArn: certItem.CertificateArn,
|
||||
}
|
||||
getCertificateResp, err := d.sdkClients.ACM.GetCertificate(ctx, getCertificateReq)
|
||||
if err != nil {
|
||||
var sdkerr smithy.APIError
|
||||
if errors.As(err, &sdkerr) {
|
||||
if sdkErrCode := sdkerr.ErrorCode(); sdkErrCode == "InvalidArnException" || sdkErrCode == "ResourceNotFoundException" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to execute sdk request 'acm.GetCertificate': %w", err)
|
||||
}
|
||||
|
||||
certX509InACM, err := xcert.ParseCertificateFromPEM(aws.ToString(getCertificateResp.Certificate))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if lo.ElementsMatch(cloudCertSANs, certX509InACM.DNSNames) {
|
||||
certArnsToDelete = append(certArnsToDelete, aws.ToString(certItem.CertificateArn))
|
||||
}
|
||||
}
|
||||
|
||||
case strings.HasPrefix(aws.ToString(certItem.CertificateArn), iamArnPrefix):
|
||||
{
|
||||
getServerCertificateReq := &iam.GetServerCertificateInput{
|
||||
ServerCertificateName: certItem.CertificateArn,
|
||||
}
|
||||
getServerCertificateResp, err := d.sdkClients.IAM.GetServerCertificate(ctx, getServerCertificateReq)
|
||||
if err != nil {
|
||||
var sdkerr smithy.APIError
|
||||
if errors.As(err, &sdkerr) {
|
||||
if sdkerr.ErrorCode() == "NoSuchEntity" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to execute sdk request 'iam.GetServerCertificate': %w", err)
|
||||
}
|
||||
|
||||
certX509InIAM, err := xcert.ParseCertificateFromPEM(aws.ToString(getServerCertificateResp.ServerCertificate.CertificateBody))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if lo.ElementsMatch(cloudCertSANs, certX509InIAM.DNSNames) {
|
||||
certArnsToDelete = append(certArnsToDelete, aws.ToString(certItem.CertificateArn))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(describeListenerCertificatesResp.Certificates) < describeListenerCertificatesPageSize || describeListenerCertificatesResp.NextMarker == nil {
|
||||
break
|
||||
}
|
||||
|
||||
describeListenerCertificatesMarker = describeListenerCertificatesResp.NextMarker
|
||||
}
|
||||
|
||||
// 从证书列表删除证书
|
||||
// REF: https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_RemoveListenerCertificates.html
|
||||
if len(certArnsToDelete) > 0 {
|
||||
d.logger.Info("found nlb listener certificates to delete", slog.Any("certificateArns", certArnsToDelete))
|
||||
|
||||
removeListenerCertificatesReq := &elasticloadbalancingv2.RemoveListenerCertificatesInput{
|
||||
ListenerArn: aws.String(d.config.ListenerArn),
|
||||
Certificates: lo.Map(certArnsToDelete, func(certArn string, _ int) types.Certificate {
|
||||
return types.Certificate{
|
||||
CertificateArn: aws.String(certArn),
|
||||
}
|
||||
}),
|
||||
}
|
||||
removeListenerCertificatesResp, err := d.sdkClients.ELB.RemoveListenerCertificates(ctx, removeListenerCertificatesReq)
|
||||
d.logger.Debug("sdk request 'elasticloadbalancingv2.RemoveListenerCertificates'", slog.Any("request", removeListenerCertificatesReq), slog.Any("response", removeListenerCertificatesResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'elasticloadbalancingv2.RemoveListenerCertificates': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClientELB(authMethod, accessKeyId, secretAccessKey, region string) (*elasticloadbalancingv2.Client, error) {
|
||||
opts := []func(options *awscfg.LoadOptions) error{
|
||||
awscfg.WithRegion(region),
|
||||
}
|
||||
@@ -255,3 +413,61 @@ func createSDKClient(authMethod, accessKeyId, secretAccessKey, region string) (*
|
||||
client := elasticloadbalancingv2.NewFromConfig(cfg)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func createSDKClientACM(authMethod, accessKeyId, secretAccessKey, region string) (*acm.Client, error) {
|
||||
opts := []func(options *awscfg.LoadOptions) error{
|
||||
awscfg.WithRegion(region),
|
||||
}
|
||||
|
||||
staticCredsProvider := awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, "")
|
||||
imdsCredsProvider := aws.NewCredentialsCache(ec2rolecreds.New())
|
||||
switch authMethod {
|
||||
case "":
|
||||
if accessKeyId != "" && secretAccessKey != "" {
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
}
|
||||
case AUTH_METHOD_ACCESSKEY:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
case AUTH_METHOD_IMDS:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(imdsCredsProvider))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported auth method '%s'", authMethod)
|
||||
}
|
||||
|
||||
cfg, err := awscfg.LoadDefaultConfig(context.Background(), opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := acm.NewFromConfig(cfg)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func createSDKClientIAM(authMethod, accessKeyId, secretAccessKey, region string) (*iam.Client, error) {
|
||||
opts := []func(options *awscfg.LoadOptions) error{
|
||||
awscfg.WithRegion(region),
|
||||
}
|
||||
|
||||
staticCredsProvider := awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, "")
|
||||
imdsCredsProvider := aws.NewCredentialsCache(ec2rolecreds.New())
|
||||
switch authMethod {
|
||||
case "":
|
||||
if accessKeyId != "" && secretAccessKey != "" {
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
}
|
||||
case AUTH_METHOD_ACCESSKEY:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
case AUTH_METHOD_IMDS:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(imdsCredsProvider))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported auth method '%s'", authMethod)
|
||||
}
|
||||
|
||||
cfg, err := awscfg.LoadDefaultConfig(context.Background(), opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := iam.NewFromConfig(cfg)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//go:build tester
|
||||
|
||||
package awsacm_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
tester "github.com/certimate-go/certimate/pkg/core/purger/providers-tester"
|
||||
impl "github.com/certimate-go/certimate/pkg/core/purger/providers/aws-acm"
|
||||
)
|
||||
|
||||
var (
|
||||
fp = tester.InitArgs("AWSACM_")
|
||||
fAccessKeyId string
|
||||
fSecretAccessKey string
|
||||
fRegion string
|
||||
)
|
||||
|
||||
func init() {
|
||||
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
|
||||
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
|
||||
fp.DefineString(&fRegion, "REGION")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -tags=tester -v ./aws_acm_test.go -args \
|
||||
--AWSACM_ACCESSKEYID="your-access-key-id" \
|
||||
--AWSACM_SECRETACCESSKEY="your-access-key-secret" \
|
||||
--AWSACM_REGION="us-east-1"
|
||||
*/
|
||||
func TestProvider(t *testing.T) {
|
||||
fp.Parse()
|
||||
|
||||
t.Run("Purge", func(t *testing.T) {
|
||||
provider, err := impl.NewPurger(&impl.PurgerConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
Region: fRegion,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
tester.Purge(t, provider, tester.PurgeInput{})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package awsacm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
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/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/acm"
|
||||
"github.com/aws/aws-sdk-go-v2/service/acm/types"
|
||||
"github.com/aws/smithy-go"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
xloop "github.com/certimate-go/certimate/pkg/utils/loop"
|
||||
xwait "github.com/certimate-go/certimate/pkg/utils/wait"
|
||||
)
|
||||
|
||||
type (
|
||||
Provider = core.Purger
|
||||
PurgeResult = core.PurgerPurgeResult
|
||||
)
|
||||
|
||||
type PurgerConfig struct {
|
||||
// AWS API 认证方式。
|
||||
// 可取值 "accesskey"、"imds"。
|
||||
// 零值时默认值 [AUTH_METHOD_ACCESSKEY]。
|
||||
AuthMethod string `json:"authMethod,omitempty"`
|
||||
// AWS AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// AWS SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
// AWS 区域。
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type Purger struct {
|
||||
config *PurgerConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *acm.Client
|
||||
}
|
||||
|
||||
var _ Provider = (*Purger)(nil)
|
||||
|
||||
func NewPurger(config *PurgerConfig) (*Purger, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("the configuration of the purger provider is nil")
|
||||
}
|
||||
|
||||
client, err := createSDKClient(config.AuthMethod, config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
return &Purger{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Purger) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
p.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
p.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Purger) Purge(ctx context.Context, expiry time.Duration) (*PurgeResult, error) {
|
||||
purgingCertArns := make([]string, 0)
|
||||
|
||||
// 获取证书列表,避免重复上传
|
||||
// REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_ListCertificates.html
|
||||
listCertificatesNextToken := (*string)(nil)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listCertificatesReq := &acm.ListCertificatesInput{
|
||||
NextToken: listCertificatesNextToken,
|
||||
MaxItems: aws.Int32(1000),
|
||||
}
|
||||
listCertificatesResp, err := p.sdkClient.ListCertificates(ctx, listCertificatesReq)
|
||||
p.logger.Debug("sdk request 'acm.ListCertificates'", slog.Any("request", listCertificatesReq), slog.Any("response", listCertificatesResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'acm.ListCertificates': %w", err)
|
||||
}
|
||||
|
||||
for _, certItem := range listCertificatesResp.CertificateSummaryList {
|
||||
if certItem.CertificateKeyPairOrigin == types.CertificateKeyPairOriginAcme {
|
||||
continue
|
||||
}
|
||||
if certItem.NotAfter == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
certNotAfter := lo.FromPtr(certItem.NotAfter)
|
||||
if time.Since(certNotAfter) >= expiry {
|
||||
purgingCertArns = append(purgingCertArns, *certItem.CertificateArn)
|
||||
}
|
||||
}
|
||||
|
||||
if len(listCertificatesResp.CertificateSummaryList) == 0 || listCertificatesResp.NextToken == nil {
|
||||
break
|
||||
}
|
||||
|
||||
listCertificatesNextToken = listCertificatesResp.NextToken
|
||||
}
|
||||
|
||||
// 删除服务器证书
|
||||
// REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_DeleteCertificate.html
|
||||
purgedCount := 0
|
||||
if len(purgingCertArns) > 0 {
|
||||
p.logger.Info("found certificates to purge", slog.Any("certArns", purgingCertArns))
|
||||
|
||||
if err := xloop.ForRangeAllWithContext(ctx, purgingCertArns, func(ctx context.Context, certArn string, i int) error {
|
||||
if i > 0 {
|
||||
if err := xwait.DelayWithContext(ctx, 1*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
deleteServerCertificateReq := &acm.DeleteCertificateInput{
|
||||
CertificateArn: aws.String(certArn),
|
||||
}
|
||||
deleteServerCertificateResp, err := p.sdkClient.DeleteCertificate(ctx, deleteServerCertificateReq)
|
||||
p.logger.Debug("sdk request 'acm.DeleteCertificate'", slog.Any("request", deleteServerCertificateReq), slog.Any("response", deleteServerCertificateResp))
|
||||
if err != nil {
|
||||
var sdkErr smithy.APIError
|
||||
if errors.As(err, &sdkErr) {
|
||||
if sdkErrCode := sdkErr.ErrorCode(); sdkErrCode == "ResourceNotFoundException" || sdkErrCode == "ResourceInUseException" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to execute sdk request 'acm.DeleteCertificate': %w", err)
|
||||
}
|
||||
|
||||
purgedCount++
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &PurgeResult{
|
||||
Count: purgedCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createSDKClient(authMethod, accessKeyId, secretAccessKey, region string) (*acm.Client, error) {
|
||||
opts := []func(options *awscfg.LoadOptions) error{
|
||||
awscfg.WithRegion(region),
|
||||
}
|
||||
|
||||
staticCredsProvider := awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, "")
|
||||
imdsCredsProvider := aws.NewCredentialsCache(ec2rolecreds.New())
|
||||
switch authMethod {
|
||||
case "":
|
||||
if accessKeyId != "" && secretAccessKey != "" {
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
}
|
||||
case AUTH_METHOD_ACCESSKEY:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(staticCredsProvider))
|
||||
case AUTH_METHOD_IMDS:
|
||||
opts = append(opts, awscfg.WithCredentialsProvider(imdsCredsProvider))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported auth method '%s'", authMethod)
|
||||
}
|
||||
|
||||
cfg, err := awscfg.LoadDefaultConfig(context.Background(), opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := acm.NewFromConfig(cfg)
|
||||
return client, nil
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package awsacm
|
||||
|
||||
import (
|
||||
iam "github.com/certimate-go/certimate/pkg/core/certmgr/providers/aws-iam"
|
||||
)
|
||||
|
||||
const (
|
||||
AUTH_METHOD_ACCESSKEY = iam.AUTH_METHOD_ACCESSKEY
|
||||
AUTH_METHOD_IMDS = iam.AUTH_METHOD_IMDS
|
||||
)
|
||||
@@ -5,6 +5,7 @@ import { PURGE_PROVIDERS, type PurgeProviderType } from "@/domain/provider";
|
||||
import BizPurgeNodeConfigFieldsProviderAliyunCAS from "./BizPurgeNodeConfigFieldsProviderAliyunCAS";
|
||||
import BizPurgeNodeConfigFieldsProviderAliyunCLB from "./BizPurgeNodeConfigFieldsProviderAliyunCLB";
|
||||
import BizPurgeNodeConfigFieldsProviderAliyunESA from "./BizPurgeNodeConfigFieldsProviderAliyunESA";
|
||||
import BizPurgeNodeConfigFieldsProviderAWSACM from "./BizPurgeNodeConfigFieldsProviderAWSACM";
|
||||
import BizPurgeNodeConfigFieldsProviderAWSIAM from "./BizPurgeNodeConfigFieldsProviderAWSIAM";
|
||||
import BizPurgeNodeConfigFieldsProviderAzureKeyVault from "./BizPurgeNodeConfigFieldsProviderAzureKeyVault";
|
||||
import BizPurgeNodeConfigFieldsProviderHuaweiCloudELB from "./BizPurgeNodeConfigFieldsProviderHuaweiCloudELB";
|
||||
@@ -23,6 +24,7 @@ const providerComponentMap: Partial<Record<PurgeProviderType, React.ComponentTyp
|
||||
[PURGE_PROVIDERS.ALIYUN_CAS]: BizPurgeNodeConfigFieldsProviderAliyunCAS,
|
||||
[PURGE_PROVIDERS.ALIYUN_CLB]: BizPurgeNodeConfigFieldsProviderAliyunCLB,
|
||||
[PURGE_PROVIDERS.ALIYUN_ESA]: BizPurgeNodeConfigFieldsProviderAliyunESA,
|
||||
[PURGE_PROVIDERS.AWS_ACM]: BizPurgeNodeConfigFieldsProviderAWSACM,
|
||||
[PURGE_PROVIDERS.AWS_IAM]: BizPurgeNodeConfigFieldsProviderAWSIAM,
|
||||
[PURGE_PROVIDERS.AZURE_KEYVAULT]: BizPurgeNodeConfigFieldsProviderAzureKeyVault,
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_ELB]: BizPurgeNodeConfigFieldsProviderHuaweiCloudELB,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const BizPurgeNodeConfigFieldsProviderAWSACM = () => {
|
||||
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.purge.form.aws_acm_region.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.purge.form.aws_acm_region.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.purge.form.aws_acm_region.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
region: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t: _ } = i18n;
|
||||
|
||||
return z.object({
|
||||
region: z.string().nonempty(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizPurgeNodeConfigFieldsProviderAWSACM, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@@ -989,6 +989,7 @@ export const PURGE_PROVIDERS = Object.freeze(
|
||||
ALIYUN_CAS: `${ACCESS_PROVIDERS.ALIYUN}-cas`,
|
||||
ALIYUN_CLB: `${ACCESS_PROVIDERS.ALIYUN}-clb`,
|
||||
ALIYUN_ESA: `${ACCESS_PROVIDERS.ALIYUN}-esa`,
|
||||
AWS_ACM: `${ACCESS_PROVIDERS.AWS}-acm`,
|
||||
AWS_IAM: `${ACCESS_PROVIDERS.AWS}-iam`,
|
||||
AZURE_KEYVAULT: `${ACCESS_PROVIDERS.AZURE}-keyvault`,
|
||||
BAIDUCLOUD_CERT: `${ACCESS_PROVIDERS.BAIDUCLOUD}-cert`,
|
||||
@@ -1025,6 +1026,7 @@ export const purgeProvidersMap: Map<PurgeProvider["type"] | string, PurgeProvide
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_ELB, "provider.huaweicloud_elb"],
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_WAF, "provider.huaweicloud_waf"],
|
||||
[PURGE_PROVIDERS.VOLCENGINE_CERTCENTER, "provider.volcengine_certcenter"],
|
||||
[PURGE_PROVIDERS.AWS_ACM, "provider.aws_acm"],
|
||||
[PURGE_PROVIDERS.AWS_IAM, "provider.aws_iam"],
|
||||
[PURGE_PROVIDERS.AZURE_KEYVAULT, "provider.azure_keyvault"],
|
||||
[PURGE_PROVIDERS.BAIDUCLOUD_CERT, "provider.baiducloud_cert"],
|
||||
|
||||
@@ -3052,6 +3052,11 @@
|
||||
"placeholder": "Please enter Alibaba Cloud ESA site ID",
|
||||
"tooltip": "For more information, see <a href=\"https://esa.console.aliyun.com/siteManage/list\" target=\"_blank\">https://esa.console.aliyun.com/siteManage/list</a>"
|
||||
},
|
||||
"aws_acm_region": {
|
||||
"label": "AWS Region",
|
||||
"placeholder": "Please enter AWS ACM 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_iam_region": {
|
||||
"label": "AWS Region",
|
||||
"placeholder": "Please enter AWS IAM region (e.g. us-east-1)",
|
||||
|
||||
@@ -3051,6 +3051,11 @@
|
||||
"placeholder": "请输入阿里云 ESA 站点 ID",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://esa.console.aliyun.com/siteManage/list\" target=\"_blank\">https://esa.console.aliyun.com/siteManage/list</a>"
|
||||
},
|
||||
"aws_acm_region": {
|
||||
"label": "AWS 服务区域",
|
||||
"placeholder": "请输入 AWS ACM 服务区域(例如: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_iam_region": {
|
||||
"label": "AWS 服务区域",
|
||||
"placeholder": "请输入 AWS IAM 服务区域(例如:us-east-1)",
|
||||
|
||||
Reference in New Issue
Block a user