mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
refactor: improve lego providers
This commit is contained in:
@@ -177,47 +177,6 @@ func (client *EsaClient) DeleteRecord(request *aliesa.DeleteRecordRequest) (_res
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *EsaClient) ListRecordsWithOptions(request *aliesa.ListRecordsRequest, runtime *dara.RuntimeOptions) (_result *aliesa.ListRecordsResponse, _err error) {
|
||||
_err = request.Validate()
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
query := openapiutil.Query(dara.ToMap(request))
|
||||
req := &openapiutil.OpenApiRequest{
|
||||
Query: openapiutil.Query(query),
|
||||
}
|
||||
params := &openapiutil.Params{
|
||||
Action: dara.String("ListRecords"),
|
||||
Version: dara.String("2024-09-10"),
|
||||
Protocol: dara.String("HTTPS"),
|
||||
Pathname: dara.String("/"),
|
||||
Method: dara.String("GET"),
|
||||
AuthType: dara.String("AK"),
|
||||
Style: dara.String("RPC"),
|
||||
ReqBodyType: dara.String("formData"),
|
||||
BodyType: dara.String("json"),
|
||||
}
|
||||
_result = &aliesa.ListRecordsResponse{}
|
||||
_body, _err := client.CallApi(params, req, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_err = dara.Convert(_body, &_result)
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *EsaClient) ListRecords(request *aliesa.ListRecordsRequest) (_result *aliesa.ListRecordsResponse, _err error) {
|
||||
runtime := &dara.RuntimeOptions{}
|
||||
_result = &aliesa.ListRecordsResponse{}
|
||||
_body, _err := client.ListRecordsWithOptions(request, runtime)
|
||||
if _err != nil {
|
||||
return _result, _err
|
||||
}
|
||||
_result = _body
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func (client *EsaClient) ListSitesWithOptions(tmpReq *aliesa.ListSitesRequest, runtime *dara.RuntimeOptions) (_result *aliesa.ListSitesResponse, _err error) {
|
||||
_err = tmpReq.Validate()
|
||||
if _err != nil {
|
||||
|
||||
@@ -44,8 +44,8 @@ type DNSProvider struct {
|
||||
client *EsaClient
|
||||
config *Config
|
||||
|
||||
siteIDs map[string]int64
|
||||
siteIDsMtx sync.Mutex
|
||||
recordIDs map[string]int64
|
||||
recordIDsMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
@@ -76,6 +76,10 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
return nil, errors.New("alicloud-esa: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.RegionID == "" {
|
||||
config.RegionID = "cn-hangzhou"
|
||||
}
|
||||
|
||||
client, err := NewEsaClient(&aliopen.Config{
|
||||
AccessKeyId: tea.String(config.SecretID),
|
||||
AccessKeySecret: tea.String(config.SecretKey),
|
||||
@@ -86,10 +90,10 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
|
||||
siteIDs: make(map[string]int64),
|
||||
client: client,
|
||||
config: config,
|
||||
recordIDs: make(map[string]int64),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -102,14 +106,29 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
}
|
||||
|
||||
siteName := dns01.UnFqdn(authZone)
|
||||
siteId, err := d.getSiteId(siteName)
|
||||
siteID, err := d.getSiteId(siteName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(siteId, dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
|
||||
return fmt.Errorf("alicloud-esa: %w", err)
|
||||
// REF: https://www.alibabacloud.com/help/en/edge-security-acceleration/esa/api-esa-2024-09-10-createrecord
|
||||
aliCreateRecordReq := &aliesa.CreateRecordRequest{
|
||||
SiteId: tea.Int64(siteID),
|
||||
Type: tea.String("TXT"),
|
||||
RecordName: tea.String(dns01.UnFqdn(info.EffectiveFQDN)),
|
||||
Data: &aliesa.CreateRecordRequestData{
|
||||
Value: tea.String(info.Value),
|
||||
},
|
||||
Ttl: tea.Int32(int32(d.config.TTL)),
|
||||
}
|
||||
aliCreateRecordResp, err := d.client.CreateRecord(aliCreateRecordReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("alicloud-esa: error when create record: %w", err)
|
||||
}
|
||||
|
||||
d.recordIDsMu.Lock()
|
||||
d.recordIDs[token] = *aliCreateRecordResp.Body.GetRecordId()
|
||||
d.recordIDsMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -117,19 +136,19 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
||||
d.recordIDsMu.Lock()
|
||||
recordID, ok := d.recordIDs[token]
|
||||
d.recordIDsMu.Unlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("alicloud-esa: unknown record ID for '%s'", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
siteName := dns01.UnFqdn(authZone)
|
||||
siteId, err := d.getSiteId(siteName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err)
|
||||
// REF: https://www.alibabacloud.com/help/en/edge-security-acceleration/esa/api-esa-2024-09-10-deleterecord
|
||||
aliDeleteRecordReq := &aliesa.DeleteRecordRequest{
|
||||
RecordId: &recordID,
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(siteId, dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
|
||||
return fmt.Errorf("alicloud-esa: %w", err)
|
||||
if _, err := d.client.DeleteRecord(aliDeleteRecordReq); err != nil {
|
||||
return fmt.Errorf("alicloud-esa: error when delete record %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -140,41 +159,32 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
|
||||
d.siteIDsMtx.Lock()
|
||||
siteID, ok := d.siteIDs[siteName]
|
||||
d.siteIDsMtx.Unlock()
|
||||
if ok {
|
||||
return siteID, nil
|
||||
}
|
||||
|
||||
pageNumber := 1
|
||||
pageSize := 500
|
||||
for {
|
||||
request := &aliesa.ListSitesRequest{
|
||||
// REF: https://www.alibabacloud.com/help/en/edge-security-acceleration/esa/api-esa-2024-09-10-listsites
|
||||
aliListSitesReq := &aliesa.ListSitesRequest{
|
||||
SiteName: tea.String(siteName),
|
||||
SiteSearchType: tea.String("exact"),
|
||||
PageNumber: tea.Int32(int32(pageNumber)),
|
||||
PageSize: tea.Int32(int32(pageSize)),
|
||||
AccessType: tea.String("NS"),
|
||||
}
|
||||
response, err := d.client.ListSites(request)
|
||||
aliListSitesResp, err := d.client.ListSites(aliListSitesReq)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if response.Body == nil {
|
||||
if aliListSitesResp.Body == nil {
|
||||
break
|
||||
} else {
|
||||
for _, record := range response.Body.Sites {
|
||||
if tea.StringValue(record.SiteName) == siteName {
|
||||
d.siteIDsMtx.Lock()
|
||||
d.siteIDs[siteName] = *record.SiteId
|
||||
d.siteIDsMtx.Unlock()
|
||||
return *record.SiteId, nil
|
||||
for _, site := range aliListSitesResp.Body.Sites {
|
||||
if *site.GetSiteName() == siteName {
|
||||
return *site.GetSiteId(), nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Body.Sites) < pageSize {
|
||||
if len(aliListSitesResp.Body.Sites) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -184,80 +194,3 @@ func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
|
||||
|
||||
return 0, errors.New("site not found")
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN string, value string) (*aliesa.ListRecordsResponseBodyRecords, error) {
|
||||
pageNumber := 1
|
||||
pageSize := 500
|
||||
for {
|
||||
request := &aliesa.ListRecordsRequest{
|
||||
SiteId: tea.Int64(siteId),
|
||||
Type: tea.String("TXT"),
|
||||
RecordName: tea.String(effectiveFQDN),
|
||||
RecordMatchType: tea.String("exact"),
|
||||
PageNumber: tea.Int32(int32(pageNumber)),
|
||||
PageSize: tea.Int32(int32(pageSize)),
|
||||
}
|
||||
response, err := d.client.ListRecords(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Body == nil {
|
||||
break
|
||||
} else {
|
||||
for _, record := range response.Body.Records {
|
||||
if tea.StringValue(record.RecordName) == effectiveFQDN && tea.StringValue(record.Data.Value) == value {
|
||||
return record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Body.Records) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageNumber++
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(siteId int64, effectiveFQDN, value string) error {
|
||||
record, err := d.findDNSRecord(siteId, effectiveFQDN, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
request := &aliesa.CreateRecordRequest{
|
||||
SiteId: tea.Int64(siteId),
|
||||
Type: tea.String("TXT"),
|
||||
RecordName: tea.String(effectiveFQDN),
|
||||
Data: &aliesa.CreateRecordRequestData{
|
||||
Value: tea.String(value),
|
||||
},
|
||||
Ttl: tea.Int32(int32(d.config.TTL)),
|
||||
}
|
||||
_, err = d.client.CreateRecord(request)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(siteId int64, effectiveFQDN, value string) error {
|
||||
record, err := d.findDNSRecord(siteId, effectiveFQDN, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
request := &aliesa.DeleteRecordRequest{
|
||||
RecordId: record.RecordId,
|
||||
}
|
||||
_, err = d.client.DeleteRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger,
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
// 没有使用 github.com/go-acme/lego/v4/providers/dns/baiducloud
|
||||
// 因为该实现存在一些问题
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.AccessKeyID = config.AccessKeyId
|
||||
providerConfig.SecretAccessKey = config.SecretAccessKey
|
||||
|
||||
@@ -99,8 +99,16 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("baiducloud: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("baiducloud: %w", err)
|
||||
// REF: https://cloud.baidu.com/doc/DNS/s/El4s7lssr#%E6%B7%BB%E5%8A%A0%E8%A7%A3%E6%9E%90%E8%AE%B0%E5%BD%95
|
||||
bceCreateRecordReq := &bcedns.CreateRecordRequest{
|
||||
Type: "TXT",
|
||||
Rr: subDomain,
|
||||
Value: info.Value,
|
||||
Description: lo.ToPtr("certimate acme"),
|
||||
Ttl: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
if err := d.client.CreateRecord(dns01.UnFqdn(authZone), bceCreateRecordReq, security.RandomString(32)); err != nil {
|
||||
return fmt.Errorf("baiducloud: error when create record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -119,7 +127,13 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("baiducloud: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
record, err := d.findDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("baiducloud: error when find record: %q: %w", domain, err)
|
||||
}
|
||||
|
||||
// REF: https://cloud.baidu.com/doc/DNS/s/El4s7lssr#%E5%88%A0%E9%99%A4%E8%A7%A3%E6%9E%90%E8%AE%B0%E5%BD%95
|
||||
if err := d.client.DeleteRecord(dns01.UnFqdn(authZone), record.Id, security.RandomString(32)); err != nil {
|
||||
return fmt.Errorf("baiducloud: %w", err)
|
||||
}
|
||||
|
||||
@@ -130,73 +144,32 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*bcedns.Record, error) {
|
||||
func (d *DNSProvider) findDNSRecord(zoneName, subDomain, tokenValue string) (*bcedns.Record, error) {
|
||||
pageMarker := ""
|
||||
pageSize := 1000
|
||||
for {
|
||||
request := &bcedns.ListRecordRequest{}
|
||||
request.Rr = subDomain
|
||||
request.Marker = pageMarker
|
||||
request.MaxKeys = pageSize
|
||||
// REF: https://cloud.baidu.com/doc/DNS/s/El4s7lssr#%E6%9F%A5%E8%AF%A2%E8%A7%A3%E6%9E%90%E8%AE%B0%E5%BD%95%E5%88%97%E8%A1%A8
|
||||
bceListRecordReq := &bcedns.ListRecordRequest{}
|
||||
bceListRecordReq.Rr = subDomain
|
||||
bceListRecordReq.Marker = pageMarker
|
||||
bceListRecordReq.MaxKeys = pageSize
|
||||
|
||||
response, err := d.client.ListRecord(zoneName, request)
|
||||
ceListRecordResp, err := d.client.ListRecord(zoneName, bceListRecordReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, record := range response.Records {
|
||||
if record.Type == "TXT" && record.Rr == subDomain {
|
||||
for _, record := range ceListRecordResp.Records {
|
||||
if record.Type == "TXT" && record.Rr == subDomain && record.Value == tokenValue {
|
||||
return &record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if !response.IsTruncated {
|
||||
pageMarker = ceListRecordResp.NextMarker
|
||||
if pageMarker == "" {
|
||||
break
|
||||
}
|
||||
|
||||
pageMarker = response.NextMarker
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := &bcedns.CreateRecordRequest{
|
||||
Type: "TXT",
|
||||
Rr: subDomain,
|
||||
Value: value,
|
||||
Ttl: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
err := d.client.CreateRecord(zoneName, request, security.RandomString(32))
|
||||
return err
|
||||
} else {
|
||||
request := &bcedns.UpdateRecordRequest{
|
||||
Type: "TXT",
|
||||
Rr: subDomain,
|
||||
Value: value,
|
||||
Ttl: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
err := d.client.UpdateRecord(zoneName, record.Id, request, security.RandomString(32))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
err = d.client.DeleteRecord(zoneName, record.Id, security.RandomString(32))
|
||||
return err
|
||||
}
|
||||
return nil, errors.New("record not found")
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
@@ -25,7 +24,7 @@ const (
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvReadTimeOut = envNamespace + "READ_TIMEOUT"
|
||||
EnvReadTimeout = envNamespace + "READ_TIMEOUT"
|
||||
EnvConnectTimeout = envNamespace + "CONNECT_TIMEOUT"
|
||||
)
|
||||
|
||||
@@ -38,18 +37,21 @@ type Config struct {
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
ReadTimeOut int
|
||||
ReadTimeout int
|
||||
ConnectTimeout int
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *ecloudsdkclouddns.Client
|
||||
config *Config
|
||||
|
||||
recordIDs map[string]string
|
||||
recordIDsMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
ReadTimeOut: env.GetOrDefaultInt(EnvReadTimeOut, 30),
|
||||
ReadTimeout: env.GetOrDefaultInt(EnvReadTimeout, 30),
|
||||
ConnectTimeout: env.GetOrDefaultInt(EnvConnectTimeout, 30),
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 600),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
@@ -81,13 +83,15 @@ func NewDNSProviderConfig(cfg *Config) (*DNSProvider, error) {
|
||||
// 资源池常量见: https://ecloud.10086.cn/op-help-center/doc/article/54462
|
||||
// 默认全局
|
||||
PoolId: "CIDC-CORE-00",
|
||||
ReadTimeOut: cfg.ReadTimeOut,
|
||||
ReadTimeOut: cfg.ReadTimeout,
|
||||
ConnectTimeout: cfg.ConnectTimeout,
|
||||
})
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: cfg,
|
||||
client: client,
|
||||
config: cfg,
|
||||
recordIDs: make(map[string]string),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -104,91 +108,51 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
|
||||
readDomain := strings.Trim(zoneName, ".")
|
||||
record, err := d.getDomainRecord(readDomain, subDomain)
|
||||
cmccCreateRecordReq := &model.CreateRecordOpenapiRequest{
|
||||
CreateRecordOpenapiBody: &model.CreateRecordOpenapiBody{
|
||||
LineId: "0", // 默认线路
|
||||
Rr: subDomain,
|
||||
DomainName: dns01.UnFqdn(zoneName),
|
||||
Description: "certimate acme",
|
||||
Type: model.CreateRecordOpenapiBodyTypeEnumTxt,
|
||||
Value: info.Value,
|
||||
Ttl: lo.ToPtr(int32(d.config.TTL)),
|
||||
},
|
||||
}
|
||||
cmccCreateRecordResp, err := d.client.CreateRecordOpenapi(cmccCreateRecordReq)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("cmccecloud: error when create record: %w", err)
|
||||
} else if cmccCreateRecordResp.State != model.CreateRecordOpenapiResponseStateEnumOk {
|
||||
return fmt.Errorf("cmccecloud: failed to create record: unexpected response state: '%s', errcode: '%s', errmsg: '%s'", cmccCreateRecordResp.State, cmccCreateRecordResp.ErrorCode, cmccCreateRecordResp.ErrorMessage)
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
resp, err := d.client.CreateRecordOpenapi(&model.CreateRecordOpenapiRequest{
|
||||
CreateRecordOpenapiBody: &model.CreateRecordOpenapiBody{
|
||||
LineId: "0", // 默认线路
|
||||
Rr: subDomain,
|
||||
DomainName: readDomain,
|
||||
Description: "certimate acme",
|
||||
Type: model.CreateRecordOpenapiBodyTypeEnumTxt,
|
||||
Value: info.Value,
|
||||
Ttl: lo.ToPtr(int32(d.config.TTL)),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
d.recordIDsMu.Lock()
|
||||
d.recordIDs[token] = cmccCreateRecordResp.Body.RecordId
|
||||
d.recordIDsMu.Unlock()
|
||||
|
||||
if resp.State != model.CreateRecordOpenapiResponseStateEnumOk {
|
||||
return fmt.Errorf("cmccecloud: create record failed, response state: %s, message: %s, code: %s", resp.State, resp.ErrorMessage, resp.ErrorCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
resp, err := d.client.ModifyRecordOpenapi(&model.ModifyRecordOpenapiRequest{
|
||||
ModifyRecordOpenapiBody: &model.ModifyRecordOpenapiBody{
|
||||
RecordId: record.RecordId,
|
||||
Rr: subDomain,
|
||||
DomainName: readDomain,
|
||||
Description: "certmate acme",
|
||||
LineId: "0",
|
||||
Type: model.ModifyRecordOpenapiBodyTypeEnumTxt,
|
||||
Value: info.Value,
|
||||
Ttl: lo.ToPtr(int32(d.config.TTL)),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
|
||||
if resp.State != model.ModifyRecordOpenapiResponseStateEnumOk {
|
||||
return fmt.Errorf("cmccecloud: create record failed, response state: %s", resp.State)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zoneName, err := dns01.FindZoneByFqdn(challengeInfo.FQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: could not find zone for domain %q: %w", domain, err)
|
||||
d.recordIDsMu.Lock()
|
||||
recordID, ok := d.recordIDs[token]
|
||||
d.recordIDsMu.Unlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("cmccecloud: unknown record ID for '%s'", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(challengeInfo.FQDN, zoneName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
cmccDeleteRecordReq := &model.DeleteRecordOpenapiRequest{
|
||||
DeleteRecordOpenapiBody: &model.DeleteRecordOpenapiBody{
|
||||
RecordIdList: []string{recordID},
|
||||
},
|
||||
}
|
||||
|
||||
readDomain := strings.Trim(zoneName, ".")
|
||||
record, err := d.getDomainRecord(readDomain, subDomain)
|
||||
cmccDeleteRecordResp, err := d.client.DeleteRecordOpenapi(cmccDeleteRecordReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
resp, err := d.client.DeleteRecordOpenapi(&model.DeleteRecordOpenapiRequest{
|
||||
DeleteRecordOpenapiBody: &model.DeleteRecordOpenapiBody{
|
||||
RecordIdList: []string{record.RecordId},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
if resp.State != model.DeleteRecordOpenapiResponseStateEnumOk {
|
||||
return fmt.Errorf("cmccecloud: delete record failed, unexpected response state: %s", resp.State)
|
||||
}
|
||||
return fmt.Errorf("cmccecloud: error when delete record: %w", err)
|
||||
} else if cmccDeleteRecordResp.State != model.DeleteRecordOpenapiResponseStateEnumOk {
|
||||
return fmt.Errorf("cmccecloud: failed to delete record, unexpected response state: '%s', errcode: '%s', errmsg: '%s'", cmccDeleteRecordResp.State, cmccDeleteRecordResp.ErrorCode, cmccDeleteRecordResp.ErrorMessage)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -197,40 +161,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDomainRecord(domain string, rr string) (*model.ListRecordOpenapiResponseData, error) {
|
||||
pageSize := int32(50)
|
||||
page := int32(1)
|
||||
for {
|
||||
resp, err := d.client.ListRecordOpenapi(&model.ListRecordOpenapiRequest{
|
||||
ListRecordOpenapiBody: &model.ListRecordOpenapiBody{
|
||||
DomainName: domain,
|
||||
},
|
||||
ListRecordOpenapiQuery: &model.ListRecordOpenapiQuery{
|
||||
PageSize: &pageSize,
|
||||
Page: &page,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.State != model.ListRecordOpenapiResponseStateEnumOk {
|
||||
respStr, _ := json.Marshal(resp)
|
||||
return nil, fmt.Errorf("cmccecloud: request error: %s", string(respStr))
|
||||
}
|
||||
|
||||
if resp.Body.Data != nil {
|
||||
for _, item := range *resp.Body.Data {
|
||||
if item.Rr == rr {
|
||||
return &item, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if resp.Body.TotalPages == nil || page >= *resp.Body.TotalPages {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package internal
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
@@ -40,6 +41,9 @@ type Config struct {
|
||||
type DNSProvider struct {
|
||||
client *ctyundns.Client
|
||||
config *Config
|
||||
|
||||
recordIDs map[string]int32
|
||||
recordIDsMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
@@ -76,8 +80,10 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
config: config,
|
||||
recordIDs: make(map[string]int32),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -94,9 +100,24 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11259&data=181&isNormal=1&vid=259
|
||||
ctyunAddRecordReq := &ctyundns.AddRecordRequest{
|
||||
Domain: lo.ToPtr(dns01.UnFqdn(authZone)),
|
||||
Host: lo.ToPtr(subDomain),
|
||||
Type: lo.ToPtr("TXT"),
|
||||
LineCode: lo.ToPtr("Default"),
|
||||
Value: lo.ToPtr(info.Value),
|
||||
State: lo.ToPtr(int32(1)),
|
||||
TTL: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
ctyunAddRecordResp, err := d.client.AddRecord(ctyunAddRecordReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ctyun: error when create record: %w", err)
|
||||
}
|
||||
|
||||
d.recordIDsMu.Lock()
|
||||
d.recordIDs[token] = ctyunAddRecordResp.ReturnObj.RecordId
|
||||
d.recordIDsMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -104,18 +125,19 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ctyun: could not find zone for domain %q: %w", domain, err)
|
||||
d.recordIDsMu.Lock()
|
||||
recordID, ok := d.recordIDs[token]
|
||||
d.recordIDsMu.Unlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("tencentcloud-eo: unknown record ID for '%s'", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11262&data=181&isNormal=1&vid=259
|
||||
ctyunDeleteRecordReq := &ctyundns.DeleteRecordRequest{
|
||||
RecordId: lo.ToPtr(recordID),
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
if _, err := d.client.DeleteRecord(ctyunDeleteRecordReq); err != nil {
|
||||
return fmt.Errorf("ctyun: error when delete record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -124,80 +146,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecordId(zoneName, subDomain string) (int32, error) {
|
||||
// 查询解析记录列表
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11264&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.QueryRecordListRequest{}
|
||||
request.Domain = lo.ToPtr(zoneName)
|
||||
request.Host = lo.ToPtr(subDomain)
|
||||
request.Type = lo.ToPtr("TXT")
|
||||
|
||||
response, err := d.client.QueryRecordList(request)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if response.ReturnObj == nil || response.ReturnObj.Records == nil || len(response.ReturnObj.Records) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return response.ReturnObj.Records[0].RecordId, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
recordId, err := d.findDNSRecordId(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if recordId == 0 {
|
||||
// 新增解析记录
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11259&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.AddRecordRequest{
|
||||
Domain: lo.ToPtr(zoneName),
|
||||
Host: lo.ToPtr(subDomain),
|
||||
Type: lo.ToPtr("TXT"),
|
||||
LineCode: lo.ToPtr("Default"),
|
||||
Value: lo.ToPtr(value),
|
||||
State: lo.ToPtr(int32(1)),
|
||||
TTL: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.AddRecord(request)
|
||||
return err
|
||||
} else {
|
||||
// 修改解析记录
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11261&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.UpdateRecordRequest{
|
||||
RecordId: lo.ToPtr(recordId),
|
||||
Domain: lo.ToPtr(zoneName),
|
||||
Host: lo.ToPtr(subDomain),
|
||||
Type: lo.ToPtr("TXT"),
|
||||
LineCode: lo.ToPtr("Default"),
|
||||
Value: lo.ToPtr(value),
|
||||
State: lo.ToPtr(int32(1)),
|
||||
TTL: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.UpdateRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
recordId, err := d.findDNSRecordId(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if recordId == 0 {
|
||||
return nil
|
||||
} else {
|
||||
// 删除解析记录
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11262&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.DeleteRecordRequest{
|
||||
RecordId: lo.ToPtr(recordId),
|
||||
}
|
||||
_, err = d.client.DeleteRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
@@ -41,6 +42,9 @@ type Config struct {
|
||||
type DNSProvider struct {
|
||||
client *dnslasdk.Client
|
||||
config *Config
|
||||
|
||||
recordIDs map[string]string
|
||||
recordIDsMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
@@ -78,8 +82,10 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
config: config,
|
||||
recordIDs: make(map[string]string),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -96,28 +102,44 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
zone, err := d.getDNSZone(dns01.UnFqdn(authZone))
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: error when list zones: %w", err)
|
||||
}
|
||||
|
||||
// REF: https://www.dnsla.cn/docs/ApiDoc
|
||||
dnslaCreateRecordReq := &dnslasdk.CreateRecordRequest{
|
||||
DomainId: lo.ToPtr(zone.Id),
|
||||
Type: lo.ToPtr(int32(16)),
|
||||
Host: lo.ToPtr(subDomain),
|
||||
Data: lo.ToPtr(info.Value),
|
||||
Ttl: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
dnslaCreateRecordResp, err := d.client.CreateRecord(dnslaCreateRecordReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: error when create record: %w", err)
|
||||
}
|
||||
|
||||
d.recordIDsMu.Lock()
|
||||
d.recordIDs[token] = dnslaCreateRecordResp.Data.Id
|
||||
d.recordIDsMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: could not find zone for domain %q: %w", domain, err)
|
||||
d.recordIDsMu.Lock()
|
||||
recordID, ok := d.recordIDs[token]
|
||||
d.recordIDsMu.Unlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("dnsla: unknown record ID for '%s'", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
// REF: https://www.dnsla.cn/docs/ApiDoc
|
||||
if _, err := d.client.DeleteRecord(recordID); err != nil {
|
||||
return fmt.Errorf("dnsla: error when delete record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -131,111 +153,30 @@ func (d *DNSProvider) getDNSZone(zoneName string) (*dnslasdk.DomainRecord, error
|
||||
pageIndex := int32(1)
|
||||
pageSize := int32(100)
|
||||
for {
|
||||
request := &dnslasdk.ListDomainsRequest{
|
||||
// REF: https://www.dnsla.cn/docs/ApiDoc
|
||||
dnslaListDomainsReq := &dnslasdk.ListDomainsRequest{
|
||||
PageIndex: &pageIndex,
|
||||
PageSize: &pageSize,
|
||||
}
|
||||
response, err := d.client.ListDomains(request)
|
||||
dnslaListDomainsResp, err := d.client.ListDomains(dnslaListDomainsReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Data != nil {
|
||||
for _, item := range response.Data.Results {
|
||||
if dnslaListDomainsResp.Data != nil {
|
||||
for _, item := range dnslaListDomainsResp.Data.Results {
|
||||
if strings.TrimRight(item.Domain, ".") == zoneName || strings.TrimRight(item.DisplayDomain, ".") == zoneName {
|
||||
return item, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if response.Data == nil || len(response.Data.Results) < int(pageSize) {
|
||||
if dnslaListDomainsResp.Data == nil || len(dnslaListDomainsResp.Data.Results) < int(pageSize) {
|
||||
break
|
||||
}
|
||||
|
||||
pageIndex++
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("dnsla: zone %s not found", zoneName)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*dnslasdk.DomainRecord, *dnslasdk.DnsRecord, error) {
|
||||
zone, err := d.getDNSZone(zoneName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pageIndex := int32(1)
|
||||
pageSize := int32(100)
|
||||
for {
|
||||
request := &dnslasdk.ListRecordsRequest{
|
||||
DomainId: &zone.Id,
|
||||
Host: &subDomain,
|
||||
PageIndex: &pageIndex,
|
||||
PageSize: &pageSize,
|
||||
}
|
||||
response, err := d.client.ListRecords(request)
|
||||
if err != nil {
|
||||
return zone, nil, err
|
||||
}
|
||||
|
||||
if response.Data != nil {
|
||||
for _, record := range response.Data.Results {
|
||||
if record.Type == 16 && (record.Host == subDomain || record.DisplayHost == subDomain) {
|
||||
return zone, record, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if response.Data == nil || len(response.Data.Results) < int(pageSize) {
|
||||
break
|
||||
}
|
||||
|
||||
pageIndex++
|
||||
}
|
||||
|
||||
return zone, nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
zone, record, err := d.getDNSZoneAndRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const recordTypeTXT = 16
|
||||
if record == nil {
|
||||
request := &dnslasdk.CreateRecordRequest{
|
||||
DomainId: lo.ToPtr(zone.Id),
|
||||
Type: lo.ToPtr(int32(recordTypeTXT)),
|
||||
Host: lo.ToPtr(subDomain),
|
||||
Data: lo.ToPtr(value),
|
||||
Ttl: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.CreateRecord(request)
|
||||
return err
|
||||
} else {
|
||||
request := &dnslasdk.UpdateRecordRequest{
|
||||
Id: lo.ToPtr(record.Id),
|
||||
Type: lo.ToPtr(int32(recordTypeTXT)),
|
||||
Host: lo.ToPtr(subDomain),
|
||||
Data: lo.ToPtr(value),
|
||||
Ttl: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.UpdateRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
_, record, err := d.getDNSZoneAndRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
_, err = d.client.DeleteRecord(record.Id)
|
||||
return err
|
||||
}
|
||||
return nil, fmt.Errorf("zone '%s' not found", zoneName)
|
||||
}
|
||||
|
||||
@@ -84,8 +84,12 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("dynv6: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("dynv6: %w", err)
|
||||
if _, err := d.client.AppendRecords(context.Background(), dns01.UnFqdn(authZone), []libdns.Record{&libdns.TXT{
|
||||
Name: subDomain,
|
||||
Text: info.Value,
|
||||
TTL: time.Duration(d.config.TTL),
|
||||
}}); err != nil {
|
||||
return fmt.Errorf("dynv6: error when create record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -104,8 +108,13 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("dynv6: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("dynv6: %w", err)
|
||||
record, err := d.findDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dynv6: error when find record: %w", err)
|
||||
}
|
||||
|
||||
if _, err := d.client.DeleteRecords(context.Background(), dns01.UnFqdn(authZone), []libdns.Record{record}); err != nil {
|
||||
return fmt.Errorf("dynv6: error when delete record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -115,7 +124,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (libdns.Record, error) {
|
||||
func (d *DNSProvider) findDNSRecord(zoneName, subDomain, tokenValue string) (libdns.Record, error) {
|
||||
records, err := d.client.GetRecords(context.Background(), zoneName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -123,49 +132,10 @@ func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (libdns.Record,
|
||||
|
||||
for _, record := range records {
|
||||
rr := record.RR()
|
||||
if rr.Type == "TXT" && rr.Name == subDomain {
|
||||
if rr.Type == "TXT" && rr.Name == subDomain && rr.Data == tokenValue {
|
||||
return record, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
record = &libdns.TXT{
|
||||
Name: subDomain,
|
||||
Text: value,
|
||||
TTL: time.Duration(d.config.TTL),
|
||||
}
|
||||
_, err := d.client.AppendRecords(context.Background(), zoneName, []libdns.Record{record})
|
||||
return err
|
||||
} else {
|
||||
record = &libdns.TXT{
|
||||
Name: subDomain,
|
||||
Text: value,
|
||||
TTL: time.Duration(d.config.TTL),
|
||||
}
|
||||
_, err := d.client.SetRecords(context.Background(), zoneName, []libdns.Record{record})
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
_, err = d.client.DeleteRecords(context.Background(), zoneName, []libdns.Record{record})
|
||||
return err
|
||||
}
|
||||
return nil, errors.New("record not found")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package internal
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
@@ -40,6 +41,9 @@ type Config struct {
|
||||
type DNSProvider struct {
|
||||
client *gnamesdk.Client
|
||||
config *Config
|
||||
|
||||
recordIDs map[string]int64
|
||||
recordIDsMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
@@ -77,8 +81,10 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
config: config,
|
||||
recordIDs: make(map[string]int64),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -95,9 +101,22 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("gname: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("gname: %w", err)
|
||||
// REF: https://www.gname.vip/domain/api/dns/add
|
||||
gnameAddDomainResolutionReq := &gnamesdk.AddDomainResolutionRequest{
|
||||
ZoneName: lo.ToPtr(dns01.UnFqdn(authZone)),
|
||||
RecordType: lo.ToPtr("TXT"),
|
||||
RecordName: lo.ToPtr(subDomain),
|
||||
RecordValue: lo.ToPtr(info.Value),
|
||||
TTL: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
gnameAddDomainResolutionResp, err := d.client.AddDomainResolution(gnameAddDomainResolutionReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gname: error when create record: %w", err)
|
||||
}
|
||||
|
||||
d.recordIDsMu.Lock()
|
||||
d.recordIDs[token], _ = gnameAddDomainResolutionResp.Data.Int64()
|
||||
d.recordIDsMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -110,13 +129,21 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("gname: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gname: %w", err)
|
||||
d.recordIDsMu.Lock()
|
||||
recordID, ok := d.recordIDs[token]
|
||||
d.recordIDsMu.Unlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("gname: unknown record ID for '%s'", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("gname: %w", err)
|
||||
// REF: https://www.gname.vip/domain/api/dns/del
|
||||
gnameDeleteDomainResolutionReq := &gnamesdk.DeleteDomainResolutionRequest{
|
||||
ZoneName: lo.ToPtr(dns01.UnFqdn(authZone)),
|
||||
RecordID: lo.ToPtr(recordID),
|
||||
}
|
||||
_, err = d.client.DeleteDomainResolution(gnameDeleteDomainResolutionReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gname: error when delete record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -125,87 +152,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*gnamesdk.DomainResolutionRecordord, error) {
|
||||
page := int32(1)
|
||||
pageSize := int32(20)
|
||||
for {
|
||||
request := &gnamesdk.ListDomainResolutionRequest{
|
||||
ZoneName: lo.ToPtr(zoneName),
|
||||
Page: lo.ToPtr(page),
|
||||
PageSize: lo.ToPtr(pageSize),
|
||||
}
|
||||
|
||||
response, err := d.client.ListDomainResolution(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, record := range response.Data {
|
||||
if record.RecordType == "TXT" && record.RecordName == subDomain {
|
||||
return record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Data) == 0 {
|
||||
break
|
||||
}
|
||||
if response.Page*response.PageSize >= response.Count {
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := &gnamesdk.AddDomainResolutionRequest{
|
||||
ZoneName: lo.ToPtr(zoneName),
|
||||
RecordType: lo.ToPtr("TXT"),
|
||||
RecordName: lo.ToPtr(subDomain),
|
||||
RecordValue: lo.ToPtr(value),
|
||||
TTL: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.AddDomainResolution(request)
|
||||
return err
|
||||
} else {
|
||||
recordId, _ := record.ID.Int64()
|
||||
request := &gnamesdk.ModifyDomainResolutionRequest{
|
||||
ID: lo.ToPtr(recordId),
|
||||
ZoneName: lo.ToPtr(zoneName),
|
||||
RecordType: lo.ToPtr("TXT"),
|
||||
RecordName: lo.ToPtr(subDomain),
|
||||
RecordValue: lo.ToPtr(value),
|
||||
TTL: lo.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.ModifyDomainResolution(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
recordId, _ := record.ID.Int64()
|
||||
request := &gnamesdk.DeleteDomainResolutionRequest{
|
||||
ZoneName: lo.ToPtr(zoneName),
|
||||
RecordID: lo.ToPtr(recordId),
|
||||
}
|
||||
_, err = d.client.DeleteDomainResolution(request)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package internal
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
@@ -43,6 +44,9 @@ type Config struct {
|
||||
type DNSProvider struct {
|
||||
client *jddnsclient.DomainserviceClient
|
||||
config *Config
|
||||
|
||||
recordIDs map[string]int
|
||||
recordIDsMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
@@ -81,8 +85,10 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
client.DisableLogger()
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
config: config,
|
||||
recordIDs: make(map[string]int),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -99,10 +105,28 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
zone, err := d.getDNSZone(dns01.UnFqdn(authZone))
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: error when list zones: %w", err)
|
||||
}
|
||||
|
||||
// REF: https://docs.jdcloud.com/cn/jd-cloud-dns/api/createresourcerecord
|
||||
jddnsCreateResourceRecordReq := jddnsapi.NewCreateResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), &jddnsmodel.AddRR{
|
||||
Type: "TXT",
|
||||
HostRecord: subDomain,
|
||||
HostValue: info.Value,
|
||||
Ttl: int(d.config.TTL),
|
||||
ViewValue: -1,
|
||||
})
|
||||
jddnsCreateResourceRecordResp, err := d.client.CreateResourceRecord(jddnsCreateResourceRecordReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: error when create record: %w", err)
|
||||
}
|
||||
|
||||
d.recordIDsMu.Lock()
|
||||
d.recordIDs[token] = jddnsCreateResourceRecordResp.Result.DataList.Id
|
||||
d.recordIDsMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -114,13 +138,23 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("jdcloud: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
d.recordIDsMu.Lock()
|
||||
recordID, ok := d.recordIDs[token]
|
||||
d.recordIDsMu.Unlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("jdcloud: unknown record ID for '%s'", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
zone, err := d.getDNSZone(dns01.UnFqdn(authZone))
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: error when list zones: %w", err)
|
||||
}
|
||||
|
||||
// REF: https://docs.jdcloud.com/cn/jd-cloud-dns/api/deleteresourcerecord
|
||||
jddnsDeleteResourceRecordReq := jddnsapi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", recordID))
|
||||
_, err = d.client.DeleteResourceRecord(jddnsDeleteResourceRecordReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: error when delete record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -134,21 +168,22 @@ func (d *DNSProvider) getDNSZone(zoneName string) (*jddnsmodel.DomainInfo, error
|
||||
pageNumber := 1
|
||||
pageSize := 10
|
||||
for {
|
||||
request := jddnsapi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize)
|
||||
request.SetDomainName(zoneName)
|
||||
// REF: https://docs.jdcloud.com/cn/jd-cloud-dns/api/describedomains
|
||||
jddnsDescribeDomainsReq := jddnsapi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize)
|
||||
jddnsDescribeDomainsReq.SetDomainName(zoneName)
|
||||
|
||||
response, err := d.client.DescribeDomains(request)
|
||||
jddnsDescribeDomainsResp, err := d.client.DescribeDomains(jddnsDescribeDomainsReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range response.Result.DataList {
|
||||
for _, item := range jddnsDescribeDomainsResp.Result.DataList {
|
||||
if item.DomainName == zoneName {
|
||||
return &item, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Result.DataList) < pageSize {
|
||||
if len(jddnsDescribeDomainsResp.Result.DataList) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -157,82 +192,3 @@ func (d *DNSProvider) getDNSZone(zoneName string) (*jddnsmodel.DomainInfo, error
|
||||
|
||||
return nil, fmt.Errorf("jdcloud: zone %s not found", zoneName)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jddnsmodel.DomainInfo, *jddnsmodel.RRInfo, error) {
|
||||
zone, err := d.getDNSZone(zoneName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pageNumber := 1
|
||||
pageSize := 10
|
||||
for {
|
||||
request := jddnsapi.NewDescribeResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id))
|
||||
request.SetSearch(subDomain)
|
||||
request.SetPageNumber(pageNumber)
|
||||
request.SetPageSize(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 zone, nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
zone, record, err := d.getDNSZoneAndRecord(zoneName, 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),
|
||||
ViewValue: -1,
|
||||
})
|
||||
_, 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),
|
||||
ViewValue: -1,
|
||||
})
|
||||
_, err := d.client.ModifyResourceRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
zone, record, err := d.getDNSZoneAndRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
request := jddnsapi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id))
|
||||
_, err = d.client.DeleteResourceRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,43 +62,3 @@ func (c *TeoClient) DeleteDnsRecordsWithContext(ctx context.Context, request *tc
|
||||
err = c.Send(request, response)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *TeoClient) DescribeDnsRecords(request *tcteo.DescribeDnsRecordsRequest) (response *tcteo.DescribeDnsRecordsResponse, err error) {
|
||||
return c.DescribeDnsRecordsWithContext(context.Background(), request)
|
||||
}
|
||||
|
||||
func (c *TeoClient) DescribeDnsRecordsWithContext(ctx context.Context, request *tcteo.DescribeDnsRecordsRequest) (response *tcteo.DescribeDnsRecordsResponse, err error) {
|
||||
if request == nil {
|
||||
request = tcteo.NewDescribeDnsRecordsRequest()
|
||||
}
|
||||
c.InitBaseRequest(&request.BaseRequest, "teo", tcteo.APIVersion, "DescribeDnsRecords")
|
||||
|
||||
if c.GetCredential() == nil {
|
||||
return nil, errors.New("DescribeDnsRecords require credential")
|
||||
}
|
||||
|
||||
request.SetContext(ctx)
|
||||
response = tcteo.NewDescribeDnsRecordsResponse()
|
||||
err = c.Send(request, response)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *TeoClient) ModifyDnsRecordsStatus(request *tcteo.ModifyDnsRecordsStatusRequest) (response *tcteo.ModifyDnsRecordsStatusResponse, err error) {
|
||||
return c.ModifyDnsRecordsStatusWithContext(context.Background(), request)
|
||||
}
|
||||
|
||||
func (c *TeoClient) ModifyDnsRecordsStatusWithContext(ctx context.Context, request *tcteo.ModifyDnsRecordsStatusRequest) (response *tcteo.ModifyDnsRecordsStatusResponse, err error) {
|
||||
if request == nil {
|
||||
request = tcteo.NewModifyDnsRecordsStatusRequest()
|
||||
}
|
||||
c.InitBaseRequest(&request.BaseRequest, "teo", tcteo.APIVersion, "ModifyDnsRecordsStatus")
|
||||
|
||||
if c.GetCredential() == nil {
|
||||
return nil, errors.New("ModifyDnsRecordsStatus require credential")
|
||||
}
|
||||
|
||||
request.SetContext(ctx)
|
||||
response = tcteo.NewModifyDnsRecordsStatusResponse()
|
||||
err = c.Send(request, response)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
tcteo "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo/v20220901"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -43,6 +45,9 @@ type Config struct {
|
||||
type DNSProvider struct {
|
||||
client *TeoClient
|
||||
config *Config
|
||||
|
||||
recordIDs map[string]*string
|
||||
recordIDsMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
@@ -82,26 +87,57 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
config: config,
|
||||
recordIDs: make(map[string]*string),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
if err := d.addDNSRecord(dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
|
||||
return fmt.Errorf("tencentcloud-eo: %w", err)
|
||||
punnyCoded, err := idna.ToASCII(dns01.UnFqdn(info.EffectiveFQDN))
|
||||
if err != nil {
|
||||
return fmt.Errorf("tencentcloud-eo: fail to convert punycode: %w", err)
|
||||
}
|
||||
|
||||
// REF: https://cloud.tencent.com/document/product/1552/80720
|
||||
teoCreateDnsRecordReq := tcteo.NewCreateDnsRecordRequest()
|
||||
teoCreateDnsRecordReq.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
teoCreateDnsRecordReq.Name = common.StringPtr(punnyCoded)
|
||||
teoCreateDnsRecordReq.Type = common.StringPtr("TXT")
|
||||
teoCreateDnsRecordReq.Content = common.StringPtr(info.Value)
|
||||
teoCreateDnsRecordReq.TTL = common.Int64Ptr(int64(d.config.TTL))
|
||||
teoCreateDnsRecordResp, err := d.client.CreateDnsRecord(teoCreateDnsRecordReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tencentcloud-eo: error when create record: %w", err)
|
||||
}
|
||||
|
||||
d.recordIDsMu.Lock()
|
||||
d.recordIDs[token] = teoCreateDnsRecordResp.Response.RecordId
|
||||
d.recordIDsMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
|
||||
return fmt.Errorf("tencentcloud-eo: %w", err)
|
||||
d.recordIDsMu.Lock()
|
||||
recordID, ok := d.recordIDs[token]
|
||||
d.recordIDsMu.Unlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("tencentcloud-eo: unknown record ID for '%s'", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
// REF: https://cloud.tencent.com/document/product/1552/80718
|
||||
teoDeleteDnsRecordReq := tcteo.NewDeleteDnsRecordsRequest()
|
||||
teoDeleteDnsRecordReq.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
teoDeleteDnsRecordReq.RecordIds = []*string{recordID}
|
||||
_, err := d.client.DeleteDnsRecords(teoDeleteDnsRecordReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tencentcloud-eo: error when delete record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -110,88 +146,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(effectiveFQDN, value string) (*tcteo.DnsRecord, error) {
|
||||
pageOffset := 0
|
||||
pageLimit := 1000
|
||||
for {
|
||||
request := tcteo.NewDescribeDnsRecordsRequest()
|
||||
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
request.Offset = common.Int64Ptr(int64(pageOffset))
|
||||
request.Limit = common.Int64Ptr(int64(pageLimit))
|
||||
request.Filters = []*tcteo.AdvancedFilter{
|
||||
{
|
||||
Name: common.StringPtr("type"),
|
||||
Values: []*string{common.StringPtr("TXT")},
|
||||
},
|
||||
}
|
||||
|
||||
response, err := d.client.DescribeDnsRecords(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Response == nil {
|
||||
break
|
||||
} else {
|
||||
for _, record := range response.Response.DnsRecords {
|
||||
if *record.Name == effectiveFQDN && *record.Content == value {
|
||||
return record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Response.DnsRecords) < pageLimit {
|
||||
break
|
||||
}
|
||||
|
||||
pageOffset += len(response.Response.DnsRecords)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addDNSRecord(effectiveFQDN, value string) error {
|
||||
record, err := d.findDNSRecord(effectiveFQDN, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := tcteo.NewCreateDnsRecordRequest()
|
||||
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
request.Name = common.StringPtr(effectiveFQDN)
|
||||
request.Type = common.StringPtr("TXT")
|
||||
request.Content = common.StringPtr(value)
|
||||
request.TTL = common.Int64Ptr(int64(d.config.TTL))
|
||||
_, err := d.client.CreateDnsRecord(request)
|
||||
return err
|
||||
} else {
|
||||
if *record.Status == "disable" {
|
||||
request := tcteo.NewModifyDnsRecordsStatusRequest()
|
||||
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
request.RecordsToEnable = []*string{record.RecordId}
|
||||
if _, err = d.client.ModifyDnsRecordsStatus(request); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(effectiveFQDN, value string) error {
|
||||
record, err := d.findDNSRecord(effectiveFQDN, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
request := tcteo.NewDeleteDnsRecordsRequest()
|
||||
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
request.RecordIds = []*string{record.RecordId}
|
||||
_, err = d.client.DeleteDnsRecords(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger,
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
// 没有使用 github.com/go-acme/lego/v4/providers/dns/edgeone
|
||||
// 因为该实现存在一些问题
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.SecretID = config.SecretId
|
||||
providerConfig.SecretKey = config.SecretKey
|
||||
|
||||
@@ -94,43 +94,18 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("ucloud-udnr: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
recordName := dns01.UnFqdn(info.EffectiveFQDN)
|
||||
|
||||
udnrDomainDNSQueryReq := d.client.NewQueryDomainDNSRequest()
|
||||
udnrDomainDNSQueryReq.Dn = ucloud.String(authZone)
|
||||
if d.config.ProjectId != "" {
|
||||
udnrDomainDNSQueryReq.SetProjectId(d.config.ProjectId)
|
||||
}
|
||||
if udnrDomainDNSQueryResp, err := d.client.QueryDomainDNS(udnrDomainDNSQueryReq); err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||
} else {
|
||||
for _, record := range udnrDomainDNSQueryResp.Data {
|
||||
if record.DnsType == "TXT" && record.RecordName == recordName {
|
||||
udnrDomainDNSDeleteReq := d.client.NewDeleteDomainDNSRequest()
|
||||
udnrDomainDNSDeleteReq.Dn = ucloud.String(authZone)
|
||||
udnrDomainDNSDeleteReq.DnsType = ucloud.String(record.DnsType)
|
||||
udnrDomainDNSDeleteReq.RecordName = ucloud.String(record.RecordName)
|
||||
udnrDomainDNSDeleteReq.Content = ucloud.String(record.Content)
|
||||
if d.config.ProjectId != "" {
|
||||
udnrDomainDNSDeleteReq.SetProjectId(d.config.ProjectId)
|
||||
}
|
||||
d.client.DeleteDomainDNS(udnrDomainDNSDeleteReq)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// REF: https://docs.ucloud.cn/api/udnr-api/udnr_domain_dns_add
|
||||
udnrDomainDNSAddReq := d.client.NewAddDomainDNSRequest()
|
||||
udnrDomainDNSAddReq.Dn = ucloud.String(authZone)
|
||||
udnrDomainDNSAddReq.DnsType = ucloud.String("TXT")
|
||||
udnrDomainDNSAddReq.RecordName = ucloud.String(recordName)
|
||||
udnrDomainDNSAddReq.RecordName = ucloud.String(dns01.UnFqdn(info.EffectiveFQDN))
|
||||
udnrDomainDNSAddReq.Content = ucloud.String(info.Value)
|
||||
udnrDomainDNSAddReq.TTL = ucloud.String(fmt.Sprintf("%d", d.config.TTL))
|
||||
if d.config.ProjectId != "" {
|
||||
udnrDomainDNSAddReq.SetProjectId(d.config.ProjectId)
|
||||
}
|
||||
if _, err := d.client.AddDomainDNS(udnrDomainDNSAddReq); err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||
return fmt.Errorf("ucloud-udnr: error when create record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -144,29 +119,33 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
return fmt.Errorf("ucloud-udnr: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
recordName := dns01.UnFqdn(info.EffectiveFQDN)
|
||||
|
||||
// REF: https://docs.ucloud.cn/api/udnr-api/udnr_domain_dns_query
|
||||
udnrDomainDNSQueryReq := d.client.NewQueryDomainDNSRequest()
|
||||
udnrDomainDNSQueryReq.Dn = ucloud.String(authZone)
|
||||
if d.config.ProjectId != "" {
|
||||
udnrDomainDNSQueryReq.SetProjectId(d.config.ProjectId)
|
||||
}
|
||||
if udnrDomainDNSQueryResp, err := d.client.QueryDomainDNS(udnrDomainDNSQueryReq); err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||
} else {
|
||||
for _, record := range udnrDomainDNSQueryResp.Data {
|
||||
if record.DnsType == "TXT" && record.RecordName == recordName {
|
||||
udnrDomainDNSDeleteReq := d.client.NewDeleteDomainDNSRequest()
|
||||
udnrDomainDNSDeleteReq.Dn = ucloud.String(authZone)
|
||||
udnrDomainDNSDeleteReq.DnsType = ucloud.String(record.DnsType)
|
||||
udnrDomainDNSDeleteReq.RecordName = ucloud.String(record.RecordName)
|
||||
udnrDomainDNSDeleteReq.Content = ucloud.String(record.Content)
|
||||
if d.config.ProjectId != "" {
|
||||
udnrDomainDNSDeleteReq.SetProjectId(d.config.ProjectId)
|
||||
}
|
||||
d.client.DeleteDomainDNS(udnrDomainDNSDeleteReq)
|
||||
break
|
||||
udnrDomainDNSQueryResp, err := d.client.QueryDomainDNS(udnrDomainDNSQueryReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: error when list records: %w", err)
|
||||
}
|
||||
|
||||
// REF: https://docs.ucloud.cn/api/udnr-api/udnr_delete_dns_record
|
||||
for _, record := range udnrDomainDNSQueryResp.Data {
|
||||
if record.DnsType == "TXT" && record.RecordName == dns01.UnFqdn(info.EffectiveFQDN) && record.Content == info.Value {
|
||||
udnrDomainDNSDeleteReq := d.client.NewDeleteDomainDNSRequest()
|
||||
udnrDomainDNSDeleteReq.Dn = ucloud.String(authZone)
|
||||
udnrDomainDNSDeleteReq.DnsType = ucloud.String(record.DnsType)
|
||||
udnrDomainDNSDeleteReq.RecordName = ucloud.String(record.RecordName)
|
||||
udnrDomainDNSDeleteReq.Content = ucloud.String(record.Content)
|
||||
if d.config.ProjectId != "" {
|
||||
udnrDomainDNSDeleteReq.SetProjectId(d.config.ProjectId)
|
||||
}
|
||||
_, err := d.client.DeleteDomainDNS(udnrDomainDNSDeleteReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: error when delete record: %w", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user