Scert/backend/internal/dnsprovider/lego_factory.go

149 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package dnsprovider
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/go-acme/lego/v5/challenge"
"github.com/go-acme/lego/v5/providers/dns/cloudflare"
"github.com/go-acme/lego/v5/providers/dns/gcloud"
"github.com/go-acme/lego/v5/providers/dns/huaweicloud"
"github.com/go-acme/lego/v5/providers/dns/route53"
"github.com/go-acme/lego/v5/providers/dns/tencentcloud"
"golang.org/x/oauth2/google"
gdns "google.golang.org/api/dns/v1"
"git.pcux.cn/xuans/Scert/backend/internal/domain"
)
type LegoFactory struct{}
func NewLegoFactory() LegoFactory {
return LegoFactory{}
}
func (LegoFactory) Create(providerID string, credentials map[string]string) (challenge.Provider, error) {
providerID = strings.ToLower(strings.TrimSpace(providerID))
descriptor, err := GetDescriptor(providerID)
if err != nil {
return nil, err
}
if !descriptor.Available {
return nil, fmt.Errorf("%w:%s", ErrProviderUnavailable, descriptor.Name)
}
cleaned, err := ValidateCredentials(providerID, credentials)
if err != nil {
return nil, err
}
switch providerID {
case CNAME:
delegatedDomain := normalizeProviderDomain(cleaned["delegated_domain"])
if delegatedDomain == "" || strings.HasPrefix(delegatedDomain, "*.") || !domain.IsValidName(delegatedDomain) {
return nil, ErrInvalidDelegatedDomain
}
delegatedProvider := strings.ToLower(strings.TrimSpace(cleaned["delegated_provider"]))
if delegatedProvider == CNAME || delegatedProvider == Other || delegatedProvider == "" {
return nil, fmt.Errorf("%w: %s", ErrDelegatedProviderUnavailable, delegatedProvider)
}
var delegatedCredentials map[string]string
if err := json.Unmarshal([]byte(cleaned["delegated_credentials_json"]), &delegatedCredentials); err != nil {
return nil, fmt.Errorf("%w: %v", ErrInvalidDelegatedCredentials, err)
}
if delegatedCredentials == nil {
return nil, ErrInvalidDelegatedCredentials
}
target, err := (LegoFactory{}).Create(delegatedProvider, delegatedCredentials)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrDelegatedProviderUnavailable, err)
}
return newCNAMEDelegationProvider(delegatedDomain, target)
case Cloudflare:
cfg := cloudflare.NewDefaultConfig()
cfg.AuthToken = cleaned["api_token"]
cfg.ZoneToken = cleaned["zone_token"]
if cfg.ZoneToken == "" {
cfg.ZoneToken = cfg.AuthToken
}
provider, err := cloudflare.NewDNSProviderConfig(cfg)
if err != nil {
return nil, fmt.Errorf("创建 Cloudflare DNS Provider 失败:%w", err)
}
return provider, nil
case Aliyun:
return newScopedAliyunProvider(cleaned)
case DNSPod, TencentCloud:
cfg := tencentcloud.NewDefaultConfig()
cfg.SecretID = cleaned["secret_id"]
cfg.SecretKey = cleaned["secret_key"]
cfg.SessionToken = cleaned["session_token"]
provider, err := tencentcloud.NewDNSProviderConfig(cfg)
if err != nil {
return nil, fmt.Errorf("创建腾讯云 DNSPod Provider 失败:%w", err)
}
return provider, nil
case HuaweiCloud:
cfg := huaweicloud.NewDefaultConfig()
cfg.AccessKeyID = cleaned["access_key_id"]
cfg.SecretAccessKey = cleaned["secret_access_key"]
cfg.Region = cleaned["region"]
provider, err := huaweicloud.NewDNSProviderConfig(cfg)
if err != nil {
return nil, fmt.Errorf("创建华为云 DNS Provider 失败:%w", err)
}
return provider, nil
case Route53:
cfg := route53.NewDefaultConfig()
cfg.AccessKeyID = cleaned["access_key_id"]
cfg.SecretAccessKey = cleaned["secret_access_key"]
cfg.SessionToken = cleaned["session_token"]
cfg.Region = cleaned["region"]
cfg.HostedZoneID = cleaned["hosted_zone_id"]
provider, err := route53.NewDNSProviderConfig(cfg)
if err != nil {
return nil, fmt.Errorf("创建 AWS Route 53 DNS Provider 失败:%w", err)
}
return provider, nil
case GoogleCloud:
serviceAccountJSON := strings.TrimSpace(cleaned["service_account_json"])
var serviceAccount struct {
ProjectID string `json:"project_id"`
}
if err := json.Unmarshal([]byte(serviceAccountJSON), &serviceAccount); err != nil {
return nil, fmt.Errorf("创建 Google Cloud DNS Provider 失败:服务账号 JSON 格式无效:%w", err)
}
if serviceAccount.ProjectID == "" {
serviceAccount.ProjectID = cleaned["project"]
if serviceAccount.ProjectID == "" {
return nil, fmt.Errorf("创建 Google Cloud DNS Provider 失败:项目 ID 不能为空")
}
var raw map[string]any
if err := json.Unmarshal([]byte(serviceAccountJSON), &raw); err != nil {
return nil, fmt.Errorf("创建 Google Cloud DNS Provider 失败:服务账号 JSON 格式无效:%w", err)
}
raw["project_id"] = serviceAccount.ProjectID
data, err := json.Marshal(raw)
if err != nil {
return nil, fmt.Errorf("创建 Google Cloud DNS Provider 失败:编码服务账号 JSON 失败:%w", err)
}
serviceAccountJSON = string(data)
}
jwtConfig, err := google.JWTConfigFromJSON([]byte(serviceAccountJSON), gdns.NdevClouddnsReadwriteScope)
if err != nil {
return nil, fmt.Errorf("创建 Google Cloud DNS Provider 失败:服务账号凭据无效:%w", err)
}
cfg := gcloud.NewDefaultConfig()
cfg.Project = serviceAccount.ProjectID
cfg.ZoneID = cleaned["zone_id"]
cfg.HTTPClient = jwtConfig.Client(context.Background())
provider, err := gcloud.NewDNSProviderConfig(cfg)
if err != nil {
return nil, fmt.Errorf("创建 Google Cloud DNS Provider 失败:%w", err)
}
return provider, nil
default:
return nil, fmt.Errorf("%w:%s", ErrProviderUnavailable, descriptor.Name)
}
}