diff --git a/internal/deployer/aliyun_alb.go b/internal/deployer/aliyun_alb.go deleted file mode 100644 index 2e05320e..00000000 --- a/internal/deployer/aliyun_alb.go +++ /dev/null @@ -1,281 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - aliyunAlb "github.com/alibabacloud-go/alb-20200616/v2/client" - aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client" - "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderAliyunCas "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" -) - -type AliyunALBDeployer struct { - option *DeployerOption - infos []string - - sdkClient *aliyunAlb.Client - sslUploader uploader.Uploader -} - -func NewAliyunALBDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.AliyunAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&AliyunALBDeployer{}).createSdkClient( - access.AccessKeyId, - access.AccessKeySecret, - option.DeployConfig.GetConfigAsString("region"), - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - aliCasRegion := option.DeployConfig.GetConfigAsString("region") - if aliCasRegion != "" { - // 阿里云 CAS 服务接入点是独立于 ALB 服务的 - // 国内版接入点:华东一杭州 - // 国际版接入点:亚太东南一新加坡 - if !strings.HasPrefix(aliCasRegion, "cn-") { - aliCasRegion = "ap-southeast-1" - } else { - aliCasRegion = "cn-hangzhou" - } - } - uploader, err := uploaderAliyunCas.New(&uploaderAliyunCas.AliyunCASUploaderConfig{ - AccessKeyId: access.AccessKeyId, - AccessKeySecret: access.AccessKeySecret, - Region: aliCasRegion, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &AliyunALBDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *AliyunALBDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *AliyunALBDeployer) GetInfos() []string { - return d.infos -} - -func (d *AliyunALBDeployer) Deploy(ctx context.Context) error { - switch d.option.DeployConfig.GetConfigAsString("resourceType") { - case "loadbalancer": - if err := d.deployToLoadbalancer(ctx); err != nil { - return err - } - case "listener": - if err := d.deployToListener(ctx); err != nil { - return err - } - default: - return errors.New("unsupported resource type") - } - - return nil -} - -func (d *AliyunALBDeployer) createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunAlb.Client, error) { - if region == "" { - region = "cn-hangzhou" // ALB 服务默认区域:华东一杭州 - } - - aConfig := &aliyunOpen.Config{ - AccessKeyId: tea.String(accessKeyId), - AccessKeySecret: tea.String(accessKeySecret), - } - - var endpoint string - switch region { - case "cn-hangzhou-finance": - endpoint = "alb.cn-hangzhou.aliyuncs.com" - default: - endpoint = fmt.Sprintf("alb.%s.aliyuncs.com", region) - } - aConfig.Endpoint = tea.String(endpoint) - - client, err := aliyunAlb.NewClient(aConfig) - if err != nil { - return nil, err - } - - return client, nil -} - -func (d *AliyunALBDeployer) deployToLoadbalancer(ctx context.Context) error { - aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId") - if aliLoadbalancerId == "" { - return errors.New("`loadbalancerId` is required") - } - - aliListenerIds := make([]string, 0) - - // 查询负载均衡实例的详细信息 - // REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getloadbalancerattribute - getLoadBalancerAttributeReq := &aliyunAlb.GetLoadBalancerAttributeRequest{ - LoadBalancerId: tea.String(aliLoadbalancerId), - } - getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetLoadBalancerAttribute'") - } - - d.infos = append(d.infos, toStr("已查询到 ALB 负载均衡实例", getLoadBalancerAttributeResp)) - - // 查询 HTTPS 监听列表 - // REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners - listListenersPage := 1 - listListenersLimit := int32(100) - var listListenersToken *string = nil - for { - listListenersReq := &aliyunAlb.ListListenersRequest{ - MaxResults: tea.Int32(listListenersLimit), - NextToken: listListenersToken, - LoadBalancerIds: []*string{tea.String(aliLoadbalancerId)}, - ListenerProtocol: tea.String("HTTPS"), - } - listListenersResp, err := d.sdkClient.ListListeners(listListenersReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'") - } - - if listListenersResp.Body.Listeners != nil { - for _, listener := range listListenersResp.Body.Listeners { - aliListenerIds = append(aliListenerIds, *listener.ListenerId) - } - } - - if listListenersResp.Body.NextToken == nil { - break - } else { - listListenersToken = listListenersResp.Body.NextToken - listListenersPage += 1 - } - } - - d.infos = append(d.infos, toStr("已查询到 ALB 负载均衡实例下的全部 HTTPS 监听", aliListenerIds)) - - // 查询 QUIC 监听列表 - // REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners - listListenersPage = 1 - listListenersToken = nil - for { - listListenersReq := &aliyunAlb.ListListenersRequest{ - MaxResults: tea.Int32(listListenersLimit), - NextToken: listListenersToken, - LoadBalancerIds: []*string{tea.String(aliLoadbalancerId)}, - ListenerProtocol: tea.String("QUIC"), - } - listListenersResp, err := d.sdkClient.ListListeners(listListenersReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'") - } - - if listListenersResp.Body.Listeners != nil { - for _, listener := range listListenersResp.Body.Listeners { - aliListenerIds = append(aliListenerIds, *listener.ListenerId) - } - } - - if listListenersResp.Body.NextToken == nil { - break - } else { - listListenersToken = listListenersResp.Body.NextToken - listListenersPage += 1 - } - } - - d.infos = append(d.infos, toStr("已查询到 ALB 负载均衡实例下的全部 QUIC 监听", aliListenerIds)) - - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 批量更新监听证书 - var errs []error - for _, aliListenerId := range aliListenerIds { - if err := d.updateListenerCertificate(ctx, aliListenerId, upres.CertId); err != nil { - errs = append(errs, err) - } - } - if len(errs) > 0 { - return errors.Join(errs...) - } - - return nil -} - -func (d *AliyunALBDeployer) deployToListener(ctx context.Context) error { - aliListenerId := d.option.DeployConfig.GetConfigAsString("listenerId") - if aliListenerId == "" { - return errors.New("`listenerId` is required") - } - - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 更新监听 - if err := d.updateListenerCertificate(ctx, aliListenerId, upres.CertId); err != nil { - return err - } - - return nil -} - -func (d *AliyunALBDeployer) updateListenerCertificate(ctx context.Context, aliListenerId string, aliCertId string) error { - // 查询监听的属性 - // REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getlistenerattribute - getListenerAttributeReq := &aliyunAlb.GetListenerAttributeRequest{ - ListenerId: tea.String(aliListenerId), - } - getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetListenerAttribute'") - } - - d.infos = append(d.infos, toStr("已查询到 ALB 监听配置", getListenerAttributeResp)) - - // 修改监听的属性 - // REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-updatelistenerattribute - updateListenerAttributeReq := &aliyunAlb.UpdateListenerAttributeRequest{ - ListenerId: tea.String(aliListenerId), - Certificates: []*aliyunAlb.UpdateListenerAttributeRequestCertificates{{ - CertificateId: tea.String(aliCertId), - }}, - } - updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.UpdateListenerAttribute'") - } - - d.infos = append(d.infos, toStr("已更新 ALB 监听配置", updateListenerAttributeResp)) - - return nil -} diff --git a/internal/deployer/aliyun_cdn.go b/internal/deployer/aliyun_cdn.go deleted file mode 100644 index a23ce8a5..00000000 --- a/internal/deployer/aliyun_cdn.go +++ /dev/null @@ -1,88 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "time" - - aliyunCdn "github.com/alibabacloud-go/cdn-20180510/v5/client" - aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client" - "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" -) - -type AliyunCDNDeployer struct { - option *DeployerOption - infos []string - - sdkClient *aliyunCdn.Client -} - -func NewAliyunCDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.AliyunAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&AliyunCDNDeployer{}).createSdkClient( - access.AccessKeyId, - access.AccessKeySecret, - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - return &AliyunCDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - }, nil -} - -func (d *AliyunCDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *AliyunCDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *AliyunCDNDeployer) Deploy(ctx context.Context) error { - // 设置 CDN 域名域名证书 - // REF: https://help.aliyun.com/zh/cdn/developer-reference/api-cdn-2018-05-10-setcdndomainsslcertificate - setCdnDomainSSLCertificateReq := &aliyunCdn.SetCdnDomainSSLCertificateRequest{ - DomainName: tea.String(d.option.DeployConfig.GetConfigAsString("domain")), - CertRegion: tea.String(d.option.DeployConfig.GetConfigOrDefaultAsString("region", "cn-hangzhou")), - CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), - CertType: tea.String("upload"), - SSLProtocol: tea.String("on"), - SSLPub: tea.String(d.option.Certificate.Certificate), - SSLPri: tea.String(d.option.Certificate.PrivateKey), - } - setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificate(setCdnDomainSSLCertificateReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.SetCdnDomainSSLCertificate'") - } - - d.infos = append(d.infos, toStr("已设置 CDN 域名证书", setCdnDomainSSLCertificateResp)) - - return nil -} - -func (d *AliyunCDNDeployer) createSdkClient(accessKeyId, accessKeySecret string) (*aliyunCdn.Client, error) { - aConfig := &aliyunOpen.Config{ - AccessKeyId: tea.String(accessKeyId), - AccessKeySecret: tea.String(accessKeySecret), - Endpoint: tea.String("cdn.aliyuncs.com"), - } - - client, err := aliyunCdn.NewClient(aConfig) - if err != nil { - return nil, err - } - - return client, nil -} diff --git a/internal/deployer/aliyun_clb.go b/internal/deployer/aliyun_clb.go deleted file mode 100644 index 95d32b5d..00000000 --- a/internal/deployer/aliyun_clb.go +++ /dev/null @@ -1,286 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client" - aliyunSlb "github.com/alibabacloud-go/slb-20140515/v4/client" - "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderAliyunSlb "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb" -) - -type AliyunCLBDeployer struct { - option *DeployerOption - infos []string - - sdkClient *aliyunSlb.Client - sslUploader uploader.Uploader -} - -func NewAliyunCLBDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.AliyunAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&AliyunCLBDeployer{}).createSdkClient( - access.AccessKeyId, - access.AccessKeySecret, - option.DeployConfig.GetConfigAsString("region"), - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - uploader, err := uploaderAliyunSlb.New(&uploaderAliyunSlb.AliyunSLBUploaderConfig{ - AccessKeyId: access.AccessKeyId, - AccessKeySecret: access.AccessKeySecret, - Region: option.DeployConfig.GetConfigAsString("region"), - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &AliyunCLBDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *AliyunCLBDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *AliyunCLBDeployer) GetInfos() []string { - return d.infos -} - -func (d *AliyunCLBDeployer) Deploy(ctx context.Context) error { - switch d.option.DeployConfig.GetConfigAsString("resourceType") { - case "loadbalancer": - if err := d.deployToLoadbalancer(ctx); err != nil { - return err - } - case "listener": - if err := d.deployToListener(ctx); err != nil { - return err - } - default: - return errors.New("unsupported resource type") - } - - return nil -} - -func (d *AliyunCLBDeployer) createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunSlb.Client, error) { - if region == "" { - region = "cn-hangzhou" // CLB(SLB) 服务默认区域:华东一杭州 - } - - aConfig := &aliyunOpen.Config{ - AccessKeyId: tea.String(accessKeyId), - AccessKeySecret: tea.String(accessKeySecret), - } - - var endpoint string - switch region { - case - "cn-hangzhou", - "cn-hangzhou-finance", - "cn-shanghai-finance-1", - "cn-shenzhen-finance-1": - endpoint = "slb.aliyuncs.com" - default: - endpoint = fmt.Sprintf("slb.%s.aliyuncs.com", region) - } - aConfig.Endpoint = tea.String(endpoint) - - client, err := aliyunSlb.NewClient(aConfig) - if err != nil { - return nil, err - } - - return client, nil -} - -func (d *AliyunCLBDeployer) deployToLoadbalancer(ctx context.Context) error { - aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId") - aliListenerPorts := make([]int32, 0) - if aliLoadbalancerId == "" { - return errors.New("`loadbalancerId` is required") - } - - // 查询负载均衡实例的详细信息 - // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerattribute - describeLoadBalancerAttributeReq := &aliyunSlb.DescribeLoadBalancerAttributeRequest{ - RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")), - LoadBalancerId: tea.String(aliLoadbalancerId), - } - describeLoadBalancerAttributeResp, err := d.sdkClient.DescribeLoadBalancerAttribute(describeLoadBalancerAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerAttribute'") - } - - d.infos = append(d.infos, toStr("已查询到 CLB 负载均衡实例", describeLoadBalancerAttributeResp)) - - // 查询 HTTPS 监听列表 - // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerlisteners - listListenersPage := 1 - listListenersLimit := int32(100) - var listListenersToken *string = nil - for { - describeLoadBalancerListenersReq := &aliyunSlb.DescribeLoadBalancerListenersRequest{ - RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")), - MaxResults: tea.Int32(listListenersLimit), - NextToken: listListenersToken, - LoadBalancerId: []*string{tea.String(aliLoadbalancerId)}, - ListenerProtocol: tea.String("https"), - } - describeLoadBalancerListenersResp, err := d.sdkClient.DescribeLoadBalancerListeners(describeLoadBalancerListenersReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerListeners'") - } - - if describeLoadBalancerListenersResp.Body.Listeners != nil { - for _, listener := range describeLoadBalancerListenersResp.Body.Listeners { - aliListenerPorts = append(aliListenerPorts, *listener.ListenerPort) - } - } - - if describeLoadBalancerListenersResp.Body.NextToken == nil { - break - } else { - listListenersToken = describeLoadBalancerListenersResp.Body.NextToken - listListenersPage += 1 - } - } - - d.infos = append(d.infos, toStr("已查询到 CLB 负载均衡实例下的全部 HTTPS 监听", aliListenerPorts)) - - // 上传证书到 SLB - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 批量更新监听证书 - var errs []error - for _, aliListenerPort := range aliListenerPorts { - if err := d.updateListenerCertificate(ctx, aliLoadbalancerId, aliListenerPort, upres.CertId); err != nil { - errs = append(errs, err) - } - } - if len(errs) > 0 { - return errors.Join(errs...) - } - - return nil -} - -func (d *AliyunCLBDeployer) deployToListener(ctx context.Context) error { - aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId") - if aliLoadbalancerId == "" { - return errors.New("`loadbalancerId` is required") - } - - aliListenerPort := d.option.DeployConfig.GetConfigAsInt32("listenerPort") - if aliListenerPort == 0 { - return errors.New("`listenerPort` is required") - } - - // 上传证书到 SLB - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 更新监听 - if err := d.updateListenerCertificate(ctx, aliLoadbalancerId, aliListenerPort, upres.CertId); err != nil { - return err - } - - return nil -} - -func (d *AliyunCLBDeployer) updateListenerCertificate(ctx context.Context, aliLoadbalancerId string, aliListenerPort int32, aliCertId string) error { - // 查询监听配置 - // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerhttpslistenerattribute - describeLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.DescribeLoadBalancerHTTPSListenerAttributeRequest{ - LoadBalancerId: tea.String(aliLoadbalancerId), - ListenerPort: tea.Int32(aliListenerPort), - } - describeLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.DescribeLoadBalancerHTTPSListenerAttribute(describeLoadBalancerHTTPSListenerAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute'") - } - - d.infos = append(d.infos, toStr("已查询到 CLB HTTPS 监听配置", describeLoadBalancerHTTPSListenerAttributeResp)) - - // 查询扩展域名 - // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describedomainextensions - describeDomainExtensionsReq := &aliyunSlb.DescribeDomainExtensionsRequest{ - RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")), - LoadBalancerId: tea.String(aliLoadbalancerId), - ListenerPort: tea.Int32(aliListenerPort), - } - describeDomainExtensionsResp, err := d.sdkClient.DescribeDomainExtensions(describeDomainExtensionsReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeDomainExtensions'") - } - - d.infos = append(d.infos, toStr("已查询到 CLB 扩展域名", describeDomainExtensionsResp)) - - // 遍历修改扩展域名 - // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setdomainextensionattribute - // - // 这里仅修改跟被替换证书一致的扩展域名 - if describeDomainExtensionsResp.Body.DomainExtensions != nil && describeDomainExtensionsResp.Body.DomainExtensions.DomainExtension != nil { - for _, domainExtension := range describeDomainExtensionsResp.Body.DomainExtensions.DomainExtension { - if *domainExtension.ServerCertificateId != *describeLoadBalancerHTTPSListenerAttributeResp.Body.ServerCertificateId { - continue - } - - setDomainExtensionAttributeReq := &aliyunSlb.SetDomainExtensionAttributeRequest{ - RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")), - DomainExtensionId: tea.String(*domainExtension.DomainExtensionId), - ServerCertificateId: tea.String(aliCertId), - } - _, err := d.sdkClient.SetDomainExtensionAttribute(setDomainExtensionAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.SetDomainExtensionAttribute'") - } - } - } - - // 修改监听配置 - // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setloadbalancerhttpslistenerattribute - // - // 注意修改监听配置要放在修改扩展域名之后 - setLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.SetLoadBalancerHTTPSListenerAttributeRequest{ - RegionId: tea.String(d.option.DeployConfig.GetConfigAsString("region")), - LoadBalancerId: tea.String(aliLoadbalancerId), - ListenerPort: tea.Int32(aliListenerPort), - ServerCertificateId: tea.String(aliCertId), - } - setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.SetLoadBalancerHTTPSListenerAttribute(setLoadBalancerHTTPSListenerAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute'") - } - - d.infos = append(d.infos, toStr("已更新 CLB HTTPS 监听配置", setLoadBalancerHTTPSListenerAttributeResp)) - - return nil -} diff --git a/internal/deployer/aliyun_dcdn.go b/internal/deployer/aliyun_dcdn.go deleted file mode 100644 index 0f969787..00000000 --- a/internal/deployer/aliyun_dcdn.go +++ /dev/null @@ -1,95 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "strings" - "time" - - aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client" - aliyunDcdn "github.com/alibabacloud-go/dcdn-20180115/v3/client" - "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" -) - -type AliyunDCDNDeployer struct { - option *DeployerOption - infos []string - - sdkClient *aliyunDcdn.Client -} - -func NewAliyunDCDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.AliyunAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&AliyunDCDNDeployer{}).createSdkClient( - access.AccessKeyId, - access.AccessKeySecret, - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - return &AliyunDCDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - }, nil -} - -func (d *AliyunDCDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *AliyunDCDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *AliyunDCDNDeployer) Deploy(ctx context.Context) error { - // 支持泛解析域名,在 Aliyun DCDN 中泛解析域名表示为 .example.com - domain := d.option.DeployConfig.GetConfigAsString("domain") - if strings.HasPrefix(domain, "*") { - domain = strings.TrimPrefix(domain, "*") - } - - // 配置域名证书 - // REF: https://help.aliyun.com/zh/edge-security-acceleration/dcdn/developer-reference/api-dcdn-2018-01-15-setdcdndomainsslcertificate - setDcdnDomainSSLCertificateReq := &aliyunDcdn.SetDcdnDomainSSLCertificateRequest{ - DomainName: tea.String(domain), - CertRegion: tea.String(d.option.DeployConfig.GetConfigOrDefaultAsString("region", "cn-hangzhou")), - CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), - CertType: tea.String("upload"), - SSLProtocol: tea.String("on"), - SSLPub: tea.String(d.option.Certificate.Certificate), - SSLPri: tea.String(d.option.Certificate.PrivateKey), - } - setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificate(setDcdnDomainSSLCertificateReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'dcdn.SetDcdnDomainSSLCertificate'") - } - - d.infos = append(d.infos, toStr("已配置 DCDN 域名证书", setDcdnDomainSSLCertificateResp)) - - return nil -} - -func (d *AliyunDCDNDeployer) createSdkClient(accessKeyId, accessKeySecret string) (*aliyunDcdn.Client, error) { - aConfig := &aliyunOpen.Config{ - AccessKeyId: tea.String(accessKeyId), - AccessKeySecret: tea.String(accessKeySecret), - Endpoint: tea.String("dcdn.aliyuncs.com"), - } - - client, err := aliyunDcdn.NewClient(aConfig) - if err != nil { - return nil, err - } - - return client, nil -} diff --git a/internal/deployer/aliyun_nlb.go b/internal/deployer/aliyun_nlb.go deleted file mode 100644 index bbaddd7b..00000000 --- a/internal/deployer/aliyun_nlb.go +++ /dev/null @@ -1,245 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client" - aliyunNlb "github.com/alibabacloud-go/nlb-20220430/v2/client" - "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderAliyunCas "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" -) - -type AliyunNLBDeployer struct { - option *DeployerOption - infos []string - - sdkClient *aliyunNlb.Client - sslUploader uploader.Uploader -} - -func NewAliyunNLBDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.AliyunAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&AliyunNLBDeployer{}).createSdkClient( - access.AccessKeyId, - access.AccessKeySecret, - option.DeployConfig.GetConfigAsString("region"), - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - aliCasRegion := option.DeployConfig.GetConfigAsString("region") - if aliCasRegion != "" { - // 阿里云 CAS 服务接入点是独立于 NLB 服务的 - // 国内版接入点:华东一杭州 - // 国际版接入点:亚太东南一新加坡 - if !strings.HasPrefix(aliCasRegion, "cn-") { - aliCasRegion = "ap-southeast-1" - } else { - aliCasRegion = "cn-hangzhou" - } - } - uploader, err := uploaderAliyunCas.New(&uploaderAliyunCas.AliyunCASUploaderConfig{ - AccessKeyId: access.AccessKeyId, - AccessKeySecret: access.AccessKeySecret, - Region: aliCasRegion, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &AliyunNLBDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *AliyunNLBDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *AliyunNLBDeployer) GetInfos() []string { - return d.infos -} - -func (d *AliyunNLBDeployer) Deploy(ctx context.Context) error { - switch d.option.DeployConfig.GetConfigAsString("resourceType") { - case "loadbalancer": - if err := d.deployToLoadbalancer(ctx); err != nil { - return err - } - case "listener": - if err := d.deployToListener(ctx); err != nil { - return err - } - default: - return errors.New("unsupported resource type") - } - - return nil -} - -func (d *AliyunNLBDeployer) createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunNlb.Client, error) { - if region == "" { - region = "cn-hangzhou" // NLB 服务默认区域:华东一杭州 - } - - aConfig := &aliyunOpen.Config{ - AccessKeyId: tea.String(accessKeyId), - AccessKeySecret: tea.String(accessKeySecret), - } - - var endpoint string - switch region { - default: - endpoint = fmt.Sprintf("nlb.%s.aliyuncs.com", region) - } - aConfig.Endpoint = tea.String(endpoint) - - client, err := aliyunNlb.NewClient(aConfig) - if err != nil { - return nil, err - } - - return client, nil -} - -func (d *AliyunNLBDeployer) deployToLoadbalancer(ctx context.Context) error { - aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId") - if aliLoadbalancerId == "" { - return errors.New("`loadbalancerId` is required") - } - - aliListenerIds := make([]string, 0) - - // 查询负载均衡实例的详细信息 - // REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getloadbalancerattribute - getLoadBalancerAttributeReq := &aliyunNlb.GetLoadBalancerAttributeRequest{ - LoadBalancerId: tea.String(aliLoadbalancerId), - } - getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetLoadBalancerAttribute'") - } - - d.infos = append(d.infos, toStr("已查询到 NLB 负载均衡实例", getLoadBalancerAttributeResp)) - - // 查询 TCPSSL 监听列表 - // REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-listlisteners - listListenersPage := 1 - listListenersLimit := int32(100) - var listListenersToken *string = nil - for { - listListenersReq := &aliyunNlb.ListListenersRequest{ - MaxResults: tea.Int32(listListenersLimit), - NextToken: listListenersToken, - LoadBalancerIds: []*string{tea.String(aliLoadbalancerId)}, - ListenerProtocol: tea.String("TCPSSL"), - } - listListenersResp, err := d.sdkClient.ListListeners(listListenersReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'nlb.ListListeners'") - } - - if listListenersResp.Body.Listeners != nil { - for _, listener := range listListenersResp.Body.Listeners { - aliListenerIds = append(aliListenerIds, *listener.ListenerId) - } - } - - if listListenersResp.Body.NextToken == nil { - break - } else { - listListenersToken = listListenersResp.Body.NextToken - listListenersPage += 1 - } - } - - d.infos = append(d.infos, toStr("已查询到 NLB 负载均衡实例下的全部 TCPSSL 监听", aliListenerIds)) - - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 批量更新监听证书 - var errs []error - for _, aliListenerId := range aliListenerIds { - if err := d.updateListenerCertificate(ctx, aliListenerId, upres.CertId); err != nil { - errs = append(errs, err) - } - } - if len(errs) > 0 { - return errors.Join(errs...) - } - - return nil -} - -func (d *AliyunNLBDeployer) deployToListener(ctx context.Context) error { - aliListenerId := d.option.DeployConfig.GetConfigAsString("listenerId") - if aliListenerId == "" { - return errors.New("`listenerId` is required") - } - - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 更新监听 - if err := d.updateListenerCertificate(ctx, aliListenerId, upres.CertId); err != nil { - return err - } - - return nil -} - -func (d *AliyunNLBDeployer) updateListenerCertificate(ctx context.Context, aliListenerId string, aliCertId string) error { - // 查询监听的属性 - // REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getlistenerattribute - getListenerAttributeReq := &aliyunNlb.GetListenerAttributeRequest{ - ListenerId: tea.String(aliListenerId), - } - getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetListenerAttribute'") - } - - d.infos = append(d.infos, toStr("已查询到 NLB 监听配置", getListenerAttributeResp)) - - // 修改监听的属性 - // REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-updatelistenerattribute - updateListenerAttributeReq := &aliyunNlb.UpdateListenerAttributeRequest{ - ListenerId: tea.String(aliListenerId), - CertificateIds: []*string{tea.String(aliCertId)}, - } - updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'nlb.UpdateListenerAttribute'") - } - - d.infos = append(d.infos, toStr("已更新 NLB 监听配置", updateListenerAttributeResp)) - - return nil -} diff --git a/internal/deployer/aliyun_oss.go b/internal/deployer/aliyun_oss.go deleted file mode 100644 index d0045d89..00000000 --- a/internal/deployer/aliyun_oss.go +++ /dev/null @@ -1,86 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - "github.com/aliyun/aliyun-oss-go-sdk/oss" - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" -) - -type AliyunOSSDeployer struct { - option *DeployerOption - infos []string - - sdkClient *oss.Client -} - -func NewAliyunOSSDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.AliyunAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&AliyunOSSDeployer{}).createSdkClient( - access.AccessKeyId, - access.AccessKeySecret, - option.DeployConfig.GetConfigAsString("endpoint"), - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - return &AliyunOSSDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - }, nil -} - -func (d *AliyunOSSDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *AliyunOSSDeployer) GetInfos() []string { - return d.infos -} - -func (d *AliyunOSSDeployer) Deploy(ctx context.Context) error { - aliBucket := d.option.DeployConfig.GetConfigAsString("bucket") - if aliBucket == "" { - return errors.New("`bucket` is required") - } - - // 为存储空间绑定自定义域名 - // REF: https://help.aliyun.com/zh/oss/developer-reference/putcname - err := d.sdkClient.PutBucketCnameWithCertificate(aliBucket, oss.PutBucketCname{ - Cname: d.option.DeployConfig.GetConfigAsString("domain"), - CertificateConfiguration: &oss.CertificateConfiguration{ - Certificate: d.option.Certificate.Certificate, - PrivateKey: d.option.Certificate.PrivateKey, - Force: true, - }, - }) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'oss.PutBucketCnameWithCertificate'") - } - - return nil -} - -func (d *AliyunOSSDeployer) createSdkClient(accessKeyId, accessKeySecret, endpoint string) (*oss.Client, error) { - if endpoint == "" { - endpoint = "oss.aliyuncs.com" - } - - client, err := oss.New(endpoint, accessKeyId, accessKeySecret) - if err != nil { - return nil, err - } - - return client, nil -} diff --git a/internal/deployer/baiducloud_cdn.go b/internal/deployer/baiducloud_cdn.go deleted file mode 100644 index 31d789df..00000000 --- a/internal/deployer/baiducloud_cdn.go +++ /dev/null @@ -1,80 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "time" - - bceCdn "github.com/baidubce/bce-sdk-go/services/cdn" - bceCdnApi "github.com/baidubce/bce-sdk-go/services/cdn/api" - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" -) - -type BaiduCloudCDNDeployer struct { - option *DeployerOption - infos []string - - sdkClient *bceCdn.Client -} - -func NewBaiduCloudCDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.BaiduCloudAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&BaiduCloudCDNDeployer{}).createSdkClient( - access.AccessKeyId, - access.SecretAccessKey, - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - return &BaiduCloudCDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - }, nil -} - -func (d *BaiduCloudCDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *BaiduCloudCDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *BaiduCloudCDNDeployer) Deploy(ctx context.Context) error { - // 修改域名证书 - // REF: https://cloud.baidu.com/doc/CDN/s/qjzuz2hp8 - putCertResp, err := d.sdkClient.PutCert( - d.option.DeployConfig.GetConfigAsString("domain"), - &bceCdnApi.UserCertificate{ - CertName: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), - ServerData: d.option.Certificate.Certificate, - PrivateData: d.option.Certificate.PrivateKey, - }, - "ON", - ) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.PutCert'") - } - - d.infos = append(d.infos, toStr("已修改域名证书", putCertResp)) - - return nil -} - -func (d *BaiduCloudCDNDeployer) createSdkClient(accessKeyId, secretAccessKey string) (*bceCdn.Client, error) { - client, err := bceCdn.NewClient(accessKeyId, secretAccessKey, "") - if err != nil { - return nil, err - } - - return client, nil -} diff --git a/internal/deployer/byteplus_cdn.go b/internal/deployer/byteplus_cdn.go deleted file mode 100644 index 17c51522..00000000 --- a/internal/deployer/byteplus_cdn.go +++ /dev/null @@ -1,116 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - bytepluscdn "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/byteplus-cdn" - - "github.com/byteplus-sdk/byteplus-sdk-golang/service/cdn" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" -) - -type ByteplusCDNDeployer struct { - option *DeployerOption - infos []string - sdkClient *cdn.CDN - sslUploader uploader.Uploader -} - -func NewByteplusCDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.ByteplusAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - client := cdn.NewInstance() - client.Client.SetAccessKey(access.AccessKey) - client.Client.SetSecretKey(access.SecretKey) - uploader, err := bytepluscdn.New(&bytepluscdn.ByteplusCDNUploaderConfig{ - AccessKey: access.AccessKey, - SecretKey: access.SecretKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - return &ByteplusCDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *ByteplusCDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *ByteplusCDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *ByteplusCDNDeployer) Deploy(ctx context.Context) error { - apiCtx := context.Background() - // 上传证书 - upres, err := d.sslUploader.Upload(apiCtx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - domains := make([]string, 0) - configDomain := d.option.DeployConfig.GetConfigAsString("domain") - if strings.HasPrefix(configDomain, "*.") { - // 获取证书可以部署的域名 - // REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-describecertconfig-9ea17 - describeCertConfigReq := &cdn.DescribeCertConfigRequest{ - CertId: upres.CertId, - } - describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertConfig'") - } - for i := range describeCertConfigResp.Result.CertNotConfig { - // 当前未启用 HTTPS 的加速域名列表。 - domains = append(domains, describeCertConfigResp.Result.CertNotConfig[i].Domain) - } - for i := range describeCertConfigResp.Result.OtherCertConfig { - // 已启用了 HTTPS 的加速域名列表。这些加速域名关联的证书不是您指定的证书。 - domains = append(domains, describeCertConfigResp.Result.OtherCertConfig[i].Domain) - } - for i := range describeCertConfigResp.Result.SpecifiedCertConfig { - // 已启用了 HTTPS 的加速域名列表。这些加速域名关联了您指定的证书。 - d.infos = append(d.infos, fmt.Sprintf("%s域名已配置该证书", describeCertConfigResp.Result.SpecifiedCertConfig[i].Domain)) - } - if len(domains) == 0 { - if len(describeCertConfigResp.Result.SpecifiedCertConfig) > 0 { - // 所有匹配的域名都配置了该证书,跳过部署 - return nil - } else { - return xerrors.Errorf("未查询到匹配的域名: %s", configDomain) - } - } - } else { - domains = append(domains, configDomain) - } - // 部署证书 - // REF: https://github.com/byteplus-sdk/byteplus-sdk-golang/blob/master/service/cdn/api_list.go#L306 - for i := range domains { - batchDeployCertReq := &cdn.BatchDeployCertRequest{ - CertId: upres.CertId, - Domain: domains[i], - } - batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.BatchDeployCert'") - } else { - d.infos = append(d.infos, toStr(fmt.Sprintf("%s域名的证书已修改", domains[i]), batchDeployCertResp)) - } - } - - return nil -} diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index a467694d..c5013d9d 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -2,14 +2,13 @@ package deployer import ( "context" - "encoding/json" - "errors" "fmt" "github.com/pocketbase/pocketbase/models" "github.com/usual2970/certimate/internal/applicant" "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/repository" ) @@ -79,7 +78,7 @@ func Gets(record *models.Record, cert *applicant.Certificate) ([]Deployer, error } for _, deployConfig := range deployConfigs { - deployer, err := getWithDeployConfig(record, cert, deployConfig) + deployer, err := newWithDeployConfig(record, cert, deployConfig) if err != nil { return nil, err } @@ -91,10 +90,10 @@ func Gets(record *models.Record, cert *applicant.Certificate) ([]Deployer, error } func GetWithTypeAndOption(deployType string, option *DeployerOption) (Deployer, error) { - return getWithTypeAndOption(deployType, option) + return newWithTypeAndOption(deployType, option) } -func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, deployConfig domain.DeployConfig) (Deployer, error) { +func newWithDeployConfig(record *models.Record, cert *applicant.Certificate, deployConfig domain.DeployConfig) (Deployer, error) { accessRepo := repository.NewAccessRepository() access, err := accessRepo.GetById(context.Background(), deployConfig.Access) if err != nil { @@ -117,65 +116,38 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep } } - return getWithTypeAndOption(deployConfig.Type, option) + return newWithTypeAndOption(deployConfig.Type, option) } -func getWithTypeAndOption(deployType string, option *DeployerOption) (Deployer, error) { - switch deployType { - case targetAliyunOSS: - return NewAliyunOSSDeployer(option) - case targetAliyunCDN: - return NewAliyunCDNDeployer(option) - case targetAliyunDCDN: - return NewAliyunDCDNDeployer(option) - case targetAliyunCLB: - return NewAliyunCLBDeployer(option) - case targetAliyunALB: - return NewAliyunALBDeployer(option) - case targetAliyunNLB: - return NewAliyunNLBDeployer(option) - case targetTencentCloudCDN: - return NewTencentCDNDeployer(option) - case targetTencentCloudECDN: - return NewTencentECDNDeployer(option) - case targetTencentCloudCLB: - return NewTencentCLBDeployer(option) - case targetTencentCloudCOS: - return NewTencentCOSDeployer(option) - case targetTencentCloudEO: - return NewTencentTEODeployer(option) - case targetHuaweiCloudCDN: - return NewHuaweiCloudCDNDeployer(option) - case targetHuaweiCloudELB: - return NewHuaweiCloudELBDeployer(option) - case targetBaiduCloudCDN: - return NewBaiduCloudCDNDeployer(option) - case targetQiniuCDN: - return NewQiniuCDNDeployer(option) - case targetDogeCloudCDN: - return NewDogeCloudCDNDeployer(option) - case targetLocal: - return NewLocalDeployer(option) - case targetSSH: - return NewSSHDeployer(option) - case targetWebhook: - return NewWebhookDeployer(option) - case targetK8sSecret: - return NewK8sSecretDeployer(option) - case targetVolcEngineLive: - return NewVolcengineLiveDeployer(option) - case targetVolcEngineCDN: - return NewVolcengineCDNDeployer(option) - case targetBytePlusCDN: - return NewByteplusCDNDeployer(option) +func newWithTypeAndOption(deployType string, option *DeployerOption) (Deployer, error) { + deployer, logger, err := createDeployer(deployType, option.AccessRecord.Config, option.DeployConfig.Config) + if err != nil { + return nil, err } - return nil, errors.New("unsupported deploy target") + + return &proxyDeployer{ + option: option, + logger: logger, + deployer: deployer, + }, nil } -func toStr(tag string, data any) string { - if data == nil { - return tag - } - byts, _ := json.Marshal(data) - return tag + ":" + string(byts) +// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑 +type proxyDeployer struct { + option *DeployerOption + logger deployer.Logger + deployer deployer.Deployer +} + +func (d *proxyDeployer) GetID() string { + return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) +} + +func (d *proxyDeployer) GetInfos() []string { + return d.logger.GetRecords() +} + +func (d *proxyDeployer) Deploy(ctx context.Context) error { + _, err := d.deployer.Deploy(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) + return err } diff --git a/internal/deployer/dogecloud_cdn.go b/internal/deployer/dogecloud_cdn.go deleted file mode 100644 index 55e526c1..00000000 --- a/internal/deployer/dogecloud_cdn.go +++ /dev/null @@ -1,88 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "strconv" - - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderDoge "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/dogecloud" - doge "github.com/usual2970/certimate/internal/pkg/vendors/dogecloud-sdk" -) - -type DogeCloudCDNDeployer struct { - option *DeployerOption - infos []string - - sdkClient *doge.Client - sslUploader uploader.Uploader -} - -func NewDogeCloudCDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.DogeCloudAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&DogeCloudCDNDeployer{}).createSdkClient( - access.AccessKey, - access.SecretKey, - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - uploader, err := uploaderDoge.New(&uploaderDoge.DogeCloudUploaderConfig{ - AccessKey: access.AccessKey, - SecretKey: access.SecretKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &DogeCloudCDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *DogeCloudCDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *DogeCloudCDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *DogeCloudCDNDeployer) Deploy(ctx context.Context) error { - // 上传证书到 CDN - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 绑定证书 - // REF: https://docs.dogecloud.com/cdn/api-cert-bind - bindCdnCertId, _ := strconv.ParseInt(upres.CertId, 10, 64) - bindCdnCertResp, err := d.sdkClient.BindCdnCertWithDomain(bindCdnCertId, d.option.DeployConfig.GetConfigAsString("domain")) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.BindCdnCert'") - } - - d.infos = append(d.infos, toStr("已绑定证书", bindCdnCertResp)) - - return nil -} - -func (d *DogeCloudCDNDeployer) createSdkClient(accessKey, secretKey string) (*doge.Client, error) { - client := doge.NewClient(accessKey, secretKey) - return client, nil -} diff --git a/internal/deployer/factory.go b/internal/deployer/factory.go index d0ab4621..f4fde3e7 100644 --- a/internal/deployer/factory.go +++ b/internal/deployer/factory.go @@ -362,9 +362,23 @@ func createDeployer(target string, accessConfig string, deployConfig map[string] return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) } + variables := make(map[string]string) + if deployConfig != nil { + value, ok := deployConfig["variables"] + if ok { + kvs := make([]domain.KV, 0) + bts, _ := json.Marshal(value) + if err := json.Unmarshal(bts, &kvs); err == nil { + for _, kv := range kvs { + variables[kv.Key] = kv.Value + } + } + } + } + deployer, err := providerWebhook.NewWithLogger(&providerWebhook.WebhookDeployerConfig{ Url: access.Url, - Variables: nil, // TODO: 尚未实现 + Variables: variables, }, logger) return deployer, logger, err } diff --git a/internal/deployer/huaweicloud_cdn.go b/internal/deployer/huaweicloud_cdn.go deleted file mode 100644 index d3d4014f..00000000 --- a/internal/deployer/huaweicloud_cdn.go +++ /dev/null @@ -1,143 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global" - hcCdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2" - hcCdnModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/model" - hcCdnRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/region" - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderHcScm "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-scm" - "github.com/usual2970/certimate/internal/pkg/utils/cast" - hcCdnEx "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-cdn-sdk" -) - -type HuaweiCloudCDNDeployer struct { - option *DeployerOption - infos []string - - sdkClient *hcCdnEx.Client - sslUploader uploader.Uploader -} - -func NewHuaweiCloudCDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.HuaweiCloudAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&HuaweiCloudCDNDeployer{}).createSdkClient( - access.AccessKeyId, - access.SecretAccessKey, - option.DeployConfig.GetConfigAsString("region"), - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - uploader, err := uploaderHcScm.New(&uploaderHcScm.HuaweiCloudSCMUploaderConfig{ - AccessKeyId: access.AccessKeyId, - SecretAccessKey: access.SecretAccessKey, - Region: "", - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &HuaweiCloudCDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *HuaweiCloudCDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *HuaweiCloudCDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *HuaweiCloudCDNDeployer) Deploy(ctx context.Context) error { - // 上传证书到 SCM - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 查询加速域名配置 - // REF: https://support.huaweicloud.com/api-cdn/ShowDomainFullConfig.html - showDomainFullConfigReq := &hcCdnModel.ShowDomainFullConfigRequest{ - DomainName: d.option.DeployConfig.GetConfigAsString("domain"), - } - showDomainFullConfigResp, err := d.sdkClient.ShowDomainFullConfig(showDomainFullConfigReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.ShowDomainFullConfig'") - } - - d.infos = append(d.infos, toStr("已查询到加速域名配置", showDomainFullConfigResp)) - - // 更新加速域名配置 - // REF: https://support.huaweicloud.com/api-cdn/UpdateDomainMultiCertificates.html - // REF: https://support.huaweicloud.com/usermanual-cdn/cdn_01_0306.html - updateDomainMultiCertificatesReqBodyContent := &hcCdnEx.UpdateDomainMultiCertificatesExRequestBodyContent{} - updateDomainMultiCertificatesReqBodyContent.DomainName = d.option.DeployConfig.GetConfigAsString("domain") - updateDomainMultiCertificatesReqBodyContent.HttpsSwitch = 1 - updateDomainMultiCertificatesReqBodyContent.CertificateType = cast.Int32Ptr(2) - updateDomainMultiCertificatesReqBodyContent.SCMCertificateId = cast.StringPtr(upres.CertId) - updateDomainMultiCertificatesReqBodyContent.CertName = cast.StringPtr(upres.CertName) - updateDomainMultiCertificatesReqBodyContent = updateDomainMultiCertificatesReqBodyContent.MergeConfig(showDomainFullConfigResp.Configs) - updateDomainMultiCertificatesReq := &hcCdnEx.UpdateDomainMultiCertificatesExRequest{ - Body: &hcCdnEx.UpdateDomainMultiCertificatesExRequestBody{ - Https: updateDomainMultiCertificatesReqBodyContent, - }, - } - updateDomainMultiCertificatesResp, err := d.sdkClient.UploadDomainMultiCertificatesEx(updateDomainMultiCertificatesReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.UploadDomainMultiCertificatesEx'") - } - - d.infos = append(d.infos, toStr("已更新加速域名配置", updateDomainMultiCertificatesResp)) - - return nil -} - -func (d *HuaweiCloudCDNDeployer) createSdkClient(accessKeyId, secretAccessKey, region string) (*hcCdnEx.Client, error) { - if region == "" { - region = "cn-north-1" // CDN 服务默认区域:华北一北京 - } - - auth, err := global.NewCredentialsBuilder(). - WithAk(accessKeyId). - WithSk(secretAccessKey). - SafeBuild() - if err != nil { - return nil, err - } - - hcRegion, err := hcCdnRegion.SafeValueOf(region) - if err != nil { - return nil, err - } - - hcClient, err := hcCdn.CdnClientBuilder(). - WithRegion(hcRegion). - WithCredential(auth). - SafeBuild() - if err != nil { - return nil, err - } - - client := hcCdnEx.NewClient(hcClient) - return client, nil -} diff --git a/internal/deployer/huaweicloud_elb.go b/internal/deployer/huaweicloud_elb.go deleted file mode 100644 index ccc6f9f5..00000000 --- a/internal/deployer/huaweicloud_elb.go +++ /dev/null @@ -1,378 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic" - "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global" - hcElb "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3" - hcElbModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/model" - hcElbRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/region" - hcIam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3" - hcIamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model" - hcIamRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region" - xerrors "github.com/pkg/errors" - "golang.org/x/exp/slices" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderHcElb "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-elb" - "github.com/usual2970/certimate/internal/pkg/utils/cast" -) - -type HuaweiCloudELBDeployer struct { - option *DeployerOption - infos []string - - sdkClient *hcElb.ElbClient - sslUploader uploader.Uploader -} - -func NewHuaweiCloudELBDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.HuaweiCloudAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&HuaweiCloudELBDeployer{}).createSdkClient( - access.AccessKeyId, - access.SecretAccessKey, - option.DeployConfig.GetConfigAsString("region"), - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - uploader, err := uploaderHcElb.New(&uploaderHcElb.HuaweiCloudELBUploaderConfig{ - AccessKeyId: access.AccessKeyId, - SecretAccessKey: access.SecretAccessKey, - Region: option.DeployConfig.GetConfigAsString("region"), - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &HuaweiCloudELBDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *HuaweiCloudELBDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *HuaweiCloudELBDeployer) GetInfos() []string { - return d.infos -} - -func (d *HuaweiCloudELBDeployer) Deploy(ctx context.Context) error { - switch d.option.DeployConfig.GetConfigAsString("resourceType") { - case "certificate": - // 部署到指定证书 - if err := d.deployToCertificate(ctx); err != nil { - return err - } - case "loadbalancer": - // 部署到指定负载均衡器 - if err := d.deployToLoadbalancer(ctx); err != nil { - return err - } - case "listener": - // 部署到指定监听器 - if err := d.deployToListener(ctx); err != nil { - return err - } - default: - return errors.New("unsupported resource type") - } - - return nil -} - -func (d *HuaweiCloudELBDeployer) createSdkClient(accessKeyId, secretAccessKey, region string) (*hcElb.ElbClient, error) { - if region == "" { - region = "cn-north-4" // ELB 服务默认区域:华北四北京 - } - - projectId, err := (&HuaweiCloudELBDeployer{}).getSdkProjectId( - accessKeyId, - secretAccessKey, - region, - ) - if err != nil { - return nil, err - } - - auth, err := basic.NewCredentialsBuilder(). - WithAk(accessKeyId). - WithSk(secretAccessKey). - WithProjectId(projectId). - SafeBuild() - if err != nil { - return nil, err - } - - hcRegion, err := hcElbRegion.SafeValueOf(region) - if err != nil { - return nil, err - } - - hcClient, err := hcElb.ElbClientBuilder(). - WithRegion(hcRegion). - WithCredential(auth). - SafeBuild() - if err != nil { - return nil, err - } - - client := hcElb.NewElbClient(hcClient) - return client, nil -} - -func (u *HuaweiCloudELBDeployer) getSdkProjectId(accessKeyId, secretAccessKey, region string) (string, error) { - if region == "" { - region = "cn-north-4" // IAM 服务默认区域:华北四北京 - } - - auth, err := global.NewCredentialsBuilder(). - WithAk(accessKeyId). - WithSk(secretAccessKey). - SafeBuild() - if err != nil { - return "", err - } - - hcRegion, err := hcIamRegion.SafeValueOf(region) - if err != nil { - return "", err - } - - hcClient, err := hcIam.IamClientBuilder(). - WithRegion(hcRegion). - WithCredential(auth). - SafeBuild() - if err != nil { - return "", err - } - - client := hcIam.NewIamClient(hcClient) - - request := &hcIamModel.KeystoneListProjectsRequest{ - Name: ®ion, - } - response, err := client.KeystoneListProjects(request) - if err != nil { - return "", err - } else if response.Projects == nil || len(*response.Projects) == 0 { - return "", errors.New("no project found") - } - - return (*response.Projects)[0].Id, nil -} - -func (d *HuaweiCloudELBDeployer) deployToCertificate(ctx context.Context) error { - hcCertId := d.option.DeployConfig.GetConfigAsString("certificateId") - if hcCertId == "" { - return errors.New("`certificateId` is required") - } - - // 更新证书 - // REF: https://support.huaweicloud.com/api-elb/UpdateCertificate.html - updateCertificateReq := &hcElbModel.UpdateCertificateRequest{ - CertificateId: hcCertId, - Body: &hcElbModel.UpdateCertificateRequestBody{ - Certificate: &hcElbModel.UpdateCertificateOption{ - Certificate: cast.StringPtr(d.option.Certificate.Certificate), - PrivateKey: cast.StringPtr(d.option.Certificate.PrivateKey), - }, - }, - } - updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.UpdateCertificate'") - } - - d.infos = append(d.infos, toStr("已更新 ELB 证书", updateCertificateResp)) - - return nil -} - -func (d *HuaweiCloudELBDeployer) deployToLoadbalancer(ctx context.Context) error { - hcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId") - if hcLoadbalancerId == "" { - return errors.New("`loadbalancerId` is required") - } - - hcListenerIds := make([]string, 0) - - // 查询负载均衡器详情 - // REF: https://support.huaweicloud.com/api-elb/ShowLoadBalancer.html - showLoadBalancerReq := &hcElbModel.ShowLoadBalancerRequest{ - LoadbalancerId: hcLoadbalancerId, - } - showLoadBalancerResp, err := d.sdkClient.ShowLoadBalancer(showLoadBalancerReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowLoadBalancer'") - } - - d.infos = append(d.infos, toStr("已查询到 ELB 负载均衡器", showLoadBalancerResp)) - - // 查询监听器列表 - // REF: https://support.huaweicloud.com/api-elb/ListListeners.html - listListenersLimit := int32(2000) - var listListenersMarker *string = nil - for { - listListenersReq := &hcElbModel.ListListenersRequest{ - Limit: cast.Int32Ptr(listListenersLimit), - Marker: listListenersMarker, - Protocol: &[]string{"HTTPS", "TERMINATED_HTTPS"}, - LoadbalancerId: &[]string{showLoadBalancerResp.Loadbalancer.Id}, - } - listListenersResp, err := d.sdkClient.ListListeners(listListenersReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ListListeners'") - } - - if listListenersResp.Listeners != nil { - for _, listener := range *listListenersResp.Listeners { - hcListenerIds = append(hcListenerIds, listener.Id) - } - } - - if listListenersResp.Listeners == nil || len(*listListenersResp.Listeners) < int(listListenersLimit) { - break - } else { - listListenersMarker = listListenersResp.PageInfo.NextMarker - } - } - - d.infos = append(d.infos, toStr("已查询到 ELB 负载均衡器下的监听器", hcListenerIds)) - - // 上传证书到 SCM - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 批量更新监听器证书 - var errs []error - for _, hcListenerId := range hcListenerIds { - if err := d.modifyListenerCertificate(ctx, hcListenerId, upres.CertId); err != nil { - errs = append(errs, err) - } - } - if len(errs) > 0 { - return errors.Join(errs...) - } - - return nil -} - -func (d *HuaweiCloudELBDeployer) deployToListener(ctx context.Context) error { - hcListenerId := d.option.DeployConfig.GetConfigAsString("listenerId") - if hcListenerId == "" { - return errors.New("`listenerId` is required") - } - - // 上传证书到 SCM - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 更新监听器证书 - if err := d.modifyListenerCertificate(ctx, hcListenerId, upres.CertId); err != nil { - return err - } - - return nil -} - -func (d *HuaweiCloudELBDeployer) modifyListenerCertificate(ctx context.Context, hcListenerId string, hcCertId string) error { - // 查询监听器详情 - // REF: https://support.huaweicloud.com/api-elb/ShowListener.html - showListenerReq := &hcElbModel.ShowListenerRequest{ - ListenerId: hcListenerId, - } - showListenerResp, err := d.sdkClient.ShowListener(showListenerReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowListener'") - } - - d.infos = append(d.infos, toStr("已查询到 ELB 监听器", showListenerResp)) - - // 更新监听器 - // REF: https://support.huaweicloud.com/api-elb/UpdateListener.html - updateListenerReq := &hcElbModel.UpdateListenerRequest{ - ListenerId: hcListenerId, - Body: &hcElbModel.UpdateListenerRequestBody{ - Listener: &hcElbModel.UpdateListenerOption{ - DefaultTlsContainerRef: cast.StringPtr(hcCertId), - }, - }, - } - if showListenerResp.Listener.SniContainerRefs != nil { - if len(showListenerResp.Listener.SniContainerRefs) > 0 { - // 如果开启 SNI,需替换同 SAN 的证书 - sniCertIds := make([]string, 0) - sniCertIds = append(sniCertIds, hcCertId) - - listOldCertificateReq := &hcElbModel.ListCertificatesRequest{ - Id: &showListenerResp.Listener.SniContainerRefs, - } - listOldCertificateResp, err := d.sdkClient.ListCertificates(listOldCertificateReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ListCertificates'") - } - - showNewCertificateReq := &hcElbModel.ShowCertificateRequest{ - CertificateId: hcCertId, - } - showNewCertificateResp, err := d.sdkClient.ShowCertificate(showNewCertificateReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowCertificate'") - } - - for _, certificate := range *listOldCertificateResp.Certificates { - oldCertificate := certificate - newCertificate := showNewCertificateResp.Certificate - - if oldCertificate.SubjectAlternativeNames != nil && newCertificate.SubjectAlternativeNames != nil { - if slices.Equal(*oldCertificate.SubjectAlternativeNames, *newCertificate.SubjectAlternativeNames) { - continue - } - } else { - if oldCertificate.Domain == newCertificate.Domain { - continue - } - } - - sniCertIds = append(sniCertIds, certificate.Id) - } - - updateListenerReq.Body.Listener.SniContainerRefs = &sniCertIds - } - - if showListenerResp.Listener.SniMatchAlgo != "" { - updateListenerReq.Body.Listener.SniMatchAlgo = cast.StringPtr(showListenerResp.Listener.SniMatchAlgo) - } - } - updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.UpdateListener'") - } - - d.infos = append(d.infos, toStr("已更新 ELB 监听器", updateListenerResp)) - - return nil -} diff --git a/internal/deployer/k8s_secret.go b/internal/deployer/k8s_secret.go deleted file mode 100644 index 8a1c30ff..00000000 --- a/internal/deployer/k8s_secret.go +++ /dev/null @@ -1,136 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - xerrors "github.com/pkg/errors" - k8sCore "k8s.io/api/core/v1" - k8sMeta "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/utils/x509" -) - -type K8sSecretDeployer struct { - option *DeployerOption - infos []string - - k8sClient *kubernetes.Clientset -} - -func NewK8sSecretDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.KubernetesAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&K8sSecretDeployer{}).createK8sClient(access) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create k8s client") - } - - return &K8sSecretDeployer{ - option: option, - infos: make([]string, 0), - k8sClient: client, - }, nil -} - -func (d *K8sSecretDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *K8sSecretDeployer) GetInfos() []string { - return d.infos -} - -func (d *K8sSecretDeployer) Deploy(ctx context.Context) error { - namespace := d.option.DeployConfig.GetConfigAsString("namespace") - secretName := d.option.DeployConfig.GetConfigAsString("secretName") - secretDataKeyForCrt := d.option.DeployConfig.GetConfigOrDefaultAsString("secretDataKeyForCrt", "tls.crt") - secretDataKeyForKey := d.option.DeployConfig.GetConfigOrDefaultAsString("secretDataKeyForKey", "tls.key") - if namespace == "" { - namespace = "default" - } - if secretName == "" { - return errors.New("`secretName` is required") - } - - certX509, err := x509.ParseCertificateFromPEM(d.option.Certificate.Certificate) - if err != nil { - return err - } - - secretPayload := k8sCore.Secret{ - TypeMeta: k8sMeta.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: k8sMeta.ObjectMeta{ - Name: secretName, - Annotations: map[string]string{ - "certimate/domains": d.option.Domain, - "certimate/alt-names": strings.Join(certX509.DNSNames, ","), - "certimate/common-name": certX509.Subject.CommonName, - "certimate/issuer-organization": strings.Join(certX509.Issuer.Organization, ","), - }, - }, - Type: k8sCore.SecretType("kubernetes.io/tls"), - } - secretPayload.Data = make(map[string][]byte) - secretPayload.Data[secretDataKeyForCrt] = []byte(d.option.Certificate.Certificate) - secretPayload.Data[secretDataKeyForKey] = []byte(d.option.Certificate.PrivateKey) - - // 获取 Secret 实例 - _, err = d.k8sClient.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, k8sMeta.GetOptions{}) - if err != nil { - _, err = d.k8sClient.CoreV1().Secrets(namespace).Create(context.TODO(), &secretPayload, k8sMeta.CreateOptions{}) - if err != nil { - return xerrors.Wrap(err, "failed to create k8s secret") - } else { - d.infos = append(d.infos, toStr("Certificate has been created in K8s Secret", nil)) - return nil - } - } - - // 更新 Secret 实例 - _, err = d.k8sClient.CoreV1().Secrets(namespace).Update(context.TODO(), &secretPayload, k8sMeta.UpdateOptions{}) - if err != nil { - return xerrors.Wrap(err, "failed to update k8s secret") - } - - d.infos = append(d.infos, toStr("Certificate has been updated to K8s Secret", nil)) - - return nil -} - -func (d *K8sSecretDeployer) createK8sClient(access *domain.KubernetesAccess) (*kubernetes.Clientset, error) { - var config *rest.Config - var err error - if access.KubeConfig == "" { - config, err = rest.InClusterConfig() - } else { - kubeConfig, err := clientcmd.NewClientConfigFromBytes([]byte(access.KubeConfig)) - if err != nil { - return nil, err - } - config, err = kubeConfig.ClientConfig() - } - if err != nil { - return nil, err - } - - client, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - - return client, nil -} diff --git a/internal/deployer/local.go b/internal/deployer/local.go deleted file mode 100644 index 3c693d30..00000000 --- a/internal/deployer/local.go +++ /dev/null @@ -1,163 +0,0 @@ -package deployer - -import ( - "bytes" - "context" - "errors" - "fmt" - "os/exec" - "runtime" - - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/pkg/utils/fs" - "github.com/usual2970/certimate/internal/pkg/utils/x509" -) - -type LocalDeployer struct { - option *DeployerOption - infos []string -} - -const ( - certFormatPEM = "pem" - certFormatPFX = "pfx" - certFormatJKS = "jks" -) - -const ( - shellEnvSh = "sh" - shellEnvCmd = "cmd" - shellEnvPowershell = "powershell" -) - -func NewLocalDeployer(option *DeployerOption) (Deployer, error) { - return &LocalDeployer{ - option: option, - infos: make([]string, 0), - }, nil -} - -func (d *LocalDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *LocalDeployer) GetInfos() []string { - return []string{} -} - -func (d *LocalDeployer) Deploy(ctx context.Context) error { - // 执行前置命令 - preCommand := d.option.DeployConfig.GetConfigAsString("preCommand") - if preCommand != "" { - stdout, stderr, err := d.execCommand(preCommand) - if err != nil { - return xerrors.Wrapf(err, "failed to run pre-command, stdout: %s, stderr: %s", stdout, stderr) - } - - d.infos = append(d.infos, toStr("执行前置命令成功", stdout)) - } - - // 写入证书和私钥文件 - switch d.option.DeployConfig.GetConfigOrDefaultAsString("format", certFormatPEM) { - case certFormatPEM: - if err := fs.WriteFileString(d.option.DeployConfig.GetConfigAsString("certPath"), d.option.Certificate.Certificate); err != nil { - return err - } - - d.infos = append(d.infos, toStr("保存证书成功", nil)) - - if err := fs.WriteFileString(d.option.DeployConfig.GetConfigAsString("keyPath"), d.option.Certificate.PrivateKey); err != nil { - return err - } - - d.infos = append(d.infos, toStr("保存私钥成功", nil)) - - case certFormatPFX: - pfxData, err := x509.TransformCertificateFromPEMToPFX( - d.option.Certificate.Certificate, - d.option.Certificate.PrivateKey, - d.option.DeployConfig.GetConfigAsString("pfxPassword"), - ) - if err != nil { - return err - } - - if err := fs.WriteFile(d.option.DeployConfig.GetConfigAsString("certPath"), pfxData); err != nil { - return err - } - - d.infos = append(d.infos, toStr("保存证书成功", nil)) - - case certFormatJKS: - jksData, err := x509.TransformCertificateFromPEMToJKS( - d.option.Certificate.Certificate, - d.option.Certificate.PrivateKey, - d.option.DeployConfig.GetConfigAsString("jksAlias"), - d.option.DeployConfig.GetConfigAsString("jksKeypass"), - d.option.DeployConfig.GetConfigAsString("jksStorepass"), - ) - if err != nil { - return err - } - - if err := fs.WriteFile(d.option.DeployConfig.GetConfigAsString("certPath"), jksData); err != nil { - return err - } - - d.infos = append(d.infos, toStr("保存证书成功", nil)) - - default: - return errors.New("unsupported format") - } - - // 执行命令 - command := d.option.DeployConfig.GetConfigAsString("command") - if command != "" { - stdout, stderr, err := d.execCommand(command) - if err != nil { - return xerrors.Wrapf(err, "failed to run command, stdout: %s, stderr: %s", stdout, stderr) - } - - d.infos = append(d.infos, toStr("执行命令成功", stdout)) - } - - return nil -} - -func (d *LocalDeployer) execCommand(command string) (string, string, error) { - var cmd *exec.Cmd - - switch d.option.DeployConfig.GetConfigAsString("shell") { - case shellEnvSh: - cmd = exec.Command("sh", "-c", command) - - case shellEnvCmd: - cmd = exec.Command("cmd", "/C", command) - - case shellEnvPowershell: - cmd = exec.Command("powershell", "-Command", command) - - case "": - if runtime.GOOS == "windows" { - cmd = exec.Command("cmd", "/C", command) - } else { - cmd = exec.Command("sh", "-c", command) - } - - default: - return "", "", errors.New("unsupported shell") - } - - var stdoutBuf bytes.Buffer - cmd.Stdout = &stdoutBuf - var stderrBuf bytes.Buffer - cmd.Stderr = &stderrBuf - - err := cmd.Run() - if err != nil { - return "", "", xerrors.Wrap(err, "failed to execute shell script") - } - - return stdoutBuf.String(), stderrBuf.String(), nil -} diff --git a/internal/deployer/qiniu_cdn.go b/internal/deployer/qiniu_cdn.go deleted file mode 100644 index bc638af9..00000000 --- a/internal/deployer/qiniu_cdn.go +++ /dev/null @@ -1,113 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - xerrors "github.com/pkg/errors" - "github.com/qiniu/go-sdk/v7/auth" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderQiniu "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/qiniu-sslcert" - qiniuEx "github.com/usual2970/certimate/internal/pkg/vendors/qiniu-sdk" -) - -type QiniuCDNDeployer struct { - option *DeployerOption - infos []string - - sdkClient *qiniuEx.Client - sslUploader uploader.Uploader -} - -func NewQiniuCDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.QiniuAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&QiniuCDNDeployer{}).createSdkClient( - access.AccessKey, - access.SecretKey, - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") - } - - uploader, err := uploaderQiniu.New(&uploaderQiniu.QiniuSSLCertUploaderConfig{ - AccessKey: access.AccessKey, - SecretKey: access.SecretKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &QiniuCDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *QiniuCDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *QiniuCDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *QiniuCDNDeployer) Deploy(ctx context.Context) error { - // 上传证书 - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 在七牛 CDN 中泛域名表示为 .example.com,需去除前缀星号 - domain := d.option.DeployConfig.GetConfigAsString("domain") - if strings.HasPrefix(domain, "*") { - domain = strings.TrimPrefix(domain, "*") - } - - // 获取域名信息 - // REF: https://developer.qiniu.com/fusion/4246/the-domain-name - getDomainInfoResp, err := d.sdkClient.GetDomainInfo(domain) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.GetDomainInfo'") - } - - d.infos = append(d.infos, toStr("已获取域名信息", getDomainInfoResp)) - - // 判断域名是否已启用 HTTPS。如果已启用,修改域名证书;否则,启用 HTTPS - // REF: https://developer.qiniu.com/fusion/4246/the-domain-name - if getDomainInfoResp.Https != nil && getDomainInfoResp.Https.CertID != "" { - modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(domain, upres.CertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.ModifyDomainHttpsConf'") - } - - d.infos = append(d.infos, toStr("已修改域名证书", modifyDomainHttpsConfResp)) - } else { - enableDomainHttpsResp, err := d.sdkClient.EnableDomainHttps(domain, upres.CertId, true, true) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.EnableDomainHttps'") - } - - d.infos = append(d.infos, toStr("已将域名升级为 HTTPS", enableDomainHttpsResp)) - } - - return nil -} - -func (u *QiniuCDNDeployer) createSdkClient(accessKey, secretKey string) (*qiniuEx.Client, error) { - credential := auth.New(accessKey, secretKey) - client := qiniuEx.NewClient(credential) - return client, nil -} diff --git a/internal/deployer/ssh.go b/internal/deployer/ssh.go deleted file mode 100644 index 96f8bdd2..00000000 --- a/internal/deployer/ssh.go +++ /dev/null @@ -1,209 +0,0 @@ -package deployer - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - - xerrors "github.com/pkg/errors" - "github.com/pkg/sftp" - "golang.org/x/crypto/ssh" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/utils/x509" -) - -type SSHDeployer struct { - option *DeployerOption - infos []string -} - -func NewSSHDeployer(option *DeployerOption) (Deployer, error) { - return &SSHDeployer{ - option: option, - infos: make([]string, 0), - }, nil -} - -func (d *SSHDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *SSHDeployer) GetInfos() []string { - return d.infos -} - -func (d *SSHDeployer) Deploy(ctx context.Context) error { - access := &domain.SSHAccess{} - if err := json.Unmarshal([]byte(d.option.Access), access); err != nil { - return err - } - - // 连接 - client, err := d.createSshClient(access) - if err != nil { - return err - } - defer client.Close() - - d.infos = append(d.infos, toStr("SSH 连接成功", nil)) - - // 执行前置命令 - preCommand := d.option.DeployConfig.GetConfigAsString("preCommand") - if preCommand != "" { - stdout, stderr, err := d.sshExecCommand(client, preCommand) - if err != nil { - return xerrors.Wrapf(err, "failed to run pre-command: stdout: %s, stderr: %s", stdout, stderr) - } - - d.infos = append(d.infos, toStr("SSH 执行前置命令成功", stdout)) - } - - // 上传证书和私钥文件 - switch d.option.DeployConfig.GetConfigOrDefaultAsString("format", certFormatPEM) { - case certFormatPEM: - if err := d.writeSftpFileString(client, d.option.DeployConfig.GetConfigAsString("certPath"), d.option.Certificate.Certificate); err != nil { - return err - } - - d.infos = append(d.infos, toStr("SSH 上传证书成功", nil)) - - if err := d.writeSftpFileString(client, d.option.DeployConfig.GetConfigAsString("keyPath"), d.option.Certificate.PrivateKey); err != nil { - return err - } - - d.infos = append(d.infos, toStr("SSH 上传私钥成功", nil)) - - case certFormatPFX: - pfxData, err := x509.TransformCertificateFromPEMToPFX( - d.option.Certificate.Certificate, - d.option.Certificate.PrivateKey, - d.option.DeployConfig.GetConfigAsString("pfxPassword"), - ) - if err != nil { - return err - } - - if err := d.writeSftpFile(client, d.option.DeployConfig.GetConfigAsString("certPath"), pfxData); err != nil { - return err - } - - d.infos = append(d.infos, toStr("SSH 上传证书成功", nil)) - - case certFormatJKS: - jksData, err := x509.TransformCertificateFromPEMToJKS( - d.option.Certificate.Certificate, - d.option.Certificate.PrivateKey, - d.option.DeployConfig.GetConfigAsString("jksAlias"), - d.option.DeployConfig.GetConfigAsString("jksKeypass"), - d.option.DeployConfig.GetConfigAsString("jksStorepass"), - ) - if err != nil { - return err - } - - if err := d.writeSftpFile(client, d.option.DeployConfig.GetConfigAsString("certPath"), jksData); err != nil { - return err - } - - d.infos = append(d.infos, toStr("SSH 上传证书成功", nil)) - - default: - return errors.New("unsupported format") - } - - // 执行命令 - command := d.option.DeployConfig.GetConfigAsString("command") - if command != "" { - stdout, stderr, err := d.sshExecCommand(client, command) - if err != nil { - return xerrors.Wrapf(err, "failed to run command, stdout: %s, stderr: %s", stdout, stderr) - } - - d.infos = append(d.infos, toStr("SSH 执行命令成功", stdout)) - } - - return nil -} - -func (d *SSHDeployer) createSshClient(access *domain.SSHAccess) (*ssh.Client, error) { - var authMethod ssh.AuthMethod - - if access.Key != "" { - var signer ssh.Signer - var err error - - if access.KeyPassphrase != "" { - signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(access.Key), []byte(access.KeyPassphrase)) - } else { - signer, err = ssh.ParsePrivateKey([]byte(access.Key)) - } - - if err != nil { - return nil, err - } - authMethod = ssh.PublicKeys(signer) - } else { - authMethod = ssh.Password(access.Password) - } - - return ssh.Dial("tcp", fmt.Sprintf("%s:%s", access.Host, access.Port), &ssh.ClientConfig{ - User: access.Username, - Auth: []ssh.AuthMethod{ - authMethod, - }, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - }) -} - -func (d *SSHDeployer) sshExecCommand(sshCli *ssh.Client, command string) (string, string, error) { - session, err := sshCli.NewSession() - if err != nil { - return "", "", xerrors.Wrap(err, "failed to create ssh session") - } - - defer session.Close() - var stdoutBuf bytes.Buffer - session.Stdout = &stdoutBuf - var stderrBuf bytes.Buffer - session.Stderr = &stderrBuf - err = session.Run(command) - if err != nil { - return "", "", xerrors.Wrap(err, "failed to execute ssh script") - } - - return stdoutBuf.String(), stderrBuf.String(), nil -} - -func (d *SSHDeployer) writeSftpFileString(sshCli *ssh.Client, path string, content string) error { - return d.writeSftpFile(sshCli, path, []byte(content)) -} - -func (d *SSHDeployer) writeSftpFile(sshCli *ssh.Client, path string, data []byte) error { - sftpCli, err := sftp.NewClient(sshCli) - if err != nil { - return xerrors.Wrap(err, "failed to create sftp client") - } - defer sftpCli.Close() - - if err := sftpCli.MkdirAll(filepath.Dir(path)); err != nil { - return xerrors.Wrap(err, "failed to create remote directory") - } - - file, err := sftpCli.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC) - if err != nil { - return xerrors.Wrap(err, "failed to open remote file") - } - defer file.Close() - - _, err = file.Write(data) - if err != nil { - return xerrors.Wrap(err, "failed to write to remote file") - } - - return nil -} diff --git a/internal/deployer/ssh_test.go b/internal/deployer/ssh_test.go deleted file mode 100644 index c9b1e85a..00000000 --- a/internal/deployer/ssh_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package deployer - -import ( - "os" - "path" - "testing" -) - -func TestPath(t *testing.T) { - dir := path.Dir("./a/b/c") - os.MkdirAll(dir, 0o755) -} diff --git a/internal/deployer/tencent_cdn.go b/internal/deployer/tencent_cdn.go deleted file mode 100644 index f5230910..00000000 --- a/internal/deployer/tencent_cdn.go +++ /dev/null @@ -1,194 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - xerrors "github.com/pkg/errors" - tcCdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" - tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" - "golang.org/x/exp/slices" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" -) - -type TencentCDNDeployer struct { - option *DeployerOption - infos []string - - sdkClients *tencentCDNDeployerSdkClients - sslUploader uploader.Uploader -} - -type tencentCDNDeployerSdkClients struct { - ssl *tcSsl.Client - cdn *tcCdn.Client -} - -func NewTencentCDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.TencentAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - clients, err := (&TencentCDNDeployer{}).createSdkClients( - access.SecretId, - access.SecretKey, - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") - } - - uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{ - SecretId: access.SecretId, - SecretKey: access.SecretKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &TencentCDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClients: clients, - sslUploader: uploader, - }, nil -} - -func (d *TencentCDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *TencentCDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *TencentCDNDeployer) Deploy(ctx context.Context) error { - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 获取待部署的 CDN 实例 - // 如果是泛域名,根据证书匹配 CDN 实例 - tcInstanceIds := make([]string, 0) - domain := d.option.DeployConfig.GetConfigAsString("domain") - if strings.HasPrefix(domain, "*") { - domains, err := d.getDomainsByCertificateId(upres.CertId) - if err != nil { - return err - } - - tcInstanceIds = domains - } else { - tcInstanceIds = append(tcInstanceIds, domain) - } - - // 跳过已部署的 CDN 实例 - if len(tcInstanceIds) > 0 { - deployedDomains, err := d.getDeployedDomainsByCertificateId(upres.CertId) - if err != nil { - return err - } - - temp := make([]string, 0) - for _, tcInstanceId := range tcInstanceIds { - if !slices.Contains(deployedDomains, tcInstanceId) { - temp = append(temp, tcInstanceId) - } - } - tcInstanceIds = temp - } - if len(tcInstanceIds) == 0 { - d.infos = append(d.infos, "已部署过或没有要部署的 CDN 实例") - return nil - } - - // 证书部署到 CDN 实例 - // REF: https://cloud.tencent.com/document/product/400/91667 - deployCertificateInstanceReq := tcSsl.NewDeployCertificateInstanceRequest() - deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId) - deployCertificateInstanceReq.ResourceType = common.StringPtr("cdn") - deployCertificateInstanceReq.Status = common.Int64Ptr(1) - deployCertificateInstanceReq.InstanceIdList = common.StringPtrs(tcInstanceIds) - deployCertificateInstanceResp, err := d.sdkClients.ssl.DeployCertificateInstance(deployCertificateInstanceReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'") - } - - d.infos = append(d.infos, toStr("已部署证书到云资源实例", deployCertificateInstanceResp.Response)) - - return nil -} - -func (d *TencentCDNDeployer) createSdkClients(secretId, secretKey string) (*tencentCDNDeployerSdkClients, error) { - credential := common.NewCredential(secretId, secretKey) - - sslClient, err := tcSsl.NewClient(credential, "", profile.NewClientProfile()) - if err != nil { - return nil, err - } - - cdnClient, err := tcCdn.NewClient(credential, "", profile.NewClientProfile()) - if err != nil { - return nil, err - } - - return &tencentCDNDeployerSdkClients{ - ssl: sslClient, - cdn: cdnClient, - }, nil -} - -func (d *TencentCDNDeployer) getDomainsByCertificateId(tcCertId string) ([]string, error) { - // 获取证书中的可用域名 - // REF: https://cloud.tencent.com/document/product/228/42491 - describeCertDomainsReq := tcCdn.NewDescribeCertDomainsRequest() - describeCertDomainsReq.CertId = common.StringPtr(tcCertId) - describeCertDomainsReq.Product = common.StringPtr("cdn") - describeCertDomainsResp, err := d.sdkClients.cdn.DescribeCertDomains(describeCertDomainsReq) - if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertDomains'") - } - - domains := make([]string, 0) - if describeCertDomainsResp.Response.Domains == nil { - for _, domain := range describeCertDomainsResp.Response.Domains { - domains = append(domains, *domain) - } - } - - return domains, nil -} - -func (d *TencentCDNDeployer) getDeployedDomainsByCertificateId(tcCertId string) ([]string, error) { - // 根据证书查询关联 CDN 域名 - // REF: https://cloud.tencent.com/document/product/400/62674 - describeDeployedResourcesReq := tcSsl.NewDescribeDeployedResourcesRequest() - describeDeployedResourcesReq.CertificateIds = common.StringPtrs([]string{tcCertId}) - describeDeployedResourcesReq.ResourceType = common.StringPtr("cdn") - describeDeployedResourcesResp, err := d.sdkClients.ssl.DescribeDeployedResources(describeDeployedResourcesReq) - if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeDeployedResources'") - } - - domains := make([]string, 0) - if describeDeployedResourcesResp.Response.DeployedResources != nil { - for _, deployedResource := range describeDeployedResourcesResp.Response.DeployedResources { - for _, resource := range deployedResource.Resources { - domains = append(domains, *resource) - } - } - } - - return domains, nil -} diff --git a/internal/deployer/tencent_clb.go b/internal/deployer/tencent_clb.go deleted file mode 100644 index c3feefc7..00000000 --- a/internal/deployer/tencent_clb.go +++ /dev/null @@ -1,328 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - xerrors "github.com/pkg/errors" - tcClb "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb/v20180317" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" - tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" -) - -type TencentCLBDeployer struct { - option *DeployerOption - infos []string - - sdkClients *tencentCLBDeployerSdkClients - sslUploader uploader.Uploader -} - -type tencentCLBDeployerSdkClients struct { - ssl *tcSsl.Client - clb *tcClb.Client -} - -func NewTencentCLBDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.TencentAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - clients, err := (&TencentCLBDeployer{}).createSdkClients( - access.SecretId, - access.SecretKey, - option.DeployConfig.GetConfigAsString("region"), - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") - } - - uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{ - SecretId: access.SecretId, - SecretKey: access.SecretKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &TencentCLBDeployer{ - option: option, - infos: make([]string, 0), - sdkClients: clients, - sslUploader: uploader, - }, nil -} - -func (d *TencentCLBDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *TencentCLBDeployer) GetInfos() []string { - return d.infos -} - -func (d *TencentCLBDeployer) Deploy(ctx context.Context) error { - switch d.option.DeployConfig.GetConfigAsString("resourceType") { - case "ssl-deploy": - // 通过 SSL 服务部署到云资源实例 - err := d.deployToInstanceUseSsl(ctx) - if err != nil { - return err - } - case "loadbalancer": - // 部署到指定负载均衡器 - if err := d.deployToLoadbalancer(ctx); err != nil { - return err - } - case "listener": - // 部署到指定监听器 - if err := d.deployToListener(ctx); err != nil { - return err - } - case "ruledomain": - // 部署到指定七层监听转发规则域名 - if err := d.deployToRuleDomain(ctx); err != nil { - return err - } - default: - return errors.New("unsupported resource type") - } - - return nil -} - -func (d *TencentCLBDeployer) createSdkClients(secretId, secretKey, region string) (*tencentCLBDeployerSdkClients, error) { - credential := common.NewCredential(secretId, secretKey) - - sslClient, err := tcSsl.NewClient(credential, region, profile.NewClientProfile()) - if err != nil { - return nil, err - } - - clbClient, err := tcClb.NewClient(credential, region, profile.NewClientProfile()) - if err != nil { - return nil, err - } - - return &tencentCLBDeployerSdkClients{ - ssl: sslClient, - clb: clbClient, - }, nil -} - -func (d *TencentCLBDeployer) deployToInstanceUseSsl(ctx context.Context) error { - tcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId") - tcListenerId := d.option.DeployConfig.GetConfigAsString("listenerId") - tcDomain := d.option.DeployConfig.GetConfigAsString("domain") - if tcLoadbalancerId == "" { - return errors.New("`loadbalancerId` is required") - } - if tcListenerId == "" { - return errors.New("`listenerId` is required") - } - - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 证书部署到 CLB 实例 - // REF: https://cloud.tencent.com/document/product/400/91667 - deployCertificateInstanceReq := tcSsl.NewDeployCertificateInstanceRequest() - deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId) - deployCertificateInstanceReq.ResourceType = common.StringPtr("clb") - deployCertificateInstanceReq.Status = common.Int64Ptr(1) - if tcDomain == "" { - // 未开启 SNI,只需指定到监听器 - deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s", tcLoadbalancerId, tcListenerId)}) - } else { - // 开启 SNI,需指定到域名(支持泛域名) - deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s|%s", tcLoadbalancerId, tcListenerId, tcDomain)}) - } - deployCertificateInstanceResp, err := d.sdkClients.ssl.DeployCertificateInstance(deployCertificateInstanceReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'") - } - - d.infos = append(d.infos, toStr("已部署证书到云资源实例", deployCertificateInstanceResp.Response)) - - return nil -} - -func (d *TencentCLBDeployer) deployToLoadbalancer(ctx context.Context) error { - tcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId") - tcListenerIds := make([]string, 0) - if tcLoadbalancerId == "" { - return errors.New("`loadbalancerId` is required") - } - - // 查询负载均衡器详细信息 - // REF: https://cloud.tencent.com/document/api/214/46916 - describeLoadBalancersDetailReq := tcClb.NewDescribeLoadBalancersDetailRequest() - describeLoadBalancersDetailResp, err := d.sdkClients.clb.DescribeLoadBalancersDetail(describeLoadBalancersDetailReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeLoadBalancersDetail'") - } - - d.infos = append(d.infos, toStr("已查询到负载均衡详细信息", describeLoadBalancersDetailResp)) - - // 查询监听器列表 - // REF: https://cloud.tencent.com/document/api/214/30686 - describeListenersReq := tcClb.NewDescribeListenersRequest() - describeListenersReq.LoadBalancerId = common.StringPtr(tcLoadbalancerId) - describeListenersResp, err := d.sdkClients.clb.DescribeListeners(describeListenersReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeListeners'") - } else { - if describeListenersResp.Response.Listeners != nil { - for _, listener := range describeListenersResp.Response.Listeners { - if listener.Protocol == nil || (*listener.Protocol != "HTTPS" && *listener.Protocol != "TCP_SSL" && *listener.Protocol != "QUIC") { - continue - } - - tcListenerIds = append(tcListenerIds, *listener.ListenerId) - } - } - } - - d.infos = append(d.infos, toStr("已查询到负载均衡器下的监听器", tcListenerIds)) - - // 上传证书到 SCM - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 批量更新监听器证书 - var errs []error - for _, tcListenerId := range tcListenerIds { - if err := d.modifyListenerCertificate(ctx, tcLoadbalancerId, tcListenerId, upres.CertId); err != nil { - errs = append(errs, err) - } - } - if len(errs) > 0 { - return errors.Join(errs...) - } - - return nil -} - -func (d *TencentCLBDeployer) deployToListener(ctx context.Context) error { - tcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId") - tcListenerId := d.option.DeployConfig.GetConfigAsString("listenerId") - if tcLoadbalancerId == "" { - return errors.New("`loadbalancerId` is required") - } - if tcListenerId == "" { - return errors.New("`listenerId` is required") - } - - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 更新监听器证书 - if err := d.modifyListenerCertificate(ctx, tcLoadbalancerId, tcListenerId, upres.CertId); err != nil { - return err - } - - return nil -} - -func (d *TencentCLBDeployer) deployToRuleDomain(ctx context.Context) error { - tcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId") - tcListenerId := d.option.DeployConfig.GetConfigAsString("listenerId") - tcDomain := d.option.DeployConfig.GetConfigAsString("domain") - if tcLoadbalancerId == "" { - return errors.New("`loadbalancerId` is required") - } - if tcListenerId == "" { - return errors.New("`listenerId` is required") - } - if tcDomain == "" { - return errors.New("`domain` is required") - } - - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 修改负载均衡七层监听器转发规则的域名级别属性 - // REF: https://cloud.tencent.com/document/api/214/38092 - modifyDomainAttributesReq := tcClb.NewModifyDomainAttributesRequest() - modifyDomainAttributesReq.LoadBalancerId = common.StringPtr(tcLoadbalancerId) - modifyDomainAttributesReq.ListenerId = common.StringPtr(tcListenerId) - modifyDomainAttributesReq.Domain = common.StringPtr(tcDomain) - modifyDomainAttributesReq.Certificate = &tcClb.CertificateInput{ - SSLMode: common.StringPtr("UNIDIRECTIONAL"), - CertId: common.StringPtr(upres.CertId), - } - modifyDomainAttributesResp, err := d.sdkClients.clb.ModifyDomainAttributes(modifyDomainAttributesReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.ModifyDomainAttributes'") - } - - d.infos = append(d.infos, toStr("已修改七层监听器转发规则的域名级别属性", modifyDomainAttributesResp.Response)) - - return nil -} - -func (d *TencentCLBDeployer) modifyListenerCertificate(ctx context.Context, tcLoadbalancerId, tcListenerId, tcCertId string) error { - // 查询监听器列表 - // REF: https://cloud.tencent.com/document/api/214/30686 - describeListenersReq := tcClb.NewDescribeListenersRequest() - describeListenersReq.LoadBalancerId = common.StringPtr(tcLoadbalancerId) - describeListenersReq.ListenerIds = common.StringPtrs([]string{tcListenerId}) - describeListenersResp, err := d.sdkClients.clb.DescribeListeners(describeListenersReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeListeners'") - } - if len(describeListenersResp.Response.Listeners) == 0 { - d.infos = append(d.infos, toStr("未找到监听器", nil)) - return errors.New("listener not found") - } - - d.infos = append(d.infos, toStr("已查询到监听器属性", describeListenersResp.Response)) - - // 修改监听器属性 - // REF: https://cloud.tencent.com/document/product/214/30681 - modifyListenerReq := tcClb.NewModifyListenerRequest() - modifyListenerReq.LoadBalancerId = common.StringPtr(tcLoadbalancerId) - modifyListenerReq.ListenerId = common.StringPtr(tcListenerId) - modifyListenerReq.Certificate = &tcClb.CertificateInput{CertId: common.StringPtr(tcCertId)} - if describeListenersResp.Response.Listeners[0].Certificate != nil && describeListenersResp.Response.Listeners[0].Certificate.SSLMode != nil { - modifyListenerReq.Certificate.SSLMode = describeListenersResp.Response.Listeners[0].Certificate.SSLMode - modifyListenerReq.Certificate.CertCaId = describeListenersResp.Response.Listeners[0].Certificate.CertCaId - } else { - modifyListenerReq.Certificate.SSLMode = common.StringPtr("UNIDIRECTIONAL") - } - modifyListenerResp, err := d.sdkClients.clb.ModifyListener(modifyListenerReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.ModifyListener'") - } - - d.infos = append(d.infos, toStr("已修改监听器属性", modifyListenerResp.Response)) - - return nil -} diff --git a/internal/deployer/tencent_cos.go b/internal/deployer/tencent_cos.go deleted file mode 100644 index bfcd7b7d..00000000 --- a/internal/deployer/tencent_cos.go +++ /dev/null @@ -1,107 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - xerrors "github.com/pkg/errors" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" - tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" -) - -type TencentCOSDeployer struct { - option *DeployerOption - infos []string - - sdkClient *tcSsl.Client - sslUploader uploader.Uploader -} - -func NewTencentCOSDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.TencentAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - client, err := (&TencentCOSDeployer{}).createSdkClient( - access.SecretId, - access.SecretKey, - option.DeployConfig.GetConfigAsString("region"), - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") - } - - uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{ - SecretId: access.SecretId, - SecretKey: access.SecretKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &TencentCOSDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *TencentCOSDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *TencentCOSDeployer) GetInfos() []string { - return d.infos -} - -func (d *TencentCOSDeployer) Deploy(ctx context.Context) error { - tcRegion := d.option.DeployConfig.GetConfigAsString("region") - tcBucket := d.option.DeployConfig.GetConfigAsString("bucket") - tcDomain := d.option.DeployConfig.GetConfigAsString("domain") - if tcBucket == "" { - return errors.New("`bucket` is required") - } - - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 证书部署到 COS 实例 - // REF: https://cloud.tencent.com/document/product/400/91667 - deployCertificateInstanceReq := tcSsl.NewDeployCertificateInstanceRequest() - deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId) - deployCertificateInstanceReq.ResourceType = common.StringPtr("cos") - deployCertificateInstanceReq.Status = common.Int64Ptr(1) - deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s#%s#%s", tcRegion, tcBucket, tcDomain)}) - deployCertificateInstanceResp, err := d.sdkClient.DeployCertificateInstance(deployCertificateInstanceReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'") - } - - d.infos = append(d.infos, toStr("已部署证书到云资源实例", deployCertificateInstanceResp.Response)) - - return nil -} - -func (d *TencentCOSDeployer) createSdkClient(secretId, secretKey, region string) (*tcSsl.Client, error) { - credential := common.NewCredential(secretId, secretKey) - client, err := tcSsl.NewClient(credential, region, profile.NewClientProfile()) - if err != nil { - return nil, err - } - - return client, nil -} diff --git a/internal/deployer/tencent_ecdn.go b/internal/deployer/tencent_ecdn.go deleted file mode 100644 index a61c5680..00000000 --- a/internal/deployer/tencent_ecdn.go +++ /dev/null @@ -1,154 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - xerrors "github.com/pkg/errors" - tcCdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" - tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" -) - -type TencentECDNDeployer struct { - option *DeployerOption - infos []string - - sdkClients *tencentECDNDeployerSdkClients - sslUploader uploader.Uploader -} - -type tencentECDNDeployerSdkClients struct { - ssl *tcSsl.Client - cdn *tcCdn.Client -} - -func NewTencentECDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.TencentAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - clients, err := (&TencentECDNDeployer{}).createSdkClients( - access.SecretId, - access.SecretKey, - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") - } - - uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{ - SecretId: access.SecretId, - SecretKey: access.SecretKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &TencentECDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClients: clients, - sslUploader: uploader, - }, nil -} - -func (d *TencentECDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *TencentECDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *TencentECDNDeployer) Deploy(ctx context.Context) error { - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 获取待部署的 ECDN 实例 - // 如果是泛域名,根据证书匹配 ECDN 实例 - aliInstanceIds := make([]string, 0) - domain := d.option.DeployConfig.GetConfigAsString("domain") - if strings.HasPrefix(domain, "*") { - domains, err := d.getDomainsByCertificateId(upres.CertId) - if err != nil { - return err - } - - aliInstanceIds = domains - } else { - aliInstanceIds = append(aliInstanceIds, domain) - } - if len(aliInstanceIds) == 0 { - d.infos = append(d.infos, "没有要部署的 ECDN 实例") - return nil - } - - // 证书部署到 ECDN 实例 - // REF: https://cloud.tencent.com/document/product/400/91667 - deployCertificateInstanceReq := tcSsl.NewDeployCertificateInstanceRequest() - deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId) - deployCertificateInstanceReq.ResourceType = common.StringPtr("ecdn") - deployCertificateInstanceReq.Status = common.Int64Ptr(1) - deployCertificateInstanceReq.InstanceIdList = common.StringPtrs(aliInstanceIds) - deployCertificateInstanceResp, err := d.sdkClients.ssl.DeployCertificateInstance(deployCertificateInstanceReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'") - } - - d.infos = append(d.infos, toStr("已部署证书到云资源实例", deployCertificateInstanceResp.Response)) - - return nil -} - -func (d *TencentECDNDeployer) createSdkClients(secretId, secretKey string) (*tencentECDNDeployerSdkClients, error) { - credential := common.NewCredential(secretId, secretKey) - - sslClient, err := tcSsl.NewClient(credential, "", profile.NewClientProfile()) - if err != nil { - return nil, err - } - - cdnClient, err := tcCdn.NewClient(credential, "", profile.NewClientProfile()) - if err != nil { - return nil, err - } - - return &tencentECDNDeployerSdkClients{ - ssl: sslClient, - cdn: cdnClient, - }, nil -} - -func (d *TencentECDNDeployer) getDomainsByCertificateId(tcCertId string) ([]string, error) { - // 获取证书中的可用域名 - // REF: https://cloud.tencent.com/document/product/228/42491 - describeCertDomainsReq := tcCdn.NewDescribeCertDomainsRequest() - describeCertDomainsReq.CertId = common.StringPtr(tcCertId) - describeCertDomainsReq.Product = common.StringPtr("ecdn") - describeCertDomainsResp, err := d.sdkClients.cdn.DescribeCertDomains(describeCertDomainsReq) - if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertDomains'") - } - - domains := make([]string, 0) - if describeCertDomainsResp.Response.Domains == nil { - for _, domain := range describeCertDomainsResp.Response.Domains { - domains = append(domains, *domain) - } - } - - return domains, nil -} diff --git a/internal/deployer/tencent_teo.go b/internal/deployer/tencent_teo.go deleted file mode 100644 index 583089dd..00000000 --- a/internal/deployer/tencent_teo.go +++ /dev/null @@ -1,119 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - xerrors "github.com/pkg/errors" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" - "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" - tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" - tcTeo "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo/v20220901" - - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" -) - -type TencentTEODeployer struct { - option *DeployerOption - infos []string - - sdkClients *tencentTEODeployerSdkClients - sslUploader uploader.Uploader -} - -type tencentTEODeployerSdkClients struct { - ssl *tcSsl.Client - teo *tcTeo.Client -} - -func NewTencentTEODeployer(option *DeployerOption) (Deployer, error) { - access := &domain.TencentAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - - clients, err := (&TencentTEODeployer{}).createSdkClients( - access.SecretId, - access.SecretKey, - ) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") - } - - uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{ - SecretId: access.SecretId, - SecretKey: access.SecretKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - - return &TencentTEODeployer{ - option: option, - infos: make([]string, 0), - sdkClients: clients, - sslUploader: uploader, - }, nil -} - -func (d *TencentTEODeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *TencentTEODeployer) GetInfos() []string { - return d.infos -} - -func (d *TencentTEODeployer) Deploy(ctx context.Context) error { - tcZoneId := d.option.DeployConfig.GetConfigAsString("zoneId") - if tcZoneId == "" { - return xerrors.New("`zoneId` is required") - } - - // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - // 配置域名证书 - // REF: https://cloud.tencent.com/document/product/1552/80764 - modifyHostsCertificateReq := tcTeo.NewModifyHostsCertificateRequest() - modifyHostsCertificateReq.ZoneId = common.StringPtr(tcZoneId) - modifyHostsCertificateReq.Mode = common.StringPtr("sslcert") - modifyHostsCertificateReq.Hosts = common.StringPtrs(strings.Split(strings.ReplaceAll(d.option.Domain, "\r\n", "\n"), "\n")) - modifyHostsCertificateReq.ServerCertInfo = []*tcTeo.ServerCertInfo{{CertId: common.StringPtr(upres.CertId)}} - modifyHostsCertificateResp, err := d.sdkClients.teo.ModifyHostsCertificate(modifyHostsCertificateReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'teo.ModifyHostsCertificate'") - } - - d.infos = append(d.infos, toStr("已配置域名证书", modifyHostsCertificateResp.Response)) - - return nil -} - -func (d *TencentTEODeployer) createSdkClients(secretId, secretKey string) (*tencentTEODeployerSdkClients, error) { - credential := common.NewCredential(secretId, secretKey) - - sslClient, err := tcSsl.NewClient(credential, "", profile.NewClientProfile()) - if err != nil { - return nil, err - } - - teoClient, err := tcTeo.NewClient(credential, "", profile.NewClientProfile()) - if err != nil { - return nil, err - } - - return &tencentTEODeployerSdkClients{ - ssl: sslClient, - teo: teoClient, - }, nil -} diff --git a/internal/deployer/volcengine_cdn.go b/internal/deployer/volcengine_cdn.go deleted file mode 100644 index 6ba8a23d..00000000 --- a/internal/deployer/volcengine_cdn.go +++ /dev/null @@ -1,116 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - volcenginecdn "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-cdn" - - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/volcengine/volc-sdk-golang/service/cdn" -) - -type VolcengineCDNDeployer struct { - option *DeployerOption - infos []string - sdkClient *cdn.CDN - sslUploader uploader.Uploader -} - -func NewVolcengineCDNDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.VolcEngineAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - client := cdn.NewInstance() - client.Client.SetAccessKey(access.AccessKeyId) - client.Client.SetSecretKey(access.SecretAccessKey) - uploader, err := volcenginecdn.New(&volcenginecdn.VolcEngineCDNUploaderConfig{ - AccessKeyId: access.AccessKeyId, - AccessKeySecret: access.SecretAccessKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - return &VolcengineCDNDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *VolcengineCDNDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *VolcengineCDNDeployer) GetInfos() []string { - return d.infos -} - -func (d *VolcengineCDNDeployer) Deploy(ctx context.Context) error { - apiCtx := context.Background() - // 上传证书 - upres, err := d.sslUploader.Upload(apiCtx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - domains := make([]string, 0) - configDomain := d.option.DeployConfig.GetConfigAsString("domain") - if strings.HasPrefix(configDomain, "*.") { - // 获取证书可以部署的域名 - // REF: https://www.volcengine.com/docs/6454/125711 - describeCertConfigReq := &cdn.DescribeCertConfigRequest{ - CertId: upres.CertId, - } - describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertConfig'") - } - for i := range describeCertConfigResp.Result.CertNotConfig { - // 当前未启用 HTTPS 的加速域名列表。 - domains = append(domains, describeCertConfigResp.Result.CertNotConfig[i].Domain) - } - for i := range describeCertConfigResp.Result.OtherCertConfig { - // 已启用了 HTTPS 的加速域名列表。这些加速域名关联的证书不是您指定的证书。 - domains = append(domains, describeCertConfigResp.Result.OtherCertConfig[i].Domain) - } - for i := range describeCertConfigResp.Result.SpecifiedCertConfig { - // 已启用了 HTTPS 的加速域名列表。这些加速域名关联了您指定的证书。 - d.infos = append(d.infos, fmt.Sprintf("%s域名已配置该证书", describeCertConfigResp.Result.SpecifiedCertConfig[i].Domain)) - } - if len(domains) == 0 { - if len(describeCertConfigResp.Result.SpecifiedCertConfig) > 0 { - // 所有匹配的域名都配置了该证书,跳过部署 - return nil - } else { - return xerrors.Errorf("未查询到匹配的域名: %s", configDomain) - } - } - } else { - domains = append(domains, configDomain) - } - // 部署证书 - // REF: https://www.volcengine.com/docs/6454/125712 - for i := range domains { - batchDeployCertReq := &cdn.BatchDeployCertRequest{ - CertId: upres.CertId, - Domain: domains[i], - } - batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdn.BatchDeployCert'") - } else { - d.infos = append(d.infos, toStr(fmt.Sprintf("%s域名的证书已修改", domains[i]), batchDeployCertResp)) - } - } - - return nil -} diff --git a/internal/deployer/volcengine_live.go b/internal/deployer/volcengine_live.go deleted file mode 100644 index 1795d79f..00000000 --- a/internal/deployer/volcengine_live.go +++ /dev/null @@ -1,148 +0,0 @@ -package deployer - -import ( - "context" - "encoding/json" - "fmt" - "regexp" - "strings" - - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - volcenginelive "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-live" - "github.com/usual2970/certimate/internal/pkg/utils/cast" - "github.com/volcengine/volc-sdk-golang/base" - live "github.com/volcengine/volc-sdk-golang/service/live/v20230101" -) - -type VolcengineLiveDeployer struct { - option *DeployerOption - infos []string - sdkClient *live.Live - sslUploader uploader.Uploader -} - -func NewVolcengineLiveDeployer(option *DeployerOption) (Deployer, error) { - access := &domain.VolcEngineAccess{} - if err := json.Unmarshal([]byte(option.Access), access); err != nil { - return nil, xerrors.Wrap(err, "failed to get access") - } - client := live.NewInstance() - client.SetCredential(base.Credentials{ - AccessKeyID: access.AccessKeyId, - SecretAccessKey: access.SecretAccessKey, - }) - uploader, err := volcenginelive.New(&volcenginelive.VolcEngineLiveUploaderConfig{ - AccessKeyId: access.AccessKeyId, - AccessKeySecret: access.SecretAccessKey, - }) - if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") - } - return &VolcengineLiveDeployer{ - option: option, - infos: make([]string, 0), - sdkClient: client, - sslUploader: uploader, - }, nil -} - -func (d *VolcengineLiveDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *VolcengineLiveDeployer) GetInfos() []string { - return d.infos -} - -func (d *VolcengineLiveDeployer) Deploy(ctx context.Context) error { - apiCtx := context.Background() - // 上传证书 - upres, err := d.sslUploader.Upload(apiCtx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) - if err != nil { - return err - } - - d.infos = append(d.infos, toStr("已上传证书", upres)) - - domains := make([]string, 0) - configDomain := d.option.DeployConfig.GetConfigAsString("domain") - if strings.HasPrefix(configDomain, "*.") { - // 如果是泛域名,获取所有的域名并匹配 - matchDomains, err := d.getDomainsByWildcardDomain(apiCtx, configDomain) - if err != nil { - d.infos = append(d.infos, toStr("获取域名列表失败", upres)) - return xerrors.Wrap(err, "failed to execute sdk request 'live.ListDomainDetail'") - } - if len(matchDomains) == 0 { - return xerrors.Errorf("未查询到匹配的域名: %s", configDomain) - } - domains = matchDomains - } else { - domains = append(domains, configDomain) - } - - // 部署证书 - // REF: https://www.volcengine.com/docs/6469/1186278#%E7%BB%91%E5%AE%9A%E8%AF%81%E4%B9%A6d - for i := range domains { - bindCertReq := &live.BindCertBody{ - ChainID: upres.CertId, - Domain: domains[i], - HTTPS: cast.BoolPtr(true), - } - bindCertResp, err := d.sdkClient.BindCert(apiCtx, bindCertReq) - if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'live.BindCert'") - } else { - d.infos = append(d.infos, toStr(fmt.Sprintf("%s域名的证书已修改", domains[i]), bindCertResp)) - } - } - - return nil -} - -func (d *VolcengineLiveDeployer) getDomainsByWildcardDomain(ctx context.Context, wildcardDomain string) ([]string, error) { - pageNum := int32(1) - searchTotal := 0 - domains := make([]string, 0) - for { - listDomainDetailReq := &live.ListDomainDetailBody{ - PageNum: pageNum, - PageSize: 1000, - } - // 查询域名列表 - // REF: https://www.volcengine.com/docs/6469/1186277#%E6%9F%A5%E8%AF%A2%E5%9F%9F%E5%90%8D%E5%88%97%E8%A1%A8 - listDomainDetailResp, err := d.sdkClient.ListDomainDetail(ctx, listDomainDetailReq) - if err != nil { - return domains, err - } - if listDomainDetailResp.Result.DomainList != nil { - for _, item := range listDomainDetailResp.Result.DomainList { - if matchWildcardDomain(item.Domain, wildcardDomain) { - domains = append(domains, item.Domain) - } - } - } - searchTotal += len(listDomainDetailResp.Result.DomainList) - if int(listDomainDetailResp.Result.Total) > searchTotal { - pageNum++ - } else { - break - } - } - - return domains, nil -} - -func matchWildcardDomain(domain, wildcardDomain string) bool { - if strings.HasPrefix(wildcardDomain, "*.") { - if "*."+domain == wildcardDomain { - return true - } - regexPattern := "^([a-zA-Z0-9_-]+)\\." + regexp.QuoteMeta(wildcardDomain[2:]) + "$" - regex := regexp.MustCompile(regexPattern) - return regex.MatchString(domain) - } - return domain == wildcardDomain -} diff --git a/internal/deployer/webhook.go b/internal/deployer/webhook.go deleted file mode 100644 index 35c37235..00000000 --- a/internal/deployer/webhook.go +++ /dev/null @@ -1,66 +0,0 @@ -package deployer - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - - xerrors "github.com/pkg/errors" - - "github.com/usual2970/certimate/internal/domain" - xhttp "github.com/usual2970/certimate/internal/utils/http" -) - -type WebhookDeployer struct { - option *DeployerOption - infos []string -} - -func NewWebhookDeployer(option *DeployerOption) (Deployer, error) { - return &WebhookDeployer{ - option: option, - infos: make([]string, 0), - }, nil -} - -func (d *WebhookDeployer) GetID() string { - return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) -} - -func (d *WebhookDeployer) GetInfos() []string { - return d.infos -} - -type webhookData struct { - Domain string `json:"domain"` - Certificate string `json:"certificate"` - PrivateKey string `json:"privateKey"` - Variables map[string]string `json:"variables"` -} - -func (d *WebhookDeployer) Deploy(ctx context.Context) error { - access := &domain.WebhookAccess{} - if err := json.Unmarshal([]byte(d.option.Access), access); err != nil { - return xerrors.Wrap(err, "failed to get access") - } - - data := &webhookData{ - Domain: d.option.Domain, - Certificate: d.option.Certificate.Certificate, - PrivateKey: d.option.Certificate.PrivateKey, - Variables: d.option.DeployConfig.GetConfigAsVariables(), - } - body, _ := json.Marshal(data) - resp, err := xhttp.Req(access.Url, http.MethodPost, bytes.NewReader(body), map[string]string{ - "Content-Type": "application/json", - }) - if err != nil { - return xerrors.Wrap(err, "failed to send webhook request") - } - - d.infos = append(d.infos, toStr("Webhook Response", string(resp))) - - return nil -}