diff --git a/internal/pkg/core/uploader/impl/huaweicloud_elb.go b/internal/pkg/core/uploader/impl/huaweicloud_elb.go new file mode 100644 index 00000000..07b20251 --- /dev/null +++ b/internal/pkg/core/uploader/impl/huaweicloud_elb.go @@ -0,0 +1,160 @@ +package impl + +import ( + "context" + "fmt" + "time" + + "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic" + elb "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3" + elbModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/model" + elbRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/region" + + "github.com/usual2970/certimate/internal/pkg/core/uploader" + "github.com/usual2970/certimate/internal/pkg/utils/cast" + "github.com/usual2970/certimate/internal/pkg/utils/x509" +) + +type HuaweiCloudELBUploaderConfig struct { + Region string `json:"region"` + ProjectId string `json:"projectId"` + AccessKeyId string `json:"accessKeyId"` + SecretAccessKey string `json:"secretAccessKey"` +} + +type HuaweiCloudELBUploader struct { + config *HuaweiCloudELBUploaderConfig + sdkClient *elb.ElbClient +} + +func NewHuaweiCloudELBUploader(config *HuaweiCloudELBUploaderConfig) (*HuaweiCloudELBUploader, error) { + client, err := (&HuaweiCloudELBUploader{config: config}).createSdkClient() + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &HuaweiCloudELBUploader{ + config: config, + sdkClient: client, + }, nil +} + +func (u *HuaweiCloudELBUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { + // 解析证书内容 + newCert, err := x509.ParseCertificateFromPEM(certPem) + if err != nil { + return nil, err + } + + // 遍历查询已有证书,避免重复上传 + // REF: https://support.huaweicloud.com/api-elb/ListCertificates.html + listCertificatesPage := 1 + listCertificatesLimit := int32(2000) + var listCertificatesMarker *string = nil + for { + listCertificatesReq := &elbModel.ListCertificatesRequest{ + Limit: cast.Int32Ptr(listCertificatesLimit), + Marker: listCertificatesMarker, + Type: &[]string{"server"}, + } + listCertificatesResp, err := u.sdkClient.ListCertificates(listCertificatesReq) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'elb.ListCertificates': %w", err) + } + + if listCertificatesResp.Certificates != nil { + for _, certDetail := range *listCertificatesResp.Certificates { + var isSameCert bool + if certDetail.Certificate == certPem { + isSameCert = true + } else { + cert, err := x509.ParseCertificateFromPEM(certDetail.Certificate) + if err != nil { + continue + } + + isSameCert = x509.EqualCertificate(cert, newCert) + } + + // 如果已存在相同证书,直接返回已有的证书信息 + if isSameCert { + return &uploader.UploadResult{ + CertId: certDetail.Id, + CertName: certDetail.Name, + }, nil + } + } + } + + if listCertificatesResp.Certificates == nil || len(*listCertificatesResp.Certificates) < int(listCertificatesLimit) { + break + } + + listCertificatesMarker = listCertificatesResp.PageInfo.NextMarker + listCertificatesPage++ + if listCertificatesPage >= 9 { // 避免无限获取 + break + } + } + + // 生成证书名(需符合华为云命名规则) + var certId, certName string + certName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) + + // 创建新证书 + // REF: https://support.huaweicloud.com/api-elb/CreateCertificate.html + createCertificateReq := &elbModel.CreateCertificateRequest{ + Body: &elbModel.CreateCertificateRequestBody{ + Certificate: &elbModel.CreateCertificateOption{ + ProjectId: cast.StringPtr(u.config.ProjectId), + Name: cast.StringPtr(certName), + Certificate: cast.StringPtr(certPem), + PrivateKey: cast.StringPtr(privkeyPem), + }, + }, + } + createCertificateResp, err := u.sdkClient.CreateCertificate(createCertificateReq) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'elb.CreateCertificate': %w", err) + } + + certId = createCertificateResp.Certificate.Id + certName = createCertificateResp.Certificate.Name + return &uploader.UploadResult{ + CertId: certId, + CertName: certName, + }, nil +} + +func (u *HuaweiCloudELBUploader) createSdkClient() (*elb.ElbClient, error) { + region := u.config.Region + accessKeyId := u.config.AccessKeyId + secretAccessKey := u.config.SecretAccessKey + if region == "" { + region = "cn-north-4" // ELB 服务默认区域:华北北京四 + } + + auth, err := basic.NewCredentialsBuilder(). + WithAk(accessKeyId). + WithSk(secretAccessKey). + SafeBuild() + if err != nil { + return nil, err + } + + hcRegion, err := elbRegion.SafeValueOf(region) + if err != nil { + return nil, err + } + + hcClient, err := elb.ElbClientBuilder(). + WithRegion(hcRegion). + WithCredential(auth). + SafeBuild() + if err != nil { + return nil, err + } + + client := elb.NewElbClient(hcClient) + return client, nil +} diff --git a/internal/pkg/core/uploader/impl/huaweicloud_scm.go b/internal/pkg/core/uploader/impl/huaweicloud_scm.go index 48e2d8bc..5469f7de 100644 --- a/internal/pkg/core/uploader/impl/huaweicloud_scm.go +++ b/internal/pkg/core/uploader/impl/huaweicloud_scm.go @@ -22,19 +22,19 @@ type HuaweiCloudSCMUploaderConfig struct { } type HuaweiCloudSCMUploader struct { - config *HuaweiCloudSCMUploaderConfig - client *scm.ScmClient + config *HuaweiCloudSCMUploaderConfig + sdkClient *scm.ScmClient } func NewHuaweiCloudSCMUploader(config *HuaweiCloudSCMUploaderConfig) (*HuaweiCloudSCMUploader, error) { - client, err := createClient(config.Region, config.AccessKeyId, config.SecretAccessKey) + client, err := (&HuaweiCloudSCMUploader{config: config}).createSdkClient() if err != nil { - return nil, fmt.Errorf("failed to create client: %w", err) + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &HuaweiCloudSCMUploader{ - config: config, - client: client, + config: config, + sdkClient: client, }, nil } @@ -48,6 +48,7 @@ func (u *HuaweiCloudSCMUploader) Upload(ctx context.Context, certPem string, pri // 遍历查询已有证书,避免重复上传 // REF: https://support.huaweicloud.com/api-ccm/ListCertificates.html // REF: https://support.huaweicloud.com/api-ccm/ExportCertificate_0.html + listCertificatesPage := 1 listCertificatesLimit := int32(50) listCertificatesOffset := int32(0) for { @@ -57,9 +58,9 @@ func (u *HuaweiCloudSCMUploader) Upload(ctx context.Context, certPem string, pri SortDir: cast.StringPtr("DESC"), SortKey: cast.StringPtr("certExpiredTime"), } - listCertificatesResp, err := u.client.ListCertificates(listCertificatesReq) + listCertificatesResp, err := u.sdkClient.ListCertificates(listCertificatesReq) if err != nil { - return nil, fmt.Errorf("failed to execute request 'scm.ListCertificates': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'scm.ListCertificates': %w", err) } if listCertificatesResp.Certificates != nil { @@ -67,12 +68,12 @@ func (u *HuaweiCloudSCMUploader) Upload(ctx context.Context, certPem string, pri exportCertificateReq := &scmModel.ExportCertificateRequest{ CertificateId: certDetail.Id, } - exportCertificateResp, err := u.client.ExportCertificate(exportCertificateReq) + exportCertificateResp, err := u.sdkClient.ExportCertificate(exportCertificateReq) if err != nil { if exportCertificateResp != nil && exportCertificateResp.HttpStatusCode == 404 { continue } - return nil, fmt.Errorf("failed to execute request 'scm.ExportCertificate': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'scm.ExportCertificate': %w", err) } var isSameCert bool @@ -102,7 +103,8 @@ func (u *HuaweiCloudSCMUploader) Upload(ctx context.Context, certPem string, pri } listCertificatesOffset += listCertificatesLimit - if listCertificatesOffset >= 999 { // 避免无限获取 + listCertificatesPage += 1 + if listCertificatesPage > 99 { // 避免无限获取 break } } @@ -120,9 +122,9 @@ func (u *HuaweiCloudSCMUploader) Upload(ctx context.Context, certPem string, pri PrivateKey: privkeyPem, }, } - importCertificateResp, err := u.client.ImportCertificate(importCertificateReq) + importCertificateResp, err := u.sdkClient.ImportCertificate(importCertificateReq) if err != nil { - return nil, fmt.Errorf("failed to execute request 'scm.ImportCertificate': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'scm.ImportCertificate': %w", err) } certId = *importCertificateResp.CertificateId @@ -132,7 +134,14 @@ func (u *HuaweiCloudSCMUploader) Upload(ctx context.Context, certPem string, pri }, nil } -func (u *HuaweiCloudSCMUploader) createClient(region, accessKeyId, secretAccessKey string) (*scm.ScmClient, error) { +func (u *HuaweiCloudSCMUploader) createSdkClient() (*scm.ScmClient, error) { + region := u.config.Region + accessKeyId := u.config.AccessKeyId + secretAccessKey := u.config.SecretAccessKey + if region == "" { + region = "cn-north-4" // SCM 服务默认区域:华北北京四 + } + auth, err := basic.NewCredentialsBuilder(). WithAk(accessKeyId). WithSk(secretAccessKey). @@ -141,10 +150,6 @@ func (u *HuaweiCloudSCMUploader) createClient(region, accessKeyId, secretAccessK return nil, err } - if region == "" { - region = "cn-north-4" // SCM 服务默认区域:华北北京四 - } - hcRegion, err := scmRegion.SafeValueOf(region) if err != nil { return nil, err