mirror of
https://github.com/usual2970/certimate.git
synced 2025-10-05 22:14:53 +00:00
feat: add jdcloud dns-01 applicant
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
||||
pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname"
|
||||
pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy"
|
||||
pHuaweiCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud"
|
||||
pJDCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud"
|
||||
pNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom"
|
||||
pNameSilo "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo"
|
||||
pNS1 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1"
|
||||
@@ -85,7 +86,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
||||
return applicant, err
|
||||
}
|
||||
|
||||
case domain.ApplyDNSProviderTypeAzureDNS:
|
||||
case domain.ApplyDNSProviderTypeAzure, domain.ApplyDNSProviderTypeAzureDNS:
|
||||
{
|
||||
access := domain.AccessConfigForAzure{}
|
||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||
@@ -214,6 +215,23 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
||||
return applicant, err
|
||||
}
|
||||
|
||||
case domain.ApplyDNSProviderTypeJDCloud, domain.ApplyDNSProviderTypeJDCloudDNS:
|
||||
{
|
||||
access := domain.AccessConfigForJDCloud{}
|
||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{
|
||||
AccessKeyId: access.AccessKeyId,
|
||||
AccessKeySecret: access.AccessKeySecret,
|
||||
RegionId: maps.GetValueAsString(options.ProviderApplyConfig, "region_id"),
|
||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||
DnsTTL: options.DnsTTL,
|
||||
})
|
||||
return applicant, err
|
||||
}
|
||||
|
||||
case domain.ApplyDNSProviderTypeNameDotCom:
|
||||
{
|
||||
access := domain.AccessConfigForNameDotCom{}
|
||||
|
@@ -115,12 +115,17 @@ type AccessConfigForHuaweiCloud struct {
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
}
|
||||
|
||||
type AccessConfigForLocal struct{}
|
||||
type AccessConfigForJDCloud struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
}
|
||||
|
||||
type AccessConfigForKubernetes struct {
|
||||
KubeConfig string `json:"kubeConfig,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForLocal struct{}
|
||||
|
||||
type AccessConfigForNameDotCom struct {
|
||||
Username string `json:"username"`
|
||||
ApiToken string `json:"apiToken"`
|
||||
|
@@ -34,7 +34,7 @@ const (
|
||||
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
|
||||
AccessProviderTypeGoEdge = AccessProviderType("goedge") // GoEdge(预留)
|
||||
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
|
||||
AccessProviderTypeJDCloud = AccessProviderType("jdcloud") // 京东云(预留)
|
||||
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
|
||||
AccessProviderTypeKubernetes = AccessProviderType("k8s")
|
||||
AccessProviderTypeLocal = AccessProviderType("local")
|
||||
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
|
||||
@@ -68,6 +68,7 @@ const (
|
||||
ApplyDNSProviderTypeAliyunDNS = ApplyDNSProviderType("aliyun-dns")
|
||||
ApplyDNSProviderTypeAWS = ApplyDNSProviderType("aws") // 兼容旧值,等同于 [ApplyDNSProviderTypeAWSRoute53]
|
||||
ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType("aws-route53")
|
||||
ApplyDNSProviderTypeAzure = ApplyDNSProviderType("azure") // 兼容旧值,等同于 [ApplyDNSProviderTypeAzure]
|
||||
ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns")
|
||||
ApplyDNSProviderTypeBaiduCloud = ApplyDNSProviderType("baiducloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeBaiduCloudDNS]
|
||||
ApplyDNSProviderTypeBaiduCloudDNS = ApplyDNSProviderType("baiducloud-dns")
|
||||
@@ -78,6 +79,8 @@ const (
|
||||
ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy")
|
||||
ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS]
|
||||
ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns")
|
||||
ApplyDNSProviderTypeJDCloud = ApplyDNSProviderType("jdcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeJDCloudDNS]
|
||||
ApplyDNSProviderTypeJDCloudDNS = ApplyDNSProviderType("jdcloud-dns")
|
||||
ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom")
|
||||
ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo")
|
||||
ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1")
|
||||
|
@@ -184,10 +184,10 @@ func (d *DNSProvider) removeDNSRecord(domain, subDomain, value string) error {
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
err = d.client.DeleteRecord(domain, record.Id, d.generateClientToken())
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.client.DeleteRecord(domain, record.Id, d.generateClientToken())
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *DNSProvider) generateClientToken() string {
|
||||
|
@@ -0,0 +1,229 @@
|
||||
package lego_jdcloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core"
|
||||
jdDnsApi "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/apis"
|
||||
jdDnsClient "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/client"
|
||||
jdDnsModel "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/models"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "JDCLOUD_"
|
||||
|
||||
EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID"
|
||||
EnvAccessKeySecret = envNamespace + "ACCESS_KEY_SECRET"
|
||||
EnvRegionId = envNamespace + "REGION_ID"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
AccessKeyID string
|
||||
AccessKeySecret string
|
||||
RegionId string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int32
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *jdDnsClient.DomainserviceClient
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAccessKeyID, EnvAccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AccessKeyID = values[EnvAccessKeyID]
|
||||
config.AccessKeySecret = values[EnvAccessKeySecret]
|
||||
config.RegionId = values[EnvRegionId]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("jdcloud: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
clientCredentials := jdCore.NewCredentials(config.AccessKeyID, config.AccessKeySecret)
|
||||
clientConfig := jdCore.NewConfig()
|
||||
clientConfig.SetTimeout(config.HTTPTimeout)
|
||||
client := jdDnsClient.NewDomainserviceClient(clientCredentials)
|
||||
client.SetConfig(clientConfig)
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(domain, subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||
subDomain := dns01.UnFqdn(fqdn)
|
||||
|
||||
if err := d.removeDNSRecord(domain, subDomain, value); err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZone(domain string) (*jdDnsModel.DomainInfo, error) {
|
||||
pageNumber := 1
|
||||
pageSize := 100
|
||||
for {
|
||||
request := &jdDnsApi.DescribeDomainsRequest{}
|
||||
request.RegionId = d.config.RegionId
|
||||
request.DomainName = &domain
|
||||
request.PageNumber = pageNumber
|
||||
request.PageSize = pageSize
|
||||
|
||||
response, err := d.client.DescribeDomains(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range response.Result.DataList {
|
||||
if item.DomainName == domain {
|
||||
return &item, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Result.DataList) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageNumber++
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("jdcloud: zone %s not found", domain)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZoneAndRecord(domain, subDomain string) (*jdDnsModel.DomainInfo, *jdDnsModel.RRInfo, error) {
|
||||
zone, err := d.getDNSZone(domain)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pageNumber := 1
|
||||
pageSize := 100
|
||||
for {
|
||||
request := jdDnsApi.NewDescribeResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", &zone.Id))
|
||||
request.Search = &subDomain
|
||||
request.PageNumber = &pageNumber
|
||||
request.PageSize = &pageSize
|
||||
|
||||
response, err := d.client.DescribeResourceRecord(request)
|
||||
if err != nil {
|
||||
return zone, nil, err
|
||||
}
|
||||
|
||||
for _, record := range response.Result.DataList {
|
||||
if record.Type == "TXT" && record.HostRecord == subDomain {
|
||||
return zone, &record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Result.DataList) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageNumber++
|
||||
}
|
||||
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(domain, subDomain, value string) error {
|
||||
zone, record, err := d.getDNSZoneAndRecord(domain, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := jdDnsApi.NewCreateResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", &zone.Id), &jdDnsModel.AddRR{
|
||||
Type: "TXT",
|
||||
HostRecord: subDomain,
|
||||
HostValue: value,
|
||||
Ttl: int(d.config.TTL),
|
||||
})
|
||||
_, err := d.client.CreateResourceRecord(request)
|
||||
return err
|
||||
} else {
|
||||
request := jdDnsApi.NewModifyResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", &zone.Id), fmt.Sprintf("%d", &record.Id), &jdDnsModel.UpdateRR{
|
||||
Type: "TXT",
|
||||
HostRecord: subDomain,
|
||||
HostValue: value,
|
||||
Ttl: int(d.config.TTL),
|
||||
})
|
||||
_, err := d.client.ModifyResourceRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(domain, subDomain, value string) error {
|
||||
zone, record, err := d.getDNSZoneAndRecord(domain, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
req := jdDnsApi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", &zone.Id), fmt.Sprintf("%d", &record.Id))
|
||||
_, err = d.client.DeleteResourceRecord(req)
|
||||
return err
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package jdcloud
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
|
||||
internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
RegionId string `json:"regionId"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||
if config == nil {
|
||||
panic("config is nil")
|
||||
}
|
||||
|
||||
regionId := config.RegionId
|
||||
if regionId == "" {
|
||||
// 京东云的 SDK 要求必须传一个区域,实际上 DNS-01 流程里用不到,但不传会报错
|
||||
regionId = "cn-north-1"
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.AccessKeyID = config.AccessKeyId
|
||||
providerConfig.AccessKeySecret = config.AccessKeySecret
|
||||
providerConfig.RegionId = regionId
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
Reference in New Issue
Block a user