mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-19 09:51:55 +08:00
feat: implement more Deployer
This commit is contained in:
parent
a59184ae5f
commit
643a666853
@ -41,7 +41,7 @@ func (dc *DeployConfig) GetConfigAsString(key string) string {
|
|||||||
// - defaultValue: 默认值。
|
// - defaultValue: 默认值。
|
||||||
//
|
//
|
||||||
// 出参:
|
// 出参:
|
||||||
// - 配置项的值。如果配置项不存在或者类型不是字符串,则返回默认值。
|
// - 配置项的值。如果配置项不存在、类型不是字符串或者值为零值,则返回默认值。
|
||||||
func (dc *DeployConfig) GetConfigOrDefaultAsString(key string, defaultValue string) string {
|
func (dc *DeployConfig) GetConfigOrDefaultAsString(key string, defaultValue string) string {
|
||||||
return maps.GetValueOrDefaultAsString(dc.Config, key, defaultValue)
|
return maps.GetValueOrDefaultAsString(dc.Config, key, defaultValue)
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ func (dc *DeployConfig) GetConfigAsInt32(key string) int32 {
|
|||||||
// - defaultValue: 默认值。
|
// - defaultValue: 默认值。
|
||||||
//
|
//
|
||||||
// 出参:
|
// 出参:
|
||||||
// - 配置项的值。如果配置项不存在或者类型不是 32 位整数,则返回默认值。
|
// - 配置项的值。如果配置项不存在、类型不是 32 位整数或者值为零值,则返回默认值。
|
||||||
func (dc *DeployConfig) GetConfigOrDefaultAsInt32(key string, defaultValue int32) int32 {
|
func (dc *DeployConfig) GetConfigOrDefaultAsInt32(key string, defaultValue int32) int32 {
|
||||||
return maps.GetValueOrDefaultAsInt32(dc.Config, key, defaultValue)
|
return maps.GetValueOrDefaultAsInt32(dc.Config, key, defaultValue)
|
||||||
}
|
}
|
||||||
|
289
internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go
Normal file
289
internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
package aliyunalb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"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/pkg/core/deployer"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
upAliyunCas "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliyunALBDeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 阿里云地域。
|
||||||
|
Region string `json:"region"`
|
||||||
|
// 部署资源类型。
|
||||||
|
ResourceType DeployResourceType `json:"resourceType"`
|
||||||
|
// 负载均衡实例 ID。
|
||||||
|
// 部署资源类型为 [DEPLOY_RESOURCE_LOADBALANCER] 时必填。
|
||||||
|
LoadbalancerId string `json:"loadbalancerId,omitempty"`
|
||||||
|
// 负载均衡监听 ID。
|
||||||
|
// 部署资源类型为 [DEPLOY_RESOURCE_LISTENER] 时必填。
|
||||||
|
ListenerId string `json:"listenerId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AliyunALBDeployer struct {
|
||||||
|
config *AliyunALBDeployerConfig
|
||||||
|
logger deployer.Logger
|
||||||
|
sdkClient *aliyunAlb.Client
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*AliyunALBDeployer)(nil)
|
||||||
|
|
||||||
|
func New(config *AliyunALBDeployerConfig) (*AliyunALBDeployer, error) {
|
||||||
|
return NewWithLogger(config, deployer.NewNilLogger())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithLogger(config *AliyunALBDeployerConfig, logger deployer.Logger) (*AliyunALBDeployer, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger == nil {
|
||||||
|
return nil, errors.New("logger is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
||||||
|
}
|
||||||
|
|
||||||
|
aliyunCasRegion := config.Region
|
||||||
|
if aliyunCasRegion != "" {
|
||||||
|
// 阿里云 CAS 服务接入点是独立于 ALB 服务的
|
||||||
|
// 国内版固定接入点:华东一杭州
|
||||||
|
// 国际版固定接入点:亚太东南一新加坡
|
||||||
|
if !strings.HasPrefix(aliyunCasRegion, "cn-") {
|
||||||
|
aliyunCasRegion = "ap-southeast-1"
|
||||||
|
} else {
|
||||||
|
aliyunCasRegion = "cn-hangzhou"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uploader, err := upAliyunCas.New(&upAliyunCas.AliyunCASUploaderConfig{
|
||||||
|
AccessKeyId: config.AccessKeyId,
|
||||||
|
AccessKeySecret: config.AccessKeySecret,
|
||||||
|
Region: aliyunCasRegion,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AliyunALBDeployer{
|
||||||
|
logger: logger,
|
||||||
|
config: config,
|
||||||
|
sdkClient: client,
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunALBDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||||
|
// 上传证书到 CAS
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("certificate file uploaded", upres)
|
||||||
|
|
||||||
|
// 根据部署资源类型决定部署方式
|
||||||
|
switch d.config.ResourceType {
|
||||||
|
case DEPLOY_RESOURCE_LOADBALANCER:
|
||||||
|
if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case DEPLOY_RESOURCE_LISTENER:
|
||||||
|
if err := d.deployToListener(ctx, upres.CertId); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunALBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertId string) error {
|
||||||
|
if d.config.LoadbalancerId == "" {
|
||||||
|
return errors.New("config `loadbalancerId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
listenerIds := 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(d.config.LoadbalancerId),
|
||||||
|
}
|
||||||
|
getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetLoadBalancerAttribute'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 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(d.config.LoadbalancerId)},
|
||||||
|
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 {
|
||||||
|
listenerIds = append(listenerIds, *listener.ListenerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if listListenersResp.Body.NextToken == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
listListenersToken = listListenersResp.Body.NextToken
|
||||||
|
listListenersPage += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 ALB 负载均衡实例下的全部 HTTPS 监听", listenerIds)
|
||||||
|
|
||||||
|
// 查询 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(d.config.LoadbalancerId)},
|
||||||
|
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 {
|
||||||
|
listenerIds = append(listenerIds, *listener.ListenerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if listListenersResp.Body.NextToken == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
listListenersToken = listListenersResp.Body.NextToken
|
||||||
|
listListenersPage += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 ALB 负载均衡实例下的全部 QUIC 监听", listenerIds)
|
||||||
|
|
||||||
|
// 批量更新监听证书
|
||||||
|
var errs []error
|
||||||
|
for _, listenerId := range listenerIds {
|
||||||
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunALBDeployer) deployToListener(ctx context.Context, cloudCertId string) error {
|
||||||
|
if d.config.ListenerId == "" {
|
||||||
|
return errors.New("config `listenerId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新监听
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.ListenerId, cloudCertId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunALBDeployer) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId 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(cloudListenerId),
|
||||||
|
}
|
||||||
|
getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetListenerAttribute'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 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(cloudListenerId),
|
||||||
|
Certificates: []*aliyunAlb.UpdateListenerAttributeRequestCertificates{{
|
||||||
|
CertificateId: tea.String(cloudCertId),
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'alb.UpdateListenerAttribute'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已更新 ALB 监听配置", updateListenerAttributeResp)
|
||||||
|
|
||||||
|
// TODO: #347
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunAlb.Client, error) {
|
||||||
|
if region == "" {
|
||||||
|
region = "cn-hangzhou"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接入点一览 https://www.alibabacloud.com/help/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-endpoint
|
||||||
|
var endpoint string
|
||||||
|
switch region {
|
||||||
|
case "cn-hangzhou-finance":
|
||||||
|
endpoint = "alb.cn-hangzhou.aliyuncs.com"
|
||||||
|
default:
|
||||||
|
endpoint = fmt.Sprintf("alb.%s.aliyuncs.com", region)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &aliyunOpen.Config{
|
||||||
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
|
Endpoint: tea.String(endpoint),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := aliyunAlb.NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
package aliyunalb_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
dpAliyunAlb "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-alb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fRegion string
|
||||||
|
fLoadbalancerId string
|
||||||
|
fListenerId string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNALB_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
|
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
|
||||||
|
flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v aliyun_alb_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNALB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNALB_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNALB_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNALB_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNALB_REGION="cn-hangzhou" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNALB_LOADBALANCERID="your-alb-instance-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNALB_LISTENERID="your-alb-listener-id"
|
||||||
|
*/
|
||||||
|
func Test(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy_ToLoadbalancer", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := dpAliyunAlb.New(&dpAliyunAlb.AliyunALBDeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
ResourceType: dpAliyunAlb.DEPLOY_RESOURCE_LOADBALANCER,
|
||||||
|
LoadbalancerId: fLoadbalancerId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deploy_ToListener", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
fmt.Sprintf("LISTENERID: %v", fListenerId),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := dpAliyunAlb.New(&dpAliyunAlb.AliyunALBDeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
ResourceType: dpAliyunAlb.DEPLOY_RESOURCE_LISTENER,
|
||||||
|
ListenerId: fListenerId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
10
internal/pkg/core/deployer/providers/aliyun-alb/defines.go
Normal file
10
internal/pkg/core/deployer/providers/aliyun-alb/defines.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package aliyunalb
|
||||||
|
|
||||||
|
type DeployResourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 资源类型:部署到指定负载均衡器。
|
||||||
|
DEPLOY_RESOURCE_LOADBALANCER = DeployResourceType("loadbalancer")
|
||||||
|
// 资源类型:部署到指定监听器。
|
||||||
|
DEPLOY_RESOURCE_LISTENER = DeployResourceType("listener")
|
||||||
|
)
|
@ -0,0 +1,93 @@
|
|||||||
|
package aliyuncdn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"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/pkg/core/deployer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliyunCDNDeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 加速域名(不支持泛域名)。
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AliyunCDNDeployer struct {
|
||||||
|
config *AliyunCDNDeployerConfig
|
||||||
|
logger deployer.Logger
|
||||||
|
sdkClient *aliyunCdn.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*AliyunCDNDeployer)(nil)
|
||||||
|
|
||||||
|
func New(config *AliyunCDNDeployerConfig) (*AliyunCDNDeployer, error) {
|
||||||
|
return NewWithLogger(config, deployer.NewNilLogger())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithLogger(config *AliyunCDNDeployerConfig, logger deployer.Logger) (*AliyunCDNDeployer, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger == nil {
|
||||||
|
return nil, errors.New("logger is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AliyunCDNDeployer{
|
||||||
|
logger: logger,
|
||||||
|
config: config,
|
||||||
|
sdkClient: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||||
|
// 设置 CDN 域名域名证书
|
||||||
|
// REF: https://help.aliyun.com/zh/cdn/developer-reference/api-cdn-2018-05-10-setcdndomainsslcertificate
|
||||||
|
setCdnDomainSSLCertificateReq := &aliyunCdn.SetCdnDomainSSLCertificateRequest{
|
||||||
|
DomainName: tea.String(d.config.Domain),
|
||||||
|
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||||
|
CertType: tea.String("upload"),
|
||||||
|
SSLProtocol: tea.String("on"),
|
||||||
|
SSLPub: tea.String(certPem),
|
||||||
|
SSLPri: tea.String(privkeyPem),
|
||||||
|
}
|
||||||
|
setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificate(setCdnDomainSSLCertificateReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.SetCdnDomainSSLCertificate'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已设置 CDN 域名证书", setCdnDomainSSLCertificateResp)
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret string) (*aliyunCdn.Client, error) {
|
||||||
|
config := &aliyunOpen.Config{
|
||||||
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
|
Endpoint: tea.String("cdn.aliyuncs.com"),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := aliyunCdn.NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package aliyuncdn_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
dpAliyunCdn "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fDomain string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNCDN_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v aliyun_cdn_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCDN_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCDN_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCDN_DOMAIN="example.com" \
|
||||||
|
*/
|
||||||
|
func Test(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := dpAliyunCdn.New(&dpAliyunCdn.AliyunCDNDeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Domain: fDomain,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
291
internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go
Normal file
291
internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
package aliyunclb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"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/pkg/core/deployer"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
upAliyunSlb "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliyunCLBDeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 阿里云地域。
|
||||||
|
Region string `json:"region"`
|
||||||
|
// 部署资源类型。
|
||||||
|
ResourceType DeployResourceType `json:"resourceType"`
|
||||||
|
// 负载均衡实例 ID。
|
||||||
|
// 部署资源类型为 [DEPLOY_RESOURCE_LOADBALANCER]、[DEPLOY_RESOURCE_LISTENER] 时必填。
|
||||||
|
LoadbalancerId string `json:"loadbalancerId,omitempty"`
|
||||||
|
// 负载均衡监听端口。
|
||||||
|
// 部署资源类型为 [DEPLOY_RESOURCE_LISTENER] 时必填。
|
||||||
|
ListenerPort int32 `json:"listenerPort,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AliyunCLBDeployer struct {
|
||||||
|
config *AliyunCLBDeployerConfig
|
||||||
|
logger deployer.Logger
|
||||||
|
sdkClient *aliyunSlb.Client
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*AliyunCLBDeployer)(nil)
|
||||||
|
|
||||||
|
func New(config *AliyunCLBDeployerConfig) (*AliyunCLBDeployer, error) {
|
||||||
|
return NewWithLogger(config, deployer.NewNilLogger())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithLogger(config *AliyunCLBDeployerConfig, logger deployer.Logger) (*AliyunCLBDeployer, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger == nil {
|
||||||
|
return nil, errors.New("logger is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := upAliyunSlb.New(&upAliyunSlb.AliyunSLBUploaderConfig{
|
||||||
|
AccessKeyId: config.AccessKeyId,
|
||||||
|
AccessKeySecret: config.AccessKeySecret,
|
||||||
|
Region: config.Region,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AliyunCLBDeployer{
|
||||||
|
logger: logger,
|
||||||
|
config: config,
|
||||||
|
sdkClient: client,
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunCLBDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||||
|
// 上传证书到 SLB
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("certificate file uploaded", upres)
|
||||||
|
|
||||||
|
// 根据部署资源类型决定部署方式
|
||||||
|
switch d.config.ResourceType {
|
||||||
|
case DEPLOY_RESOURCE_LOADBALANCER:
|
||||||
|
if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case DEPLOY_RESOURCE_LISTENER:
|
||||||
|
if err := d.deployToListener(ctx, upres.CertId); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunCLBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertId string) error {
|
||||||
|
if d.config.LoadbalancerId == "" {
|
||||||
|
return errors.New("config `loadbalancerId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
listenerPorts := make([]int32, 0)
|
||||||
|
|
||||||
|
// 查询负载均衡实例的详细信息
|
||||||
|
// 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.config.Region),
|
||||||
|
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
||||||
|
}
|
||||||
|
describeLoadBalancerAttributeResp, err := d.sdkClient.DescribeLoadBalancerAttribute(describeLoadBalancerAttributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerAttribute'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 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.config.Region),
|
||||||
|
MaxResults: tea.Int32(listListenersLimit),
|
||||||
|
NextToken: listListenersToken,
|
||||||
|
LoadBalancerId: []*string{tea.String(d.config.LoadbalancerId)},
|
||||||
|
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 {
|
||||||
|
listenerPorts = append(listenerPorts, *listener.ListenerPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if describeLoadBalancerListenersResp.Body.NextToken == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
listListenersToken = describeLoadBalancerListenersResp.Body.NextToken
|
||||||
|
listListenersPage += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 CLB 负载均衡实例下的全部 HTTPS 监听", listenerPorts)
|
||||||
|
|
||||||
|
// 批量更新监听证书
|
||||||
|
var errs []error
|
||||||
|
for _, listenerPort := range listenerPorts {
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunCLBDeployer) deployToListener(ctx context.Context, cloudCertId string) error {
|
||||||
|
if d.config.LoadbalancerId == "" {
|
||||||
|
return errors.New("config `loadbalancerId` is required")
|
||||||
|
}
|
||||||
|
if d.config.ListenerPort == 0 {
|
||||||
|
return errors.New("config `listenerPort` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新监听
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, d.config.ListenerPort, cloudCertId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunCLBDeployer) updateListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudListenerPort int32, cloudCertId 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(cloudLoadbalancerId),
|
||||||
|
ListenerPort: tea.Int32(cloudListenerPort),
|
||||||
|
}
|
||||||
|
describeLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.DescribeLoadBalancerHTTPSListenerAttribute(describeLoadBalancerHTTPSListenerAttributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 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.config.Region),
|
||||||
|
LoadBalancerId: tea.String(cloudLoadbalancerId),
|
||||||
|
ListenerPort: tea.Int32(cloudListenerPort),
|
||||||
|
}
|
||||||
|
describeDomainExtensionsResp, err := d.sdkClient.DescribeDomainExtensions(describeDomainExtensionsReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeDomainExtensions'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 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.config.Region),
|
||||||
|
DomainExtensionId: tea.String(*domainExtension.DomainExtensionId),
|
||||||
|
ServerCertificateId: tea.String(cloudCertId),
|
||||||
|
}
|
||||||
|
_, 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.config.Region),
|
||||||
|
LoadBalancerId: tea.String(cloudLoadbalancerId),
|
||||||
|
ListenerPort: tea.Int32(cloudListenerPort),
|
||||||
|
ServerCertificateId: tea.String(cloudCertId),
|
||||||
|
}
|
||||||
|
setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.SetLoadBalancerHTTPSListenerAttribute(setLoadBalancerHTTPSListenerAttributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已更新 CLB HTTPS 监听配置", setLoadBalancerHTTPSListenerAttributeResp)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunSlb.Client, error) {
|
||||||
|
if region == "" {
|
||||||
|
region = "cn-hangzhou" // CLB(SLB) 服务默认区域:华东一杭州
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接入点一览 https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-endpoint
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &aliyunOpen.Config{
|
||||||
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
|
Endpoint: tea.String(endpoint),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := aliyunSlb.NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
package aliyunclb_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
dpAliyunClb "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-clb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fRegion string
|
||||||
|
fLoadbalancerId string
|
||||||
|
fListenerPort int
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNCLB_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
|
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
|
||||||
|
flag.IntVar(&fListenerPort, argsPrefix+"LISTENERPORT", 443, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v aliyun_clb_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_REGION="cn-hangzhou" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_LOADBALANCERID="your-clb-instance-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_LISTENERPORT=443
|
||||||
|
*/
|
||||||
|
func Test(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy_ToLoadbalancer", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := dpAliyunClb.New(&dpAliyunClb.AliyunCLBDeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
ResourceType: dpAliyunClb.DEPLOY_RESOURCE_LOADBALANCER,
|
||||||
|
LoadbalancerId: fLoadbalancerId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deploy_ToListener", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
|
||||||
|
fmt.Sprintf("LISTENERPORT: %v", fListenerPort),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := dpAliyunClb.New(&dpAliyunClb.AliyunCLBDeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
ResourceType: dpAliyunClb.DEPLOY_RESOURCE_LISTENER,
|
||||||
|
LoadbalancerId: fLoadbalancerId,
|
||||||
|
ListenerPort: int32(fListenerPort),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
10
internal/pkg/core/deployer/providers/aliyun-clb/defines.go
Normal file
10
internal/pkg/core/deployer/providers/aliyun-clb/defines.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package aliyunclb
|
||||||
|
|
||||||
|
type DeployResourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 资源类型:部署到指定负载均衡器。
|
||||||
|
DEPLOY_RESOURCE_LOADBALANCER = DeployResourceType("loadbalancer")
|
||||||
|
// 资源类型:部署到指定监听器。
|
||||||
|
DEPLOY_RESOURCE_LISTENER = DeployResourceType("listener")
|
||||||
|
)
|
@ -0,0 +1,97 @@
|
|||||||
|
package aliyundcdn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"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/pkg/core/deployer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliyunDCDNDeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 加速域名(支持泛域名)。
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AliyunDCDNDeployer struct {
|
||||||
|
config *AliyunDCDNDeployerConfig
|
||||||
|
logger deployer.Logger
|
||||||
|
sdkClient *aliyunDcdn.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*AliyunDCDNDeployer)(nil)
|
||||||
|
|
||||||
|
func New(config *AliyunDCDNDeployerConfig) (*AliyunDCDNDeployer, error) {
|
||||||
|
return NewWithLogger(config, deployer.NewNilLogger())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithLogger(config *AliyunDCDNDeployerConfig, logger deployer.Logger) (*AliyunDCDNDeployer, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger == nil {
|
||||||
|
return nil, errors.New("logger is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AliyunDCDNDeployer{
|
||||||
|
logger: logger,
|
||||||
|
config: config,
|
||||||
|
sdkClient: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunDCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||||
|
// "*.example.com" → ".example.com",适配阿里云 DCDN 要求的泛域名格式
|
||||||
|
domain := strings.TrimPrefix(d.config.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),
|
||||||
|
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||||
|
CertType: tea.String("upload"),
|
||||||
|
SSLProtocol: tea.String("on"),
|
||||||
|
SSLPub: tea.String(certPem),
|
||||||
|
SSLPri: tea.String(privkeyPem),
|
||||||
|
}
|
||||||
|
setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificate(setDcdnDomainSSLCertificateReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to execute sdk request 'dcdn.SetDcdnDomainSSLCertificate'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已配置 DCDN 域名证书", setDcdnDomainSSLCertificateResp)
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret string) (*aliyunDcdn.Client, error) {
|
||||||
|
config := &aliyunOpen.Config{
|
||||||
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
|
Endpoint: tea.String("dcdn.aliyuncs.com"),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := aliyunDcdn.NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package aliyundcdn_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
dpAliyunDcdn "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-dcdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fDomain string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNDCDN_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v aliyun_dcdn_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDCDN_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDCDN_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDCDN_DOMAIN="example.com" \
|
||||||
|
*/
|
||||||
|
func Test(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := dpAliyunDcdn.New(&dpAliyunDcdn.AliyunDCDNDeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Domain: fDomain,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
251
internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go
Normal file
251
internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
package aliyunnlb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"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/pkg/core/deployer"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
upAliyunCas "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliyunNLBDeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 阿里云地域。
|
||||||
|
Region string `json:"region"`
|
||||||
|
// 部署资源类型。
|
||||||
|
ResourceType DeployResourceType `json:"resourceType"`
|
||||||
|
// 负载均衡实例 ID。
|
||||||
|
// 部署资源类型为 [DEPLOY_RESOURCE_LOADBALANCER] 时必填。
|
||||||
|
LoadbalancerId string `json:"loadbalancerId,omitempty"`
|
||||||
|
// 负载均衡监听 ID。
|
||||||
|
// 部署资源类型为 [DEPLOY_RESOURCE_LISTENER] 时必填。
|
||||||
|
ListenerId string `json:"listenerId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AliyunNLBDeployer struct {
|
||||||
|
config *AliyunNLBDeployerConfig
|
||||||
|
logger deployer.Logger
|
||||||
|
sdkClient *aliyunNlb.Client
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*AliyunNLBDeployer)(nil)
|
||||||
|
|
||||||
|
func New(config *AliyunNLBDeployerConfig) (*AliyunNLBDeployer, error) {
|
||||||
|
return NewWithLogger(config, deployer.NewNilLogger())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithLogger(config *AliyunNLBDeployerConfig, logger deployer.Logger) (*AliyunNLBDeployer, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger == nil {
|
||||||
|
return nil, errors.New("logger is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
||||||
|
}
|
||||||
|
|
||||||
|
aliyunCasRegion := config.Region
|
||||||
|
if aliyunCasRegion != "" {
|
||||||
|
// 阿里云 CAS 服务接入点是独立于 NLB 服务的
|
||||||
|
// 国内版固定接入点:华东一杭州
|
||||||
|
// 国际版固定接入点:亚太东南一新加坡
|
||||||
|
if !strings.HasPrefix(aliyunCasRegion, "cn-") {
|
||||||
|
aliyunCasRegion = "ap-southeast-1"
|
||||||
|
} else {
|
||||||
|
aliyunCasRegion = "cn-hangzhou"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uploader, err := upAliyunCas.New(&upAliyunCas.AliyunCASUploaderConfig{
|
||||||
|
AccessKeyId: config.AccessKeyId,
|
||||||
|
AccessKeySecret: config.AccessKeySecret,
|
||||||
|
Region: aliyunCasRegion,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AliyunNLBDeployer{
|
||||||
|
logger: logger,
|
||||||
|
config: config,
|
||||||
|
sdkClient: client,
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunNLBDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||||
|
// 上传证书到 CAS
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("certificate file uploaded", upres)
|
||||||
|
|
||||||
|
// 根据部署资源类型决定部署方式
|
||||||
|
switch d.config.ResourceType {
|
||||||
|
case DEPLOY_RESOURCE_LOADBALANCER:
|
||||||
|
if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case DEPLOY_RESOURCE_LISTENER:
|
||||||
|
if err := d.deployToListener(ctx, upres.CertId); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunNLBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertId string) error {
|
||||||
|
if d.config.LoadbalancerId == "" {
|
||||||
|
return errors.New("config `loadbalancerId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
listenerIds := 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(d.config.LoadbalancerId),
|
||||||
|
}
|
||||||
|
getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetLoadBalancerAttribute'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 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(d.config.LoadbalancerId)},
|
||||||
|
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 {
|
||||||
|
listenerIds = append(listenerIds, *listener.ListenerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if listListenersResp.Body.NextToken == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
listListenersToken = listListenersResp.Body.NextToken
|
||||||
|
listListenersPage += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 NLB 负载均衡实例下的全部 TCPSSL 监听", listenerIds)
|
||||||
|
|
||||||
|
// 批量更新监听证书
|
||||||
|
var errs []error
|
||||||
|
for _, listenerId := range listenerIds {
|
||||||
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunNLBDeployer) deployToListener(ctx context.Context, cloudCertId string) error {
|
||||||
|
if d.config.ListenerId == "" {
|
||||||
|
return errors.New("config `listenerId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新监听
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.ListenerId, cloudCertId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunNLBDeployer) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId 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(cloudListenerId),
|
||||||
|
}
|
||||||
|
getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetListenerAttribute'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已查询到 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(cloudListenerId),
|
||||||
|
CertificateIds: []*string{tea.String(cloudCertId)},
|
||||||
|
}
|
||||||
|
updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Wrap(err, "failed to execute sdk request 'nlb.UpdateListenerAttribute'")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Appendt("已更新 NLB 监听配置", updateListenerAttributeResp)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunNlb.Client, error) {
|
||||||
|
if region == "" {
|
||||||
|
region = "cn-hangzhou" // NLB 服务默认区域:华东一杭州
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接入点一览 https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-endpoint
|
||||||
|
var endpoint string
|
||||||
|
switch region {
|
||||||
|
default:
|
||||||
|
endpoint = fmt.Sprintf("nlb.%s.aliyuncs.com", region)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &aliyunOpen.Config{
|
||||||
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
|
Endpoint: tea.String(endpoint),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := aliyunNlb.NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
package aliyunnlb_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
dpAliyunClb "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-nlb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fRegion string
|
||||||
|
fLoadbalancerId string
|
||||||
|
fListenerId string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNNLB_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
|
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
|
||||||
|
flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v aliyun_nlb_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNNLB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNNLB_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNNLB_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNNLB_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNNLB_REGION="cn-hangzhou" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNNLB_LOADBALANCERID="your-nlb-instance-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNNLB_LISTENERID="your-nlb-listener-id"
|
||||||
|
*/
|
||||||
|
func Test(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy_ToLoadbalancer", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := dpAliyunClb.New(&dpAliyunClb.AliyunNLBDeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
ResourceType: dpAliyunClb.DEPLOY_RESOURCE_LOADBALANCER,
|
||||||
|
LoadbalancerId: fLoadbalancerId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deploy_ToListener", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
|
||||||
|
fmt.Sprintf("LISTENERID: %v", fListenerId),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := dpAliyunClb.New(&dpAliyunClb.AliyunNLBDeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
ResourceType: dpAliyunClb.DEPLOY_RESOURCE_LISTENER,
|
||||||
|
ListenerId: fListenerId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
10
internal/pkg/core/deployer/providers/aliyun-nlb/defines.go
Normal file
10
internal/pkg/core/deployer/providers/aliyun-nlb/defines.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package aliyunnlb
|
||||||
|
|
||||||
|
type DeployResourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 资源类型:部署到指定负载均衡器。
|
||||||
|
DEPLOY_RESOURCE_LOADBALANCER = DeployResourceType("loadbalancer")
|
||||||
|
// 资源类型:部署到指定监听器。
|
||||||
|
DEPLOY_RESOURCE_LISTENER = DeployResourceType("listener")
|
||||||
|
)
|
112
internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go
Normal file
112
internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package aliyunoss
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||||
|
xerrors "github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliyunOSSDeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 阿里云地域。
|
||||||
|
Region string `json:"region"`
|
||||||
|
// 存储桶名。
|
||||||
|
Bucket string `json:"bucket"`
|
||||||
|
// 自定义域名(不支持泛域名)。
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AliyunOSSDeployer struct {
|
||||||
|
config *AliyunOSSDeployerConfig
|
||||||
|
logger deployer.Logger
|
||||||
|
sdkClient *oss.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*AliyunOSSDeployer)(nil)
|
||||||
|
|
||||||
|
func New(config *AliyunOSSDeployerConfig) (*AliyunOSSDeployer, error) {
|
||||||
|
return NewWithLogger(config, deployer.NewNilLogger())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithLogger(config *AliyunOSSDeployerConfig, logger deployer.Logger) (*AliyunOSSDeployer, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger == nil {
|
||||||
|
return nil, errors.New("logger is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AliyunOSSDeployer{
|
||||||
|
logger: logger,
|
||||||
|
config: config,
|
||||||
|
sdkClient: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyunOSSDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||||
|
if d.config.Bucket == "" {
|
||||||
|
return nil, errors.New("config `bucket` is required")
|
||||||
|
}
|
||||||
|
if d.config.Domain == "" {
|
||||||
|
return nil, errors.New("config `domain` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为存储空间绑定自定义域名
|
||||||
|
// REF: https://help.aliyun.com/zh/oss/developer-reference/putcname
|
||||||
|
err := d.sdkClient.PutBucketCnameWithCertificate(d.config.Bucket, oss.PutBucketCname{
|
||||||
|
Cname: d.config.Domain,
|
||||||
|
CertificateConfiguration: &oss.CertificateConfiguration{
|
||||||
|
Certificate: certPem,
|
||||||
|
PrivateKey: privkeyPem,
|
||||||
|
Force: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Wrap(err, "failed to execute sdk request 'oss.PutBucketCnameWithCertificate'")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*oss.Client, error) {
|
||||||
|
// 接入点一览 https://help.aliyun.com/zh/oss/user-guide/regions-and-endpoints
|
||||||
|
var endpoint string
|
||||||
|
switch region {
|
||||||
|
case "":
|
||||||
|
endpoint = "oss.aliyuncs.com"
|
||||||
|
case
|
||||||
|
"cn-hzjbp",
|
||||||
|
"cn-hzjbp-a",
|
||||||
|
"cn-hzjbp-b":
|
||||||
|
endpoint = "oss-cn-hzjbp-a-internal.aliyuncs.com"
|
||||||
|
case
|
||||||
|
"cn-shanghai-finance-1",
|
||||||
|
"cn-shenzhen-finance-1",
|
||||||
|
"cn-beijing-finance-1",
|
||||||
|
"cn-north-2-gov-1":
|
||||||
|
endpoint = fmt.Sprintf("oss-%s-internal.aliyuncs.com", region)
|
||||||
|
default:
|
||||||
|
endpoint = fmt.Sprintf("oss-%s.aliyuncs.com", region)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := oss.New(endpoint, accessKeyId, accessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package aliyunoss_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
dpAliyunOss "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-oss"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fRegion string
|
||||||
|
fBucket string
|
||||||
|
fDomain string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNOSS_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
|
flag.StringVar(&fBucket, argsPrefix+"BUCKET", "", "")
|
||||||
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v aliyun_oss_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNOSS_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNOSS_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNOSS_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNOSS_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNOSS_REGION="cn-hangzhou" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNOSS_BUCKET="your-bucket" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNOSS_DOMAIN="example.com" \
|
||||||
|
*/
|
||||||
|
func Test(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
fmt.Sprintf("BUCKET: %v", fBucket),
|
||||||
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := dpAliyunOss.New(&dpAliyunOss.AliyunOSSDeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
Bucket: fBucket,
|
||||||
|
Domain: fDomain,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
@ -34,6 +34,8 @@ type K8sSecretDeployer struct {
|
|||||||
logger deployer.Logger
|
logger deployer.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*K8sSecretDeployer)(nil)
|
||||||
|
|
||||||
func New(config *K8sSecretDeployerConfig) (*K8sSecretDeployer, error) {
|
func New(config *K8sSecretDeployerConfig) (*K8sSecretDeployer, error) {
|
||||||
return NewWithLogger(config, deployer.NewNilLogger())
|
return NewWithLogger(config, deployer.NewNilLogger())
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,13 @@ import (
|
|||||||
|
|
||||||
type LocalDeployerConfig struct {
|
type LocalDeployerConfig struct {
|
||||||
// Shell 执行环境。
|
// Shell 执行环境。
|
||||||
// 空值时默认根据操作系统决定。
|
// 零值时默认根据操作系统决定。
|
||||||
ShellEnv ShellEnvType `json:"shellEnv,omitempty"`
|
ShellEnv ShellEnvType `json:"shellEnv,omitempty"`
|
||||||
// 前置命令。
|
// 前置命令。
|
||||||
PreCommand string `json:"preCommand,omitempty"`
|
PreCommand string `json:"preCommand,omitempty"`
|
||||||
// 后置命令。
|
// 后置命令。
|
||||||
PostCommand string `json:"postCommand,omitempty"`
|
PostCommand string `json:"postCommand,omitempty"`
|
||||||
// 输出证书格式。
|
// 输出证书格式。
|
||||||
// 空值时默认为 [OUTPUT_FORMAT_PEM]。
|
|
||||||
OutputFormat OutputFormatType `json:"outputFormat,omitempty"`
|
OutputFormat OutputFormatType `json:"outputFormat,omitempty"`
|
||||||
// 输出证书文件路径。
|
// 输出证书文件路径。
|
||||||
OutputCertPath string `json:"outputCertPath,omitempty"`
|
OutputCertPath string `json:"outputCertPath,omitempty"`
|
||||||
@ -49,6 +48,8 @@ type LocalDeployer struct {
|
|||||||
logger deployer.Logger
|
logger deployer.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*LocalDeployer)(nil)
|
||||||
|
|
||||||
func New(config *LocalDeployerConfig) (*LocalDeployer, error) {
|
func New(config *LocalDeployerConfig) (*LocalDeployer, error) {
|
||||||
return NewWithLogger(config, deployer.NewNilLogger())
|
return NewWithLogger(config, deployer.NewNilLogger())
|
||||||
}
|
}
|
||||||
@ -81,7 +82,7 @@ func (d *LocalDeployer) Deploy(ctx context.Context, certPem string, privkeyPem s
|
|||||||
|
|
||||||
// 写入证书和私钥文件
|
// 写入证书和私钥文件
|
||||||
switch d.config.OutputFormat {
|
switch d.config.OutputFormat {
|
||||||
case "", OUTPUT_FORMAT_PEM:
|
case OUTPUT_FORMAT_PEM:
|
||||||
if err := fs.WriteFileString(d.config.OutputCertPath, certPem); err != nil {
|
if err := fs.WriteFileString(d.config.OutputCertPath, certPem); err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to save certificate file")
|
return nil, xerrors.Wrap(err, "failed to save certificate file")
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
dLocal "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/local"
|
dpLocal "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/local"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -60,7 +60,7 @@ func Test(t *testing.T) {
|
|||||||
fmt.Sprintf("OUTPUTKEYPATH: %v", fOutputKeyPath),
|
fmt.Sprintf("OUTPUTKEYPATH: %v", fOutputKeyPath),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := dLocal.New(&dLocal.LocalDeployerConfig{
|
deployer, err := dpLocal.New(&dpLocal.LocalDeployerConfig{
|
||||||
OutputCertPath: fOutputCertPath,
|
OutputCertPath: fOutputCertPath,
|
||||||
OutputKeyPath: fOutputKeyPath,
|
OutputKeyPath: fOutputKeyPath,
|
||||||
})
|
})
|
||||||
@ -108,8 +108,8 @@ func Test(t *testing.T) {
|
|||||||
fmt.Sprintf("PFXPASSWORD: %v", fPfxPassword),
|
fmt.Sprintf("PFXPASSWORD: %v", fPfxPassword),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := dLocal.New(&dLocal.LocalDeployerConfig{
|
deployer, err := dpLocal.New(&dpLocal.LocalDeployerConfig{
|
||||||
OutputFormat: dLocal.OUTPUT_FORMAT_PFX,
|
OutputFormat: dpLocal.OUTPUT_FORMAT_PFX,
|
||||||
OutputCertPath: fOutputCertPath,
|
OutputCertPath: fOutputCertPath,
|
||||||
OutputKeyPath: fOutputKeyPath,
|
OutputKeyPath: fOutputKeyPath,
|
||||||
PfxPassword: fPfxPassword,
|
PfxPassword: fPfxPassword,
|
||||||
@ -151,8 +151,8 @@ func Test(t *testing.T) {
|
|||||||
fmt.Sprintf("JKSSTOREPASS: %v", fJksStorepass),
|
fmt.Sprintf("JKSSTOREPASS: %v", fJksStorepass),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := dLocal.New(&dLocal.LocalDeployerConfig{
|
deployer, err := dpLocal.New(&dpLocal.LocalDeployerConfig{
|
||||||
OutputFormat: dLocal.OUTPUT_FORMAT_JKS,
|
OutputFormat: dpLocal.OUTPUT_FORMAT_JKS,
|
||||||
OutputCertPath: fOutputCertPath,
|
OutputCertPath: fOutputCertPath,
|
||||||
OutputKeyPath: fOutputKeyPath,
|
OutputKeyPath: fOutputKeyPath,
|
||||||
JksAlias: fJksAlias,
|
JksAlias: fJksAlias,
|
||||||
|
@ -18,10 +18,10 @@ import (
|
|||||||
|
|
||||||
type SshDeployerConfig struct {
|
type SshDeployerConfig struct {
|
||||||
// SSH 主机。
|
// SSH 主机。
|
||||||
// 空值时默认为 "localhost"。
|
// 零值时默认为 "localhost"。
|
||||||
SshHost string `json:"sshHost,omitempty"`
|
SshHost string `json:"sshHost,omitempty"`
|
||||||
// SSH 端口。
|
// SSH 端口。
|
||||||
// 空值时默认为 22。
|
// 零值时默认为 22。
|
||||||
SshPort int32 `json:"sshPort,omitempty"`
|
SshPort int32 `json:"sshPort,omitempty"`
|
||||||
// SSH 登录用户名。
|
// SSH 登录用户名。
|
||||||
SshUsername string `json:"sshUsername,omitempty"`
|
SshUsername string `json:"sshUsername,omitempty"`
|
||||||
@ -36,7 +36,6 @@ type SshDeployerConfig struct {
|
|||||||
// 后置命令。
|
// 后置命令。
|
||||||
PostCommand string `json:"postCommand,omitempty"`
|
PostCommand string `json:"postCommand,omitempty"`
|
||||||
// 输出证书格式。
|
// 输出证书格式。
|
||||||
// 空值时默认为 [OUTPUT_FORMAT_PEM]。
|
|
||||||
OutputFormat OutputFormatType `json:"outputFormat,omitempty"`
|
OutputFormat OutputFormatType `json:"outputFormat,omitempty"`
|
||||||
// 输出证书文件路径。
|
// 输出证书文件路径。
|
||||||
OutputCertPath string `json:"outputCertPath,omitempty"`
|
OutputCertPath string `json:"outputCertPath,omitempty"`
|
||||||
@ -61,6 +60,8 @@ type SshDeployer struct {
|
|||||||
logger deployer.Logger
|
logger deployer.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*SshDeployer)(nil)
|
||||||
|
|
||||||
func New(config *SshDeployerConfig) (*SshDeployer, error) {
|
func New(config *SshDeployerConfig) (*SshDeployer, error) {
|
||||||
return NewWithLogger(config, deployer.NewNilLogger())
|
return NewWithLogger(config, deployer.NewNilLogger())
|
||||||
}
|
}
|
||||||
@ -109,7 +110,7 @@ func (d *SshDeployer) Deploy(ctx context.Context, certPem string, privkeyPem str
|
|||||||
|
|
||||||
// 上传证书和私钥文件
|
// 上传证书和私钥文件
|
||||||
switch d.config.OutputFormat {
|
switch d.config.OutputFormat {
|
||||||
case "", OUTPUT_FORMAT_PEM:
|
case OUTPUT_FORMAT_PEM:
|
||||||
if err := writeSftpFileString(client, d.config.OutputCertPath, certPem); err != nil {
|
if err := writeSftpFileString(client, d.config.OutputCertPath, certPem); err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
dSsh "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ssh"
|
dpSsh "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -64,7 +64,7 @@ func Test(t *testing.T) {
|
|||||||
fmt.Sprintf("OUTPUTKEYPATH: %v", fOutputKeyPath),
|
fmt.Sprintf("OUTPUTKEYPATH: %v", fOutputKeyPath),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := dSsh.New(&dSsh.SshDeployerConfig{
|
deployer, err := dpSsh.New(&dpSsh.SshDeployerConfig{
|
||||||
SshHost: fSshHost,
|
SshHost: fSshHost,
|
||||||
SshPort: int32(fSshPort),
|
SshPort: int32(fSshPort),
|
||||||
SshUsername: fSshUsername,
|
SshUsername: fSshUsername,
|
||||||
|
@ -144,6 +144,7 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunCas.Cl
|
|||||||
region = "cn-hangzhou" // CAS 服务默认区域:华东一杭州
|
region = "cn-hangzhou" // CAS 服务默认区域:华东一杭州
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 接入点一览 https://help.aliyun.com/zh/ssl-certificate/developer-reference/endpoints
|
||||||
var endpoint string
|
var endpoint string
|
||||||
switch region {
|
switch region {
|
||||||
case "cn-hangzhou":
|
case "cn-hangzhou":
|
||||||
|
@ -121,6 +121,7 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunSlb.Cl
|
|||||||
region = "cn-hangzhou" // SLB 服务默认区域:华东一杭州
|
region = "cn-hangzhou" // SLB 服务默认区域:华东一杭州
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 接入点一览 https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-endpoint
|
||||||
var endpoint string
|
var endpoint string
|
||||||
switch region {
|
switch region {
|
||||||
case
|
case
|
||||||
|
Loading…
x
Reference in New Issue
Block a user