Merge branch 'main' into feat/cloud-load-balance

This commit is contained in:
Fu Diwei
2024-10-24 22:45:25 +08:00
41 changed files with 1267 additions and 3547 deletions

View File

@@ -19,6 +19,7 @@ const (
targetAliyunCDN = "aliyun-cdn"
targetAliyunESA = "aliyun-dcdn"
targetTencentCDN = "tencent-cdn"
targetTencentCLB = "tencent-clb"
targetTencentCOS = "tencent-cos"
targetHuaweiCloudCDN = "huaweicloud-cdn"
targetHuaweiCloudELB = "huaweicloud-elb"
@@ -107,6 +108,8 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
return NewAliyunESADeployer(option)
case targetTencentCDN:
return NewTencentCDNDeployer(option)
case targetTencentCLB:
return NewTencentCLBDeployer(option)
case targetTencentCOS:
return NewTencentCOSDeployer(option)
case targetHuaweiCloudCDN:

View File

@@ -4,12 +4,15 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
corev1 "k8s.io/api/core/v1"
k8sMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/utils/x509"
)
type K8sSecretDeployer struct {
@@ -43,7 +46,7 @@ func (d *K8sSecretDeployer) Deploy(ctx context.Context) error {
return err
}
d.infos = append(d.infos, toStr("kubeClient 创建成功", nil))
d.infos = append(d.infos, toStr("kubeClient create success.", nil))
namespace := getDeployString(d.option.DeployConfig, "namespace")
if namespace == "" {
@@ -65,36 +68,61 @@ func (d *K8sSecretDeployer) Deploy(ctx context.Context) error {
namespace = "tls.key"
}
// 获取 Secret 实例
secret, err := client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, k8sMetaV1.GetOptions{})
certificate, err := x509.ParseCertificateFromPEM(d.option.Certificate.Certificate)
if err != nil {
return fmt.Errorf("failed to get k8s secret: %w", err)
return fmt.Errorf("failed to parse certificate: %w", err)
}
// 更新 Secret Data
secret.Data[secretDataKeyForCrt] = []byte(d.option.Certificate.Certificate)
secret.Data[secretDataKeyForKey] = []byte(d.option.Certificate.PrivateKey)
_, err = client.CoreV1().Secrets(namespace).Update(context.TODO(), secret, k8sMetaV1.UpdateOptions{})
secretPayload := corev1.Secret{
TypeMeta: k8sMetaV1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: k8sMetaV1.ObjectMeta{
Name: secretName,
Annotations: map[string]string{
"certimate/domains": d.option.Domain,
"certimate/alt-names": strings.Join(certificate.DNSNames, ","),
"certimate/common-name": certificate.Subject.CommonName,
"certimate/issuer-organization": strings.Join(certificate.Issuer.Organization, ","),
},
},
Type: corev1.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 = client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, k8sMetaV1.GetOptions{})
if err != nil {
_, err = client.CoreV1().Secrets(namespace).Create(context.TODO(), &secretPayload, k8sMetaV1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to create k8s secret: %w", err)
} else {
d.infos = append(d.infos, toStr("Certificate has been created in K8s Secret", nil))
return nil
}
}
// 更新 Secret 实例
_, err = client.CoreV1().Secrets(namespace).Update(ctx, &secretPayload, k8sMetaV1.UpdateOptions{})
if err != nil {
return fmt.Errorf("failed to update k8s secret: %w", err)
}
d.infos = append(d.infos, toStr("证书已更新到 K8s Secret", nil))
d.infos = append(d.infos, toStr("Certificate has been updated to K8s Secret", nil))
return nil
}
func (d *K8sSecretDeployer) createClient(access *domain.KubernetesAccess) (*kubernetes.Clientset, error) {
kubeConfig, err := clientcmd.Load([]byte(access.KubeConfig))
kubeConfig, err := clientcmd.NewClientConfigFromBytes([]byte(access.KubeConfig))
if err != nil {
return nil, err
}
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: ""},
&clientcmd.ConfigOverrides{CurrentContext: kubeConfig.CurrentContext},
)
config, err := clientConfig.ClientConfig()
config, err := kubeConfig.ClientConfig()
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,117 @@
package deployer
import (
"context"
"encoding/json"
"fmt"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
ssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205"
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/utils/rand"
)
type TencentCLBDeployer struct {
option *DeployerOption
credential *common.Credential
infos []string
}
func NewTencentCLBDeployer(option *DeployerOption) (Deployer, error) {
access := &domain.TencentAccess{}
if err := json.Unmarshal([]byte(option.Access), access); err != nil {
return nil, fmt.Errorf("failed to unmarshal tencent access: %w", err)
}
credential := common.NewCredential(
access.SecretId,
access.SecretKey,
)
return &TencentCLBDeployer{
option: option,
credential: credential,
infos: make([]string, 0),
}, nil
}
func (d *TencentCLBDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
}
func (d *TencentCLBDeployer) GetInfo() []string {
return d.infos
}
func (d *TencentCLBDeployer) Deploy(ctx context.Context) error {
// 上传证书
certId, err := d.uploadCert()
if err != nil {
return fmt.Errorf("failed to upload certificate: %w", err)
}
d.infos = append(d.infos, toStr("上传证书", certId))
if err := d.deploy(certId); err != nil {
return fmt.Errorf("failed to deploy: %w", err)
}
return nil
}
func (d *TencentCLBDeployer) uploadCert() (string, error) {
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "ssl.tencentcloudapi.com"
client, _ := ssl.NewClient(d.credential, "", cpf)
request := ssl.NewUploadCertificateRequest()
request.CertificatePublicKey = common.StringPtr(d.option.Certificate.Certificate)
request.CertificatePrivateKey = common.StringPtr(d.option.Certificate.PrivateKey)
request.Alias = common.StringPtr(d.option.Domain + "_" + rand.RandStr(6))
request.Repeatable = common.BoolPtr(false)
response, err := client.UploadCertificate(request)
if err != nil {
return "", fmt.Errorf("failed to upload certificate: %w", err)
}
return *response.Response.CertificateId, nil
}
func (d *TencentCLBDeployer) deploy(certId string) error {
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "ssl.tencentcloudapi.com"
// 实例化要请求产品的client对象,clientProfile是可选的
client, _ := ssl.NewClient(d.credential, getDeployString(d.option.DeployConfig, "region"), cpf)
// 实例化一个请求对象,每个接口都会对应一个request对象
request := ssl.NewDeployCertificateInstanceRequest()
request.CertificateId = common.StringPtr(certId)
request.ResourceType = common.StringPtr("clb")
request.Status = common.Int64Ptr(1)
clbId := getDeployString(d.option.DeployConfig, "clbId")
lsnId := getDeployString(d.option.DeployConfig, "lsnId")
domain := getDeployString(d.option.DeployConfig, "domain")
if(domain == ""){
// 未开启SNI只需要精确到监听器
request.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s", clbId, lsnId)})
}else{
// 开启SNI需要精确到域名支持泛域名
request.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s|%s", clbId, lsnId, domain)})
}
// 返回的resp是一个DeployCertificateInstanceResponse的实例与请求对象对应
resp, err := client.DeployCertificateInstance(request)
if err != nil {
return fmt.Errorf("failed to deploy certificate: %w", err)
}
d.infos = append(d.infos, toStr("部署证书", resp.Response))
return nil
}