mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-08 05:29:51 +00:00
commit
28647d6902
@ -138,7 +138,7 @@ The following hosting providers are supported:
|
||||
| [Doge Cloud](https://www.dogecloud.com/) | Supports deployment to Doge Cloud CDN |
|
||||
| [UCloud](https://www.ucloud-global.com/) | Supports deployment to UCloud US3, UCDN |
|
||||
| [SafeLine](https://waf.chaitin.com/) | Supports deployment to SafeLine WAF |
|
||||
| [BaoTa Panel](https://www.bt.cn/) | Supports deployment to BaoTa Panel sites |
|
||||
| [aaPanel](https://www.aapanel.com/) | Supports deployment to aaPanel (aka BaoTaPanel) sites |
|
||||
| [AWS](https://aws.amazon.com/) | Supports deployment to AWS CloudFront |
|
||||
| [BytePlus](https://www.byteplus.com/) | Supports deployment to BytePlus CDN |
|
||||
| [CacheFly](https://www.cachefly.com/) | Supports deployment to CacheFly CDN |
|
||||
|
8
go.mod
8
go.mod
@ -51,12 +51,16 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
|
||||
github.com/G-Core/gcorelabscdn-go v1.0.26 // indirect
|
||||
github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1 // indirect
|
||||
github.com/alibabacloud-go/tea-fileform v1.1.1 // indirect
|
||||
|
13
go.sum
13
go.sum
@ -54,10 +54,17 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WW
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0 h1:btEsytNrA4TG3edZnnUnzOz8W2MjOd6Bu3/7xyOXSOY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0/go.mod h1:5SlTxxL1U4LLipEr7pAbnu6Ck5y3aIEu4L/tVbGmpsY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw=
|
||||
@ -68,10 +75,16 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourceg
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1 h1:HUJQzFYTv7t3V1dxPms52eEgl0l9xCNqutDrY45Lvmw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1/go.mod h1:ig/8nSkzmfxm5QGeIy5JYIEj8JEFy5JxvY3OB1YNRC4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
|
@ -1,13 +1,12 @@
|
||||
package azuredns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/providers/dns/azuredns"
|
||||
|
||||
azcommon "github.com/usual2970/certimate/internal/pkg/vendors/azure-sdk/common"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
@ -29,16 +28,11 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider,
|
||||
providerConfig.ClientID = config.ClientId
|
||||
providerConfig.ClientSecret = config.ClientSecret
|
||||
if config.CloudName != "" {
|
||||
switch strings.ToLower(config.CloudName) {
|
||||
case "default", "public", "cloud", "azurecloud":
|
||||
providerConfig.Environment = cloud.AzurePublic
|
||||
case "usgovernment", "azureusgovernment":
|
||||
providerConfig.Environment = cloud.AzureGovernment
|
||||
case "china", "chinacloud", "azurechina", "azurechinacloud":
|
||||
providerConfig.Environment = cloud.AzureChina
|
||||
default:
|
||||
return nil, fmt.Errorf("azuredns: unknown environment %s", config.CloudName)
|
||||
env, err := azcommon.GetCloudEnvironmentConfiguration(config.CloudName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
providerConfig.Environment = env
|
||||
}
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
|
@ -47,6 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||
}
|
||||
|
||||
return &DeployerProvider{
|
||||
config: config,
|
||||
logger: logger.NewNilLogger(),
|
||||
sdkClient: client,
|
||||
sslUploader: uploader,
|
||||
|
@ -60,6 +60,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||
|
||||
return &DeployerProvider{
|
||||
config: config,
|
||||
logger: logger.NewNilLogger(),
|
||||
sdkClient: client,
|
||||
sslUploader: uploader,
|
||||
}, nil
|
||||
|
@ -2,14 +2,13 @@
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
aws "github.com/aws/aws-sdk-go-v2/aws"
|
||||
awsCfg "github.com/aws/aws-sdk-go-v2/config"
|
||||
awsCred "github.com/aws/aws-sdk-go-v2/credentials"
|
||||
awsAcm "github.com/aws/aws-sdk-go-v2/service/acm"
|
||||
xerrors "github.com/pkg/errors"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||
"github.com/usual2970/certimate/internal/pkg/utils/certs"
|
||||
@ -54,13 +53,74 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 生成 AWS 所需的服务端证书和证书链参数
|
||||
// 生成 AWS 业务参数
|
||||
scertPem, _ := certs.ConvertCertificateToPEM(certX509)
|
||||
bcertPem := certPem
|
||||
|
||||
// 生成新证书名(需符合 AWS 命名规则)
|
||||
var certId, certName string
|
||||
certName = fmt.Sprintf("certimate_%d", time.Now().UnixMilli())
|
||||
// 获取证书列表,避免重复上传
|
||||
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListCertificates.html
|
||||
listCertificatesNextToken := new(string)
|
||||
listCertificatesMaxItems := int32(1000)
|
||||
for {
|
||||
listCertificatesReq := &awsAcm.ListCertificatesInput{
|
||||
NextToken: listCertificatesNextToken,
|
||||
MaxItems: aws.Int32(listCertificatesMaxItems),
|
||||
}
|
||||
listCertificatesResp, err := u.sdkClient.ListCertificates(context.TODO(), listCertificatesReq)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'acm.ListCertificates'")
|
||||
}
|
||||
|
||||
for _, certSummary := range listCertificatesResp.CertificateSummaryList {
|
||||
// 先对比证书有效期
|
||||
if certSummary.NotBefore == nil || !certSummary.NotBefore.Equal(certX509.NotBefore) {
|
||||
continue
|
||||
}
|
||||
if certSummary.NotAfter == nil || !certSummary.NotAfter.Equal(certX509.NotAfter) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 再对比证书多域名
|
||||
if !slices.Equal(certX509.DNSNames, certSummary.SubjectAlternativeNameSummaries) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 最后对比证书内容
|
||||
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListTagsForCertificate.html
|
||||
getCertificateReq := &awsAcm.GetCertificateInput{
|
||||
CertificateArn: certSummary.CertificateArn,
|
||||
}
|
||||
getCertificateResp, err := u.sdkClient.GetCertificate(context.TODO(), getCertificateReq)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'acm.GetCertificate'")
|
||||
} else {
|
||||
oldCertPem := aws.ToString(getCertificateResp.CertificateChain)
|
||||
if oldCertPem == "" {
|
||||
oldCertPem = aws.ToString(getCertificateResp.Certificate)
|
||||
}
|
||||
|
||||
oldCertX509, err := certs.ParseCertificateFromPEM(oldCertPem)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !certs.EqualCertificate(certX509, oldCertX509) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
||||
return &uploader.UploadResult{
|
||||
CertId: *certSummary.CertificateArn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if listCertificatesResp.NextToken == nil || len(listCertificatesResp.CertificateSummaryList) < int(listCertificatesMaxItems) {
|
||||
break
|
||||
} else {
|
||||
listCertificatesNextToken = listCertificatesResp.NextToken
|
||||
}
|
||||
}
|
||||
|
||||
// 导入证书
|
||||
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ImportCertificate.html
|
||||
@ -74,10 +134,8 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'acm.ImportCertificate'")
|
||||
}
|
||||
|
||||
certId = *importCertificateResp.CertificateArn
|
||||
return &uploader.UploadResult{
|
||||
CertId: certId,
|
||||
CertName: certName,
|
||||
CertId: *importCertificateResp.CertificateArn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,181 @@
|
||||
package azurekeyvault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates"
|
||||
xerrors "github.com/pkg/errors"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||
"github.com/usual2970/certimate/internal/pkg/utils/certs"
|
||||
azcommon "github.com/usual2970/certimate/internal/pkg/vendors/azure-sdk/common"
|
||||
)
|
||||
|
||||
type UploaderConfig struct {
|
||||
// Azure TenantId。
|
||||
TenantId string `json:"tenantId"`
|
||||
// Azure ClientId。
|
||||
ClientId string `json:"clientId"`
|
||||
// Azure ClientSecret。
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
// Azure 主权云环境。
|
||||
CloudName string `json:"cloudName,omitempty"`
|
||||
// Key Vault 名称。
|
||||
KeyVaultName string `json:"keyvaultName"`
|
||||
}
|
||||
|
||||
type UploaderProvider struct {
|
||||
config *UploaderConfig
|
||||
sdkClient *azcertificates.Client
|
||||
}
|
||||
|
||||
var _ uploader.Uploader = (*UploaderProvider)(nil)
|
||||
|
||||
func NewUploader(config *UploaderConfig) (*UploaderProvider, error) {
|
||||
if config == nil {
|
||||
panic("config is nil")
|
||||
}
|
||||
|
||||
client, err := createSdkClient(config.TenantId, config.ClientId, config.ClientSecret, config.CloudName, config.KeyVaultName)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
||||
}
|
||||
|
||||
return &UploaderProvider{
|
||||
config: config,
|
||||
sdkClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) {
|
||||
// 解析证书内容
|
||||
certX509, err := certs.ParseCertificateFromPEM(certPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 生成 Azure 业务参数
|
||||
const TAG_CERTCN = "certimate/cert-cn"
|
||||
const TAG_CERTSN = "certimate/cert-sn"
|
||||
certCN := certX509.Subject.CommonName
|
||||
certSN := certX509.SerialNumber.Text(16)
|
||||
|
||||
// 获取证书列表,避免重复上传
|
||||
// REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/get-certificates/get-certificates
|
||||
listCertificatesPager := u.sdkClient.NewListCertificatesPager(nil)
|
||||
for listCertificatesPager.More() {
|
||||
page, err := listCertificatesPager.NextPage(context.TODO())
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'keyvault.GetCertificates'")
|
||||
}
|
||||
|
||||
for _, certItem := range page.Value {
|
||||
// 先对比证书有效期
|
||||
if certItem.Attributes == nil {
|
||||
continue
|
||||
}
|
||||
if certItem.Attributes.NotBefore == nil || !certItem.Attributes.NotBefore.Equal(certX509.NotBefore) {
|
||||
continue
|
||||
}
|
||||
if certItem.Attributes.Expires == nil || !certItem.Attributes.Expires.Equal(certX509.NotAfter) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 再对比 Tag 中的通用名称
|
||||
if v, ok := certItem.Tags[TAG_CERTCN]; !ok || v == nil {
|
||||
continue
|
||||
} else if *v != certCN {
|
||||
continue
|
||||
}
|
||||
|
||||
// 再对比 Tag 中的序列号
|
||||
if v, ok := certItem.Tags[TAG_CERTSN]; !ok || v == nil {
|
||||
continue
|
||||
} else if *v != certSN {
|
||||
continue
|
||||
}
|
||||
|
||||
// 最后对比证书内容
|
||||
getCertificateResp, err := u.sdkClient.GetCertificate(context.TODO(), certItem.ID.Name(), certItem.ID.Version(), nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'keyvault.GetCertificate'")
|
||||
} else {
|
||||
oldCertX509, err := x509.ParseCertificate(getCertificateResp.CER)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !certs.EqualCertificate(certX509, oldCertX509) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
||||
return &uploader.UploadResult{
|
||||
CertId: string(*certItem.ID),
|
||||
CertName: certItem.ID.Name(),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 生成新证书名(需符合 Azure 命名规则)
|
||||
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
||||
|
||||
// 导入证书
|
||||
// REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/import-certificate/import-certificate
|
||||
importCertificateParams := azcertificates.ImportCertificateParameters{
|
||||
Base64EncodedCertificate: to.Ptr(certPem),
|
||||
CertificatePolicy: &azcertificates.CertificatePolicy{
|
||||
SecretProperties: &azcertificates.SecretProperties{
|
||||
ContentType: to.Ptr("application/x-pem-file"),
|
||||
},
|
||||
},
|
||||
Tags: map[string]*string{
|
||||
TAG_CERTCN: to.Ptr(certCN),
|
||||
TAG_CERTSN: to.Ptr(certSN),
|
||||
},
|
||||
}
|
||||
importCertificateResp, err := u.sdkClient.ImportCertificate(context.TODO(), certName, importCertificateParams, nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'keyvault.ImportCertificate'")
|
||||
}
|
||||
|
||||
return &uploader.UploadResult{
|
||||
CertId: string(*importCertificateResp.ID),
|
||||
CertName: certName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createSdkClient(tenantId, clientId, clientSecret, cloudName, keyvaultName string) (*azcertificates.Client, error) {
|
||||
env, err := azcommon.GetCloudEnvironmentConfiguration(cloudName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientOptions := azcore.ClientOptions{Cloud: env}
|
||||
|
||||
credential, err := azidentity.NewClientSecretCredential(tenantId, clientId, clientSecret,
|
||||
&azidentity.ClientSecretCredentialOptions{ClientOptions: clientOptions})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endpoint := fmt.Sprintf("https://%s.vault.azure.net", keyvaultName)
|
||||
if azcommon.IsEnvironmentGovernment(cloudName) {
|
||||
endpoint = fmt.Sprintf("https://%s.vault.usgovcloudapi.net", keyvaultName)
|
||||
} else if azcommon.IsEnvironmentChina(cloudName) {
|
||||
endpoint = fmt.Sprintf("https://%s.vault.azure.cn", keyvaultName)
|
||||
}
|
||||
|
||||
client, err := azcertificates.NewClient(endpoint, credential, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
@ -76,31 +76,31 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe
|
||||
}
|
||||
|
||||
for _, certDetail := range describeCertsResp.Result.CertListDetails {
|
||||
// 先尝试匹配 CN
|
||||
// 先对比证书通用名称
|
||||
if !strings.EqualFold(certX509.Subject.CommonName, certDetail.CommonName) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 再尝试匹配 SAN
|
||||
// 再对比证书多域名
|
||||
if !slices.Equal(certX509.DNSNames, certDetail.DnsNames) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 再尝试匹配证书有效期
|
||||
// 再对比证书有效期
|
||||
oldCertNotBefore, _ := time.Parse(time.RFC3339, certDetail.StartTime)
|
||||
oldCertNotAfter, _ := time.Parse(time.RFC3339, certDetail.EndTime)
|
||||
if !certX509.NotBefore.Equal(oldCertNotBefore) || !certX509.NotAfter.Equal(oldCertNotAfter) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 最后尝试匹配私钥摘要
|
||||
// 最后对比私钥摘要
|
||||
newKeyDigest := sha256.Sum256([]byte(privkeyPem))
|
||||
newKeyDigestHex := hex.EncodeToString(newKeyDigest[:])
|
||||
if !strings.EqualFold(newKeyDigestHex, certDetail.Digest) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果以上都匹配,则视为已存在相同证书,直接返回已有的证书信息
|
||||
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
||||
return &uploader.UploadResult{
|
||||
CertId: certDetail.CertId,
|
||||
CertName: certDetail.CertName,
|
||||
|
@ -121,8 +121,8 @@ func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string) (re
|
||||
|
||||
if getCertificateListResp.CertificateList != nil {
|
||||
for _, certInfo := range getCertificateListResp.CertificateList {
|
||||
// 优刻得未提供可唯一标识证书的字段,只能通过多个字段尝试匹配来判断是否为同一证书
|
||||
// 先分别匹配证书的域名、品牌、有效期,再匹配签名算法
|
||||
// 优刻得未提供可唯一标识证书的字段,只能通过多个字段尝试对比来判断是否为同一证书
|
||||
// 先分别对比证书的多域名、品牌、有效期,再对比签名算法
|
||||
|
||||
if len(certX509.DNSNames) == 0 || certInfo.Domains != strings.Join(certX509.DNSNames, ",") {
|
||||
continue
|
||||
|
@ -24,3 +24,29 @@ func IsNil(obj any) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 将对象转换为指针。
|
||||
//
|
||||
// 入参:
|
||||
// - 待转换的对象。
|
||||
//
|
||||
// 出参:
|
||||
// - 返回对象的指针。
|
||||
func ToPtr[T any](v T) (p *T) {
|
||||
return &v
|
||||
}
|
||||
|
||||
// 将指针转换为对象。
|
||||
//
|
||||
// 入参:
|
||||
// - 待转换的指针。
|
||||
//
|
||||
// 出参:
|
||||
// - 返回指针指向的对象。如果指针为空,则返回对象的零值。
|
||||
func ToObj[T any](p *T) (v T) {
|
||||
if p == nil {
|
||||
return v
|
||||
}
|
||||
|
||||
return *p
|
||||
}
|
||||
|
47
internal/pkg/vendors/azure-sdk/common/config.go
vendored
Normal file
47
internal/pkg/vendors/azure-sdk/common/config.go
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
)
|
||||
|
||||
func IsEnvironmentPublic(env string) bool {
|
||||
switch strings.ToLower(env) {
|
||||
case "", "default", "public", "azurecloud":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func IsEnvironmentGovernment(env string) bool {
|
||||
switch strings.ToLower(env) {
|
||||
case "usgovernment", "government", "azureusgovernment", "azuregovernment":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func IsEnvironmentChina(env string) bool {
|
||||
switch strings.ToLower(env) {
|
||||
case "china", "chinacloud", "azurechina", "azurechinacloud":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func GetCloudEnvironmentConfiguration(env string) (cloud.Configuration, error) {
|
||||
if IsEnvironmentPublic(env) {
|
||||
return cloud.AzurePublic, nil
|
||||
} else if IsEnvironmentGovernment(env) {
|
||||
return cloud.AzureGovernment, nil
|
||||
} else if IsEnvironmentChina(env) {
|
||||
return cloud.AzureChina, nil
|
||||
}
|
||||
|
||||
return cloud.Configuration{}, fmt.Errorf("unknown azure cloud environment %s", env)
|
||||
}
|
23
internal/pkg/vendors/btpanel-sdk/client.go
vendored
23
internal/pkg/vendors/btpanel-sdk/client.go
vendored
@ -5,6 +5,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -45,24 +46,36 @@ func (c *Client) generateSignature(timestamp string) string {
|
||||
func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, error) {
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
data := make(map[string]any)
|
||||
data := make(map[string]string)
|
||||
if params != nil {
|
||||
temp := make(map[string]any)
|
||||
jsonb, _ := json.Marshal(params)
|
||||
json.Unmarshal(jsonb, &temp)
|
||||
for k, v := range temp {
|
||||
if v != nil {
|
||||
data[k] = v
|
||||
switch reflect.Indirect(reflect.ValueOf(v)).Kind() {
|
||||
case reflect.String:
|
||||
data[k] = v.(string)
|
||||
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
||||
data[k] = fmt.Sprintf("%v", v)
|
||||
default:
|
||||
if t, ok := v.(time.Time); ok {
|
||||
data[k] = t.Format(time.RFC3339)
|
||||
} else {
|
||||
jbytes, _ := json.Marshal(v)
|
||||
data[k] = string(jbytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
data["request_time"] = timestamp
|
||||
data["request_time"] = fmt.Sprintf("%d", timestamp)
|
||||
data["request_token"] = c.generateSignature(fmt.Sprintf("%d", timestamp))
|
||||
|
||||
url := c.apiHost + path
|
||||
req := c.client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(data)
|
||||
SetHeader("Content-Type", "application/x-www-form-urlencoded").
|
||||
SetFormData(data)
|
||||
resp, err := req.Post(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("baota api error: failed to send request: %w", err)
|
||||
|
@ -67,12 +67,12 @@
|
||||
"access.form.baiducloud_secret_access_key.tooltip": "For more information, see <a href=\"https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en\" target=\"_blank\">https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en</a>",
|
||||
"access.form.baishan_api_token.label": "Baishan Cloud API token",
|
||||
"access.form.baishan_api_token.placeholder": "Please enter Baishan Cloud API token",
|
||||
"access.form.baotapanel_api_url.label": "BaoTa Panel URL",
|
||||
"access.form.baotapanel_api_url.placeholder": "Please enter BaoTa Panel URL",
|
||||
"access.form.baotapanel_api_url.label": "aaPanel URL",
|
||||
"access.form.baotapanel_api_url.placeholder": "Please enter aaPanel URL",
|
||||
"access.form.baotapanel_api_url.tooltip": "For more information, see <a href=\"https://www.bt.cn/bbs/thread-20376-1-1.html\" target=\"_blank\">https://www.bt.cn/bbs/thread-20376-1-1.html</a>",
|
||||
"access.form.baotapanel_api_key.label": "BaoTa Panel API key",
|
||||
"access.form.baotapanel_api_key.placeholder": "Please enter BaoTa Panel API key",
|
||||
"access.form.baotapanel_api_key.tooltip": "For more information, see <a href=\"https://www.bt.cn/bbs/thread-113890-1-1.html\" target=\"_blank\">https://www.bt.cn/bbs/thread-113890-1-1.html</a>",
|
||||
"access.form.baotapanel_api_key.label": "aaPanel API key",
|
||||
"access.form.baotapanel_api_key.placeholder": "Please enter aaPanel API key",
|
||||
"access.form.baotapanel_api_key.tooltip": "For more information, see <a href=\"https://www.bt.cn/bbs/thread-20376-1-1.html\" target=\"_blank\">https://www.bt.cn/bbs/thread-20376-1-1.html</a>",
|
||||
"access.form.byteplus_access_key.label": "BytePlus AccessKey",
|
||||
"access.form.byteplus_access_key.placeholder": "Please enter BytePlus AccessKey",
|
||||
"access.form.byteplus_access_key.tooltip": "For more information, see <a href=\"https://docs.byteplus.com/en/docs/byteplus-platform/docs-managing-keys\" target=\"_blank\">https://docs.byteplus.com/en/docs/byteplus-platform/docs-managing-keys</a>",
|
||||
|
@ -26,9 +26,9 @@
|
||||
"provider.baiducloud.dns": "Baidu Cloud - DNS (Domain Name Service)",
|
||||
"provider.baishan": "Baishan",
|
||||
"provider.baishan.cdn": "Baishan - CDN (Content Delivery Network)",
|
||||
"provider.baotapanel": "BaoTa Panel",
|
||||
"provider.baotapanel.console": "BaoTa Panel - Console",
|
||||
"provider.baotapanel.site": "BaoTa Panel - Site",
|
||||
"provider.baotapanel": "aaPanel (aka BaoTaPanel)",
|
||||
"provider.baotapanel.console": "aaPanel (aka BaoTaPanel) - Console",
|
||||
"provider.baotapanel.site": "aaPanel (aka BaoTaPanel) - Site",
|
||||
"provider.byteplus": "BytePlus",
|
||||
"provider.byteplus.cdn": "BytePlus - CDN (Content Delivery Network)",
|
||||
"provider.cachefly": "CacheFly",
|
||||
|
@ -203,19 +203,19 @@
|
||||
"workflow_node.deploy.form.baishan_cdn_domain.placeholder": "Please enter Baishan CDN domain name",
|
||||
"workflow_node.deploy.form.baishan_cdn_domain.tooltip": "For more information, see <a href=\"https://cdnx.console.baishan.com\" target=\"_blank\">https://cdnx.console.baishan.com</a>",
|
||||
"workflow_node.deploy.form.baotapanel_console_auto_restart.label": "Auto restart after deployment",
|
||||
"workflow_node.deploy.form.baotapanel_site_type.label": "BaoTa Panel site type",
|
||||
"workflow_node.deploy.form.baotapanel_site_type.placeholder": "Please select BaoTa Panel site type",
|
||||
"workflow_node.deploy.form.baotapanel_site_type.label": "aaPanel site type",
|
||||
"workflow_node.deploy.form.baotapanel_site_type.placeholder": "Please select aaPanel site type",
|
||||
"workflow_node.deploy.form.baotapanel_site_type.option.php.label": "PHP sites",
|
||||
"workflow_node.deploy.form.baotapanel_site_type.option.other.label": "Other sites",
|
||||
"workflow_node.deploy.form.baotapanel_site_name.label": "BaoTa Panel site name",
|
||||
"workflow_node.deploy.form.baotapanel_site_name.placeholder": "Please enter BaoTa Panel site name",
|
||||
"workflow_node.deploy.form.baotapanel_site_name.label": "aaPanel site name",
|
||||
"workflow_node.deploy.form.baotapanel_site_name.placeholder": "Please enter aaPanel site name",
|
||||
"workflow_node.deploy.form.baotapanel_site_name.tooltip": "Usually equal to the website domain name.",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.label": "BaoTa Panel site names",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.placeholder": "Please enter BaoTa Panel site names (separated by semicolons)",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid": "Please enter a valid BaoTa Panel site name",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.label": "aaPanel site names",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.placeholder": "Please enter aaPanel site names (separated by semicolons)",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid": "Please enter a valid aaPanel site name",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.tooltip": "Usually equal to the websites domain name.",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title": "Change BaoTa Panel site names",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder": "Please enter BaoTa Panel site name",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title": "Change aaPanel site names",
|
||||
"workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder": "Please enter aaPanel site name",
|
||||
"workflow_node.deploy.form.byteplus_cdn_domain.label": "BytePlus CDN domain",
|
||||
"workflow_node.deploy.form.byteplus_cdn_domain.placeholder": "Please enter BytePlus CDN domain name",
|
||||
"workflow_node.deploy.form.byteplus_cdn_domain.tooltip": "For more information, see <a href=\"https://console.byteplus.com/cdn\" target=\"_blank\">https://console.byteplus.com/cdn</a>",
|
||||
|
Loading…
x
Reference in New Issue
Block a user