mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-07 13:09:51 +00:00
feat: new deployment provider: aws iam
This commit is contained in:
parent
6dc65eea2f
commit
7d55383cf7
1
go.mod
1
go.mod
@ -85,6 +85,7 @@ require (
|
||||
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
|
||||
github.com/avast/retry-go v3.0.0+incompatible // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.42.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 // indirect
|
||||
github.com/buger/goterm v1.0.4 // indirect
|
||||
github.com/diskfs/go-diskfs v1.5.0 // indirect
|
||||
|
2
go.sum
2
go.sum
@ -235,6 +235,8 @@ github.com/aws/aws-sdk-go-v2/service/acm v1.32.0/go.mod h1:3sKYAgRbuBa2QMYGh/WEc
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1 h1:6xZNYtuVwzBs8k+TmraERt0vL68Ppg9aUi+aTQmPaVM=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1/go.mod h1:FIBJ48TS+qJb+Ne4qJ+0NeIhtPTVXItXooTeNeVI4Po=
|
||||
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/iam v1.42.0 h1:G6+UzGvubaet9QOh0664E9JeT+b6Zvop3AChozRqkrA=
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.42.0/go.mod h1:mPJkGQzeCoPs82ElNILor2JzZgYENr4UaSKUT8K27+c=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
pAliyunWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-waf"
|
||||
pAWSACM "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-acm"
|
||||
pAWSCloudFront "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-cloudfront"
|
||||
pAWSIAM "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-iam"
|
||||
pAzureKeyVault "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/azure-keyvault"
|
||||
pBaiduCloudAppBLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-appblb"
|
||||
pBaiduCloudBLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-blb"
|
||||
@ -331,7 +332,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
|
||||
}
|
||||
}
|
||||
|
||||
case domain.DeploymentProviderTypeAWSACM, domain.DeploymentProviderTypeAWSCloudFront:
|
||||
case domain.DeploymentProviderTypeAWSACM, domain.DeploymentProviderTypeAWSCloudFront, domain.DeploymentProviderTypeAWSIAM:
|
||||
{
|
||||
access := domain.AccessConfigForAWS{}
|
||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||
@ -350,10 +351,20 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
|
||||
|
||||
case domain.DeploymentProviderTypeAWSCloudFront:
|
||||
deployer, err := pAWSCloudFront.NewDeployer(&pAWSCloudFront.DeployerConfig{
|
||||
AccessKeyId: access.AccessKeyId,
|
||||
SecretAccessKey: access.SecretAccessKey,
|
||||
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
|
||||
DistributionId: maputil.GetString(options.ProviderServiceConfig, "distributionId"),
|
||||
CertificateSource: maputil.GetOrDefaultString(options.ProviderServiceConfig, "certificateSource", "ACM"),
|
||||
})
|
||||
return deployer, err
|
||||
|
||||
case domain.DeploymentProviderTypeAWSIAM:
|
||||
deployer, err := pAWSIAM.NewDeployer(&pAWSIAM.DeployerConfig{
|
||||
AccessKeyId: access.AccessKeyId,
|
||||
SecretAccessKey: access.SecretAccessKey,
|
||||
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
|
||||
DistributionId: maputil.GetString(options.ProviderServiceConfig, "distributionId"),
|
||||
CertificatePath: maputil.GetOrDefaultString(options.ProviderServiceConfig, "certificatePath", "/"),
|
||||
})
|
||||
return deployer, err
|
||||
|
||||
|
@ -195,6 +195,7 @@ const (
|
||||
DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf")
|
||||
DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm")
|
||||
DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront")
|
||||
DeploymentProviderTypeAWSIAM = DeploymentProviderType(AccessProviderTypeAWS + "-iam")
|
||||
DeploymentProviderTypeAzureKeyVault = DeploymentProviderType(AccessProviderTypeAzure + "-keyvault")
|
||||
DeploymentProviderTypeBaiduCloudAppBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-appblb")
|
||||
DeploymentProviderTypeBaiduCloudBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-blb")
|
||||
|
@ -14,7 +14,8 @@ import (
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-acm"
|
||||
uploaderspacm "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-acm"
|
||||
uploaderspiam "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-iam"
|
||||
)
|
||||
|
||||
type DeployerConfig struct {
|
||||
@ -26,6 +27,9 @@ type DeployerConfig struct {
|
||||
Region string `json:"region"`
|
||||
// AWS CloudFront 分配 ID。
|
||||
DistributionId string `json:"distributionId"`
|
||||
// AWS CloudFront 证书来源。
|
||||
// 可取值 "ACM"、"IAM"。
|
||||
CertificateSource string `json:"certificateSource"`
|
||||
}
|
||||
|
||||
type DeployerProvider struct {
|
||||
@ -47,13 +51,28 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||
}
|
||||
|
||||
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||
AccessKeyId: config.AccessKeyId,
|
||||
SecretAccessKey: config.SecretAccessKey,
|
||||
Region: config.Region,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||
var uploader uploader.Uploader
|
||||
if config.CertificateSource == "ACM" {
|
||||
uploader, err = uploaderspacm.NewUploader(&uploaderspacm.UploaderConfig{
|
||||
AccessKeyId: config.AccessKeyId,
|
||||
SecretAccessKey: config.SecretAccessKey,
|
||||
Region: config.Region,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||
}
|
||||
} else if config.CertificateSource == "IAM" {
|
||||
uploader, err = uploaderspiam.NewUploader(&uploaderspiam.UploaderConfig{
|
||||
AccessKeyId: config.AccessKeyId,
|
||||
SecretAccessKey: config.SecretAccessKey,
|
||||
Region: config.Region,
|
||||
CertificatePath: "/cloudfront/",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported certificate source: '%s'", config.CertificateSource)
|
||||
}
|
||||
|
||||
return &DeployerProvider{
|
||||
@ -79,7 +98,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
||||
return nil, errors.New("config `distribuitionId` is required")
|
||||
}
|
||||
|
||||
// 上传证书到 ACM
|
||||
// 上传证书到 ACM/IAM
|
||||
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||
@ -109,7 +128,19 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate = &types.ViewerCertificate{}
|
||||
}
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate.CloudFrontDefaultCertificate = aws.Bool(false)
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate.ACMCertificateArn = aws.String(upres.CertId)
|
||||
if d.config.CertificateSource == "ACM" {
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate.ACMCertificateArn = aws.String(upres.CertId)
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate.IAMCertificateId = nil
|
||||
} else if d.config.CertificateSource == "IAM" {
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate.ACMCertificateArn = nil
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate.IAMCertificateId = aws.String(upres.CertId)
|
||||
if updateDistributionReq.DistributionConfig.ViewerCertificate.MinimumProtocolVersion == "" {
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate.MinimumProtocolVersion = types.MinimumProtocolVersionTLSv1
|
||||
}
|
||||
if updateDistributionReq.DistributionConfig.ViewerCertificate.SSLSupportMethod == "" {
|
||||
updateDistributionReq.DistributionConfig.ViewerCertificate.SSLSupportMethod = types.SSLSupportMethodSniOnly
|
||||
}
|
||||
}
|
||||
updateDistributionResp, err := d.sdkClient.UpdateDistribution(context.TODO(), updateDistributionReq)
|
||||
d.logger.Debug("sdk request 'cloudfront.UpdateDistribution'", slog.Any("request", updateDistributionReq), slog.Any("response", updateDistributionResp))
|
||||
if err != nil {
|
||||
|
75
internal/pkg/core/deployer/providers/aws-iam/aws_iam.go
Normal file
75
internal/pkg/core/deployer/providers/aws-iam/aws_iam.go
Normal file
@ -0,0 +1,75 @@
|
||||
package awsiam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-iam"
|
||||
)
|
||||
|
||||
type DeployerConfig struct {
|
||||
// AWS AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// AWS SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
// AWS 区域。
|
||||
Region string `json:"region"`
|
||||
// IAM 证书路径。
|
||||
// 选填。
|
||||
CertificatePath string `json:"certificatePath,omitempty"`
|
||||
}
|
||||
|
||||
type DeployerProvider struct {
|
||||
config *DeployerConfig
|
||||
logger *slog.Logger
|
||||
sslUploader uploader.Uploader
|
||||
}
|
||||
|
||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||
|
||||
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||
if config == nil {
|
||||
panic("config is nil")
|
||||
}
|
||||
|
||||
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||
AccessKeyId: config.AccessKeyId,
|
||||
SecretAccessKey: config.SecretAccessKey,
|
||||
Region: config.Region,
|
||||
CertificatePath: config.CertificatePath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||
}
|
||||
|
||||
return &DeployerProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sslUploader: uploader,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||
if logger == nil {
|
||||
d.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
d.logger = logger
|
||||
}
|
||||
d.sslUploader.WithLogger(logger)
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||
// 上传证书到 IAM
|
||||
upres, err := d.sslUploader.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))
|
||||
}
|
||||
|
||||
return &deployer.DeployResult{}, nil
|
||||
}
|
@ -74,7 +74,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
||||
// 获取证书列表,避免重复上传
|
||||
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListCertificates.html
|
||||
var listCertificatesNextToken *string = nil
|
||||
listCertificatesMaxItems := int32(1000)
|
||||
var listCertificatesMaxItems int32 = 1000
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@ -107,7 +107,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
||||
}
|
||||
|
||||
// 最后对比证书内容
|
||||
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListTagsForCertificate.html
|
||||
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_GetCertificate.html
|
||||
getCertificateReq := &awsacm.GetCertificateInput{
|
||||
CertificateArn: certSummary.CertificateArn,
|
||||
}
|
||||
@ -115,11 +115,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'acm.GetCertificate': %w", err)
|
||||
} else {
|
||||
oldCertPEM := aws.ToString(getCertificateResp.CertificateChain)
|
||||
if oldCertPEM == "" {
|
||||
oldCertPEM = aws.ToString(getCertificateResp.Certificate)
|
||||
}
|
||||
|
||||
oldCertPEM := aws.ToString(getCertificateResp.Certificate)
|
||||
oldCertX509, err := certutil.ParseCertificateFromPEM(oldCertPEM)
|
||||
if err != nil {
|
||||
continue
|
||||
@ -158,7 +154,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
||||
}
|
||||
|
||||
return &uploader.UploadResult{
|
||||
CertId: *importCertificateResp.CertificateArn,
|
||||
CertId: aws.ToString(importCertificateResp.CertificateArn),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
185
internal/pkg/core/uploader/providers/aws-iam/aws_iam.go
Normal file
185
internal/pkg/core/uploader/providers/aws-iam/aws_iam.go
Normal file
@ -0,0 +1,185 @@
|
||||
package awsiam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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"
|
||||
awsiam "github.com/aws/aws-sdk-go-v2/service/iam"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||
)
|
||||
|
||||
type UploaderConfig struct {
|
||||
// AWS AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// AWS SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
// AWS 区域。
|
||||
Region string `json:"region"`
|
||||
// IAM 证书路径。
|
||||
// 选填。
|
||||
CertificatePath string `json:"certificatePath,omitempty"`
|
||||
}
|
||||
|
||||
type UploaderProvider struct {
|
||||
config *UploaderConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *awsiam.Client
|
||||
}
|
||||
|
||||
var _ uploader.Uploader = (*UploaderProvider)(nil)
|
||||
|
||||
func NewUploader(config *UploaderConfig) (*UploaderProvider, error) {
|
||||
if config == nil {
|
||||
panic("config is nil")
|
||||
}
|
||||
|
||||
client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||
}
|
||||
|
||||
return &UploaderProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader {
|
||||
if logger == nil {
|
||||
u.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
u.logger = logger
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (*uploader.UploadResult, error) {
|
||||
// 解析证书内容
|
||||
certX509, err := certutil.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 提取服务器证书
|
||||
serverCertPEM, intermediaCertPEM, err := certutil.ExtractCertificatesFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract certs: %w", err)
|
||||
}
|
||||
|
||||
// 获取证书列表,避免重复上传
|
||||
// REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_ListServerCertificates.html
|
||||
var listServerCertificatesMarker *string = nil
|
||||
var listServerCertificatesMaxItems int32 = 1000
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listServerCertificatesReq := &awsiam.ListServerCertificatesInput{
|
||||
Marker: listServerCertificatesMarker,
|
||||
MaxItems: aws.Int32(listServerCertificatesMaxItems),
|
||||
}
|
||||
if u.config.CertificatePath != "" {
|
||||
listServerCertificatesReq.PathPrefix = aws.String(u.config.CertificatePath)
|
||||
}
|
||||
listServerCertificatesResp, err := u.sdkClient.ListServerCertificates(context.TODO(), listServerCertificatesReq)
|
||||
u.logger.Debug("sdk request 'iam.ListServerCertificates'", slog.Any("request", listServerCertificatesReq), slog.Any("response", listServerCertificatesResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'iam.ListServerCertificates': %w", err)
|
||||
}
|
||||
|
||||
for _, certMeta := range listServerCertificatesResp.ServerCertificateMetadataList {
|
||||
// 先对比证书路径
|
||||
if u.config.CertificatePath != "" && aws.ToString(certMeta.Path) != u.config.CertificatePath {
|
||||
continue
|
||||
}
|
||||
|
||||
// 先对比证书有效期
|
||||
if certMeta.Expiration == nil || !certMeta.Expiration.Equal(certX509.NotAfter) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 最后对比证书内容
|
||||
// REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_GetServerCertificate.html
|
||||
getServerCertificateReq := &awsiam.GetServerCertificateInput{
|
||||
ServerCertificateName: certMeta.ServerCertificateName,
|
||||
}
|
||||
getServerCertificateResp, err := u.sdkClient.GetServerCertificate(context.TODO(), getServerCertificateReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'iam.GetServerCertificate': %w", err)
|
||||
} else {
|
||||
oldCertPEM := aws.ToString(getServerCertificateResp.ServerCertificate.CertificateBody)
|
||||
oldCertX509, err := certutil.ParseCertificateFromPEM(oldCertPEM)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !certutil.EqualCertificate(certX509, oldCertX509) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
||||
u.logger.Info("ssl certificate already exists")
|
||||
return &uploader.UploadResult{
|
||||
CertId: aws.ToString(certMeta.ServerCertificateId),
|
||||
CertName: aws.ToString(certMeta.ServerCertificateName),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if listServerCertificatesResp.Marker == nil || len(listServerCertificatesResp.ServerCertificateMetadataList) < int(listServerCertificatesMaxItems) {
|
||||
break
|
||||
} else {
|
||||
listServerCertificatesMarker = listServerCertificatesResp.Marker
|
||||
}
|
||||
}
|
||||
|
||||
// 生成新证书名(需符合 AWS IAM 命名规则)
|
||||
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
||||
|
||||
// 导入证书
|
||||
// REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_UploadServerCertificate.html
|
||||
uploadServerCertificateReq := &awsiam.UploadServerCertificateInput{
|
||||
ServerCertificateName: aws.String(certName),
|
||||
Path: aws.String(u.config.CertificatePath),
|
||||
CertificateBody: aws.String(serverCertPEM),
|
||||
CertificateChain: aws.String(intermediaCertPEM),
|
||||
PrivateKey: aws.String(privkeyPEM),
|
||||
}
|
||||
if u.config.CertificatePath == "" {
|
||||
uploadServerCertificateReq.Path = aws.String("/")
|
||||
}
|
||||
uploadServerCertificateResp, err := u.sdkClient.UploadServerCertificate(context.TODO(), uploadServerCertificateReq)
|
||||
u.logger.Debug("sdk request 'iam.UploadServerCertificate'", slog.Any("request", uploadServerCertificateReq), slog.Any("response", uploadServerCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'iam.UploadServerCertificate': %w", err)
|
||||
}
|
||||
|
||||
return &uploader.UploadResult{
|
||||
CertId: aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.ServerCertificateId),
|
||||
CertName: certName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createSdkClient(accessKeyId, secretAccessKey, region string) (*awsiam.Client, error) {
|
||||
cfg, err := awscfg.LoadDefaultConfig(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := awsiam.NewFromConfig(cfg, func(o *awsiam.Options) {
|
||||
o.Region = region
|
||||
o.Credentials = aws.NewCredentialsCache(awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, ""))
|
||||
})
|
||||
return client, nil
|
||||
}
|
@ -35,6 +35,7 @@ import DeployNodeConfigFormAliyunVODConfig from "./DeployNodeConfigFormAliyunVOD
|
||||
import DeployNodeConfigFormAliyunWAFConfig from "./DeployNodeConfigFormAliyunWAFConfig";
|
||||
import DeployNodeConfigFormAWSACMConfig from "./DeployNodeConfigFormAWSACMConfig";
|
||||
import DeployNodeConfigFormAWSCloudFrontConfig from "./DeployNodeConfigFormAWSCloudFrontConfig";
|
||||
import DeployNodeConfigFormAWSIAMConfig from "./DeployNodeConfigFormAWSIAMConfig";
|
||||
import DeployNodeConfigFormAzureKeyVaultConfig from "./DeployNodeConfigFormAzureKeyVaultConfig";
|
||||
import DeployNodeConfigFormBaiduCloudAppBLBConfig from "./DeployNodeConfigFormBaiduCloudAppBLBConfig";
|
||||
import DeployNodeConfigFormBaiduCloudBLBConfig from "./DeployNodeConfigFormBaiduCloudBLBConfig";
|
||||
@ -238,6 +239,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
||||
return <DeployNodeConfigFormAWSACMConfig {...nestedFormProps} />;
|
||||
case DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT:
|
||||
return <DeployNodeConfigFormAWSCloudFrontConfig {...nestedFormProps} />;
|
||||
case DEPLOYMENT_PROVIDERS.AWS_IAM:
|
||||
return <DeployNodeConfigFormAWSIAMConfig {...nestedFormProps} />;
|
||||
case DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT:
|
||||
return <DeployNodeConfigFormAzureKeyVaultConfig {...nestedFormProps} />;
|
||||
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_APPBLB:
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Form, type FormInstance, Input } from "antd";
|
||||
import { Form, type FormInstance, Input, Select } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
type DeployNodeConfigFormAWSCloudFrontConfigFieldValues = Nullish<{
|
||||
region: string;
|
||||
distributionId: string;
|
||||
certificateSource: string;
|
||||
}>;
|
||||
|
||||
export type DeployNodeConfigFormAWSCloudFrontConfigProps = {
|
||||
@ -17,7 +18,9 @@ export type DeployNodeConfigFormAWSCloudFrontConfigProps = {
|
||||
};
|
||||
|
||||
const initFormModel = (): DeployNodeConfigFormAWSCloudFrontConfigFieldValues => {
|
||||
return {};
|
||||
return {
|
||||
certificateSource: "ACM",
|
||||
};
|
||||
};
|
||||
|
||||
const DeployNodeConfigFormAWSCloudFrontConfig = ({
|
||||
@ -30,15 +33,9 @@ const DeployNodeConfigFormAWSCloudFrontConfig = ({
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
region: z
|
||||
.string({ message: t("workflow_node.deploy.form.aws_cloudfront_region.placeholder") })
|
||||
.nonempty(t("workflow_node.deploy.form.aws_cloudfront_region.placeholder"))
|
||||
.trim(),
|
||||
distributionId: z
|
||||
.string({ message: t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder") })
|
||||
.nonempty(t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder"))
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
||||
.trim(),
|
||||
region: z.string().trim().nonempty(t("workflow_node.deploy.form.aws_cloudfront_region.placeholder")),
|
||||
distributionId: z.string().trim().nonempty(t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder")),
|
||||
certificateSource: z.string().trim().nonempty(t("workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder")),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
@ -72,6 +69,17 @@ const DeployNodeConfigFormAWSCloudFrontConfig = ({
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="certificateSource" label={t("workflow_node.deploy.form.aws_cloudfront_certificate_source.label")} rules={[formRule]}>
|
||||
<Select placeholder={t("workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder")}>
|
||||
<Select.Option key="ACM" value="ACM">
|
||||
ACM
|
||||
</Select.Option>
|
||||
<Select.Option key="IAM" value="IAM">
|
||||
IAM
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,77 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Form, type FormInstance, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
type DeployNodeConfigFormAWSIAMConfigFieldValues = Nullish<{
|
||||
region: string;
|
||||
certificatePath?: string;
|
||||
}>;
|
||||
|
||||
export type DeployNodeConfigFormAWSIAMConfigProps = {
|
||||
form: FormInstance;
|
||||
formName: string;
|
||||
disabled?: boolean;
|
||||
initialValues?: DeployNodeConfigFormAWSIAMConfigFieldValues;
|
||||
onValuesChange?: (values: DeployNodeConfigFormAWSIAMConfigFieldValues) => void;
|
||||
};
|
||||
|
||||
const initFormModel = (): DeployNodeConfigFormAWSIAMConfigFieldValues => {
|
||||
return {
|
||||
certificatePath: "/",
|
||||
};
|
||||
};
|
||||
|
||||
const DeployNodeConfigFormAWSIAMConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormAWSIAMConfigProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
region: z
|
||||
.string({ message: t("workflow_node.deploy.form.aws_iam_region.placeholder") })
|
||||
.nonempty(t("workflow_node.deploy.form.aws_iam_region.placeholder"))
|
||||
.trim(),
|
||||
certificatePath: z
|
||||
.string()
|
||||
.nullish()
|
||||
.refine((v) => {
|
||||
if (!v) return true;
|
||||
return v.startsWith("/") && v.endsWith("/");
|
||||
}, t("workflow_node.deploy.form.aws_iam_certificate_path.errmsg.invalid")),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||
onValuesChange?.(values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={formInst}
|
||||
disabled={disabled}
|
||||
initialValues={initialValues ?? initFormModel()}
|
||||
layout="vertical"
|
||||
name={formName}
|
||||
onValuesChange={handleFormChange}
|
||||
>
|
||||
<Form.Item
|
||||
name="region"
|
||||
label={t("workflow_node.deploy.form.aws_iam_region.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_iam_region.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.aws_iam_region.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="certificatePath"
|
||||
label={t("workflow_node.deploy.form.aws_iam_certificate_path.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_iam_certificate_path.tooltip") }}></span>}
|
||||
>
|
||||
<Input allowClear placeholder={t("workflow_node.deploy.form.aws_iam_certificate_path.placeholder")} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployNodeConfigFormAWSIAMConfig;
|
@ -389,6 +389,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
|
||||
ALIYUN_WAF: `${ACCESS_PROVIDERS.ALIYUN}-waf`,
|
||||
AWS_ACM: `${ACCESS_PROVIDERS.AWS}-acm`,
|
||||
AWS_CLOUDFRONT: `${ACCESS_PROVIDERS.AWS}-cloudfront`,
|
||||
AWS_IAM: `${ACCESS_PROVIDERS.AWS}-iam`,
|
||||
AZURE_KEYVAULT: `${ACCESS_PROVIDERS.AZURE}-keyvault`,
|
||||
BAIDUCLOUD_APPBLB: `${ACCESS_PROVIDERS.BAIDUCLOUD}-appblb`,
|
||||
BAIDUCLOUD_BLB: `${ACCESS_PROVIDERS.BAIDUCLOUD}-blb`,
|
||||
@ -561,6 +562,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
|
||||
[DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST, "provider.unicloud.webhost", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT, "provider.aws.cloudfront", DEPLOYMENT_CATEGORIES.CDN],
|
||||
[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],
|
||||
[DEPLOYMENT_PROVIDERS.BUNNY_CDN, "provider.bunny.cdn", DEPLOYMENT_CATEGORIES.CDN],
|
||||
[DEPLOYMENT_PROVIDERS.CACHEFLY, "provider.cachefly", DEPLOYMENT_CATEGORIES.CDN],
|
||||
|
@ -27,6 +27,7 @@
|
||||
"provider.aws": "AWS",
|
||||
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
|
||||
"provider.aws.cloudfront": "AWS - CloudFront",
|
||||
"provider.aws.iam": "AWS - IAM (Identity and Access Management)",
|
||||
"provider.aws.route53": "AWS - Route53",
|
||||
"provider.azure": "Azure",
|
||||
"provider.azure.dns": "Azure - DNS",
|
||||
|
@ -297,6 +297,15 @@
|
||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.label": "AWS CloudFront distribution ID",
|
||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder": "Please enter AWS CloudFront distribution ID",
|
||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html</a>",
|
||||
"workflow_node.deploy.form.aws_cloudfront_certificate_source.label": "AWS CloudFront certificate source",
|
||||
"workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder": "Please select AWS CloudFront certificate source",
|
||||
"workflow_node.deploy.form.aws_iam_region.label": "AWS IAM Region",
|
||||
"workflow_node.deploy.form.aws_iam_region.placeholder": "Please enter AWS IAM region (e.g. us-east-1)",
|
||||
"workflow_node.deploy.form.aws_iam_region.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>",
|
||||
"workflow_node.deploy.form.aws_iam_certificate_path.label": "AWS IAM certificate path (Optional)",
|
||||
"workflow_node.deploy.form.aws_iam_certificate_path.placeholder": "Please enter AWS IAM certificate path",
|
||||
"workflow_node.deploy.form.aws_iam_certificate_path.errmsg.invalid": "Please enter a valid AWS IAM certificate path",
|
||||
"workflow_node.deploy.form.aws_iam_certificate_path.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/reference_identifiers.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/reference_identifiers.html</a>",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.label": "Azure KeyVault name",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.placeholder": "Please enter Azure KeyVault name",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.tooltip": "For more information, see <a href=\"https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates\" target=\"_blank\">https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates</a>",
|
||||
|
@ -27,6 +27,7 @@
|
||||
"provider.aws": "AWS",
|
||||
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
|
||||
"provider.aws.cloudfront": "AWS - CloudFront",
|
||||
"provider.aws.iam": "AWS - IAM (Identity and Access Management)",
|
||||
"provider.aws.route53": "AWS - Route53",
|
||||
"provider.azure": "Azure",
|
||||
"provider.azure.dns": "Azure - DNS",
|
||||
|
@ -296,6 +296,15 @@
|
||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.label": "AWS CloudFront 分配 ID",
|
||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder": "请输入 AWS CloudFront 分配 ID",
|
||||
"workflow_node.deploy.form.aws_cloudfront_distribution_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html</a>",
|
||||
"workflow_node.deploy.form.aws_cloudfront_certificate_source.label": "AWS CloudFront 证书来源",
|
||||
"workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder": "请选择 AWS CloudFront 证书来源",
|
||||
"workflow_node.deploy.form.aws_iam_region.label": "AWS IAM 服务区域",
|
||||
"workflow_node.deploy.form.aws_iam_region.placeholder": "请输入 AWS IAM 服务区域(例如:us-east-1)",
|
||||
"workflow_node.deploy.form.aws_iam_region.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>",
|
||||
"workflow_node.deploy.form.aws_iam_certificate_path.label": "AWS IAM 证书路径(可选)",
|
||||
"workflow_node.deploy.form.aws_iam_certificate_path.placeholder": "请输入 AWS IAM 证书路径",
|
||||
"workflow_node.deploy.form.aws_iam_certificate_path.errmsg.invalid": "请输入正确的 AWS IAM 证书路径",
|
||||
"workflow_node.deploy.form.aws_iam_certificate_path.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/reference_identifiers.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/reference_identifiers.html</a>",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.label": "Azure KeyVault 名称",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.placeholder": "请输入 Azure KeyVault 名称",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.tooltip": "这是什么?请参阅 <a href=\"https://learn.microsoft.com/zh-cn/azure/key-vault/general/about-keys-secrets-certificates\" target=\"_blank\">https://learn.microsoft.com/zh-cn/azure/key-vault/general/about-keys-secrets-certificates</a>",
|
||||
|
Loading…
x
Reference in New Issue
Block a user