chore: move '/internal/pkg' to '/pkg'

This commit is contained in:
Fu Diwei
2025-06-17 15:54:21 +08:00
parent 30840bbba5
commit 205275b52d
611 changed files with 693 additions and 693 deletions

View File

@@ -0,0 +1,42 @@
package acmehttpreq
import (
"errors"
"net/url"
"time"
"github.com/go-acme/lego/v4/providers/dns/httpreq"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
Endpoint string `json:"endpoint"`
Mode string `json:"mode"`
Username string `json:"username"`
Password string `json:"password"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
endpoint, _ := url.Parse(config.Endpoint)
providerConfig := httpreq.NewDefaultConfig()
providerConfig.Endpoint = endpoint
providerConfig.Mode = config.Mode
providerConfig.Username = config.Username
providerConfig.Password = config.Password
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
provider, err := httpreq.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,41 @@
package aliyunesa
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/aliyun-esa/internal"
)
type ChallengeProviderConfig struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
Region string `json:"region"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.SecretID = config.AccessKeyId
providerConfig.SecretKey = config.AccessKeySecret
providerConfig.RegionID = config.Region
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
}

View File

@@ -0,0 +1,271 @@
package internal
import (
"errors"
"fmt"
"sync"
"time"
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliesa "github.com/alibabacloud-go/esa-20240910/v2/client"
"github.com/alibabacloud-go/tea/tea"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/platform/config/env"
)
const (
envNamespace = "ALICLOUDESA_"
EnvAccessKey = envNamespace + "ACCESS_KEY"
EnvSecretKey = envNamespace + "SECRET_KEY"
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 {
SecretID string
SecretKey string
RegionID string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int32
HTTPTimeout time.Duration
}
type DNSProvider struct {
client *aliesa.Client
config *Config
siteIDs map[string]int64
siteIDsMtx sync.Mutex
}
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(EnvAccessKey, EnvSecretKey, EnvRegionID)
if err != nil {
return nil, fmt.Errorf("alicloud-esa: %w", err)
}
config := NewDefaultConfig()
config.SecretID = values[EnvAccessKey]
config.SecretKey = values[EnvSecretKey]
config.RegionID = values[EnvRegionID]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("alicloud-esa: the configuration of the DNS provider is nil")
}
client, err := aliesa.NewClient(&aliopen.Config{
AccessKeyId: tea.String(config.SecretID),
AccessKeySecret: tea.String(config.SecretKey),
Endpoint: tea.String(fmt.Sprintf("esa.%s.aliyuncs.com", config.RegionID)),
})
if err != nil {
return nil, fmt.Errorf("alicloud-esa: %w", err)
}
return &DNSProvider{
client: client,
config: config,
siteIDs: make(map[string]int64),
}, nil
}
func (d *DNSProvider) Present(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)
}
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)
}
if err := d.addOrUpdateDNSRecord(siteId, dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
return fmt.Errorf("alicloud-esa: %w", err)
}
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("alicloud-esa: could not find zone for domain %q: %w", domain, err)
}
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)
}
if err := d.removeDNSRecord(siteId, dns01.UnFqdn(info.EffectiveFQDN)); err != nil {
return fmt.Errorf("alicloud-esa: %w", err)
}
return nil
}
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
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{
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)
if err != nil {
return 0, err
}
if response.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
}
}
if len(response.Body.Sites) < pageSize {
break
}
pageNumber++
}
}
return 0, errors.New("site not found")
}
func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN 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 {
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)
if err != nil {
return err
}
if record == 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(d.config.TTL),
}
_, err := d.client.CreateRecord(request)
return err
} else {
request := &aliesa.UpdateRecordRequest{
RecordId: record.RecordId,
Ttl: tea.Int32(d.config.TTL),
Data: &aliesa.UpdateRecordRequestData{
Value: tea.String(value),
},
}
_, err := d.client.UpdateRecord(request)
return err
}
}
func (d *DNSProvider) removeDNSRecord(siteId int64, effectiveFQDN string) error {
record, err := d.findDNSRecord(siteId, effectiveFQDN)
if err != nil {
return err
}
if record == nil {
return nil
} else {
request := &aliesa.DeleteRecordRequest{
RecordId: record.RecordId,
}
_, err = d.client.DeleteRecord(request)
return err
}
}

View File

@@ -0,0 +1,40 @@
package aliyun
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/alidns"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := alidns.NewDefaultConfig()
providerConfig.APIKey = config.AccessKeyId
providerConfig.SecretKey = config.AccessKeySecret
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := alidns.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,44 @@
package awsroute53
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/route53"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
Region string `json:"region"`
HostedZoneId string `json:"hostedZoneId"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := route53.NewDefaultConfig()
providerConfig.AccessKeyID = config.AccessKeyId
providerConfig.SecretAccessKey = config.SecretAccessKey
providerConfig.Region = config.Region
providerConfig.HostedZoneID = config.HostedZoneId
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := route53.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,51 @@
package azuredns
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/azuredns"
"github.com/usual2970/certimate/pkg/core"
azenv "github.com/usual2970/certimate/pkg/sdk3rd/azure/env"
)
type ChallengeProviderConfig struct {
TenantId string `json:"tenantId"`
ClientId string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
CloudName string `json:"cloudName,omitempty"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := azuredns.NewDefaultConfig()
providerConfig.TenantID = config.TenantId
providerConfig.ClientID = config.ClientId
providerConfig.ClientSecret = config.ClientSecret
if config.CloudName != "" {
env, err := azenv.GetCloudEnvConfiguration(config.CloudName)
if err != nil {
return nil, err
}
providerConfig.Environment = env
}
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := azuredns.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,39 @@
package baiducloud
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/baiducloud/internal"
)
type ChallengeProviderConfig struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.AccessKeyID = config.AccessKeyId
providerConfig.SecretAccessKey = config.SecretAccessKey
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
}

View File

@@ -0,0 +1,204 @@
package internal
import (
"errors"
"fmt"
"strings"
"time"
bcedns "github.com/baidubce/bce-sdk-go/services/dns"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/platform/config/env"
"github.com/google/uuid"
)
const (
envNamespace = "BAIDUCLOUD_"
EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID"
EnvSecretAccessKey = envNamespace + "SECRET_ACCESS_KEY"
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
SecretAccessKey string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int32
HTTPTimeout time.Duration
}
type DNSProvider struct {
client *bcedns.Client
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, EnvSecretAccessKey)
if err != nil {
return nil, fmt.Errorf("baiducloud: %w", err)
}
config := NewDefaultConfig()
config.AccessKeyID = values[EnvAccessKeyID]
config.SecretAccessKey = values[EnvSecretAccessKey]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("baiducloud: the configuration of the DNS provider is nil")
}
client, err := bcedns.NewClient(config.AccessKeyID, config.SecretAccessKey, "")
if err != nil {
return nil, err
} else {
if client.Config != nil {
client.Config.ConnectionTimeoutInMillis = int(config.HTTPTimeout.Milliseconds())
}
}
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
return fmt.Errorf("baiducloud: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("baiducloud: %w", err)
}
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
return fmt.Errorf("baiducloud: %w", err)
}
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("baiducloud: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("baiducloud: %w", err)
}
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
return fmt.Errorf("baiducloud: %w", err)
}
return nil
}
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) {
pageMarker := ""
pageSize := 1000
for {
request := &bcedns.ListRecordRequest{}
request.Rr = subDomain
request.Marker = pageMarker
request.MaxKeys = pageSize
response, err := d.client.ListRecord(zoneName, request)
if err != nil {
return nil, err
}
for _, record := range response.Records {
if record.Type == "TXT" && record.Rr == subDomain {
return &record, nil
}
}
if len(response.Records) < pageSize {
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: &d.config.TTL,
}
err := d.client.CreateRecord(zoneName, request, d.generateClientToken())
return err
} else {
request := &bcedns.UpdateRecordRequest{
Type: "TXT",
Rr: subDomain,
Value: value,
Ttl: &d.config.TTL,
}
err := d.client.UpdateRecord(zoneName, record.Id, request, d.generateClientToken())
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, d.generateClientToken())
return err
}
}
func (d *DNSProvider) generateClientToken() string {
return strings.ReplaceAll(uuid.New().String(), "-", "")
}

View File

@@ -0,0 +1,38 @@
package bunny
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/bunny"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiKey string `json:"apiKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := bunny.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := bunny.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,40 @@
package cloudflare
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
DnsApiToken string `json:"dnsApiToken"`
ZoneApiToken string `json:"zoneApiToken,omitempty"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := cloudflare.NewDefaultConfig()
providerConfig.AuthToken = config.DnsApiToken
providerConfig.ZoneToken = config.ZoneApiToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := cloudflare.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,40 @@
package cloudns
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/cloudns"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
AuthId string `json:"authId"`
AuthPassword string `json:"authPassword"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := cloudns.NewDefaultConfig()
providerConfig.AuthID = config.AuthId
providerConfig.AuthPassword = config.AuthPassword
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := cloudns.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,39 @@
package cmcccloud
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/cmcccloud/internal"
)
type ChallengeProviderConfig struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.AccessKey = config.AccessKeyId
providerConfig.SecretKey = config.AccessKeySecret
if config.DnsTTL != 0 {
providerConfig.TTL = config.DnsTTL
}
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
provider, err := internal.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,235 @@
package internal
import (
"encoding/json"
"errors"
"fmt"
"strings"
"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"
"gitlab.ecloud.com/ecloud/ecloudsdkclouddns"
"gitlab.ecloud.com/ecloud/ecloudsdkclouddns/model"
"gitlab.ecloud.com/ecloud/ecloudsdkcore/config"
)
const (
envNamespace = "CMCCCLOUD_"
EnvAccessKey = envNamespace + "ACCESS_KEY"
EnvSecretKey = envNamespace + "SECRET_KEY"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
EnvReadTimeOut = envNamespace + "READ_TIMEOUT"
EnvConnectTimeout = envNamespace + "CONNECT_TIMEOUT"
)
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
type Config struct {
AccessKey string
SecretKey string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int32
ReadTimeOut int
ConnectTimeout int
}
type DNSProvider struct {
client *ecloudsdkclouddns.Client
config *Config
}
func NewDefaultConfig() *Config {
return &Config{
ReadTimeOut: env.GetOrDefaultInt(EnvReadTimeOut, 30),
ConnectTimeout: env.GetOrDefaultInt(EnvConnectTimeout, 30),
TTL: int32(env.GetOrDefaultInt(EnvTTL, 600)),
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
}
}
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(EnvAccessKey, EnvSecretKey)
if err != nil {
return nil, fmt.Errorf("cmccecloud: %w", err)
}
cfg := NewDefaultConfig()
cfg.AccessKey = values[EnvAccessKey]
cfg.SecretKey = values[EnvSecretKey]
return NewDNSProviderConfig(cfg)
}
func NewDNSProviderConfig(cfg *Config) (*DNSProvider, error) {
if cfg == nil {
return nil, errors.New("cmccecloud: the configuration of the DNS provider is nil")
}
client := ecloudsdkclouddns.NewClient(&config.Config{
AccessKey: cfg.AccessKey,
SecretKey: cfg.SecretKey,
// 资源池常量见: https://ecloud.10086.cn/op-help-center/doc/article/54462
// 默认全局
PoolId: "CIDC-CORE-00",
ReadTimeOut: cfg.ReadTimeOut,
ConnectTimeout: cfg.ConnectTimeout,
})
return &DNSProvider{
client: client,
config: cfg,
}, 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("cmccecloud: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName)
if err != nil {
return fmt.Errorf("cmccecloud: %w", err)
}
readDomain := strings.Trim(zoneName, ".")
record, err := d.getDomainRecord(readDomain, subDomain)
if err != nil {
return err
}
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: &d.config.TTL,
},
})
if err != nil {
return fmt.Errorf("cmccecloud: %w", err)
}
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: &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
}
}
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
challengeInfo := 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)
}
subDomain, err := dns01.ExtractSubDomain(challengeInfo.FQDN, zoneName)
if err != nil {
return fmt.Errorf("cmccecloud: %w", err)
}
readDomain := strings.Trim(zoneName, ".")
record, err := d.getDomainRecord(readDomain, subDomain)
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 nil
}
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++
}
}

View File

@@ -0,0 +1,40 @@
package cloudns
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/constellix"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiKey string `json:"apiKey"`
SecretKey string `json:"secretKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := constellix.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey
providerConfig.SecretKey = config.SecretKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := constellix.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,39 @@
package ctcccloud
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/ctcccloud/internal"
)
type ChallengeProviderConfig struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.AccessKeyId = config.AccessKeyId
providerConfig.SecretAccessKey = config.SecretAccessKey
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
provider, err := internal.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,203 @@
package internal
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"
ctyundns "github.com/usual2970/certimate/pkg/sdk3rd/ctyun/dns"
xtypes "github.com/usual2970/certimate/pkg/utils/types"
)
const (
envNamespace = "CTYUNSMARTDNS_"
EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID"
EnvSecretAccessKey = envNamespace + "SECRET_ACCESS_KEY"
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
SecretAccessKey string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
HTTPTimeout time.Duration
}
type DNSProvider struct {
client *ctyundns.Client
config *Config
}
func NewDefaultConfig() *Config {
return &Config{
TTL: env.GetOrDefaultInt(EnvTTL, 600),
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
}
}
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(EnvAccessKeyID, EnvSecretAccessKey)
if err != nil {
return nil, fmt.Errorf("ctyun: %w", err)
}
config := NewDefaultConfig()
config.AccessKeyId = values[EnvAccessKeyID]
config.SecretAccessKey = values[EnvSecretAccessKey]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("ctyun: the configuration of the DNS provider is nil")
}
client, err := ctyundns.NewClient(config.AccessKeyId, config.SecretAccessKey)
if err != nil {
return nil, err
} else {
client.SetTimeout(config.HTTPTimeout)
}
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(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)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("ctyun: %w", err)
}
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
return fmt.Errorf("ctyun: %w", err)
}
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("ctyun: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("ctyun: %w", err)
}
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
return fmt.Errorf("ctyun: %w", err)
}
return nil
}
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 = xtypes.ToPtr(zoneName)
request.Host = xtypes.ToPtr(subDomain)
request.Type = xtypes.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: xtypes.ToPtr(zoneName),
Host: xtypes.ToPtr(subDomain),
Type: xtypes.ToPtr("TXT"),
LineCode: xtypes.ToPtr("Default"),
Value: xtypes.ToPtr(value),
State: xtypes.ToPtr(int32(1)),
TTL: xtypes.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: xtypes.ToPtr(recordId),
Domain: xtypes.ToPtr(zoneName),
Host: xtypes.ToPtr(subDomain),
Type: xtypes.ToPtr("TXT"),
LineCode: xtypes.ToPtr("Default"),
Value: xtypes.ToPtr(value),
State: xtypes.ToPtr(int32(1)),
TTL: xtypes.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: xtypes.ToPtr(recordId),
}
_, err = d.client.DeleteRecord(request)
return err
}
}

View File

@@ -0,0 +1,38 @@
package desec
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/desec"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
Token string `json:"token"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := desec.NewDefaultConfig()
providerConfig.Token = config.Token
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := desec.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,38 @@
package namedotcom
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/digitalocean"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
AccessToken string `json:"accessToken"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := digitalocean.NewDefaultConfig()
providerConfig.AuthToken = config.AccessToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := digitalocean.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,39 @@
package dnsla
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/dnsla/internal"
)
type ChallengeProviderConfig struct {
ApiId string `json:"apiId"`
ApiSecret string `json:"apiSecret"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.APIId = config.ApiId
providerConfig.APISecret = config.ApiSecret
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := internal.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,241 @@
package internal
import (
"errors"
"fmt"
"strings"
"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"
dnslasdk "github.com/usual2970/certimate/pkg/sdk3rd/dnsla"
)
const (
envNamespace = "DNSLA_"
EnvAPIId = envNamespace + "API_ID"
EnvAPISecret = envNamespace + "API_KEY"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
)
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
type Config struct {
APIId string
APISecret string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
HTTPTimeout time.Duration
}
type DNSProvider struct {
client *dnslasdk.Client
config *Config
}
func NewDefaultConfig() *Config {
return &Config{
TTL: 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(EnvAPIId, EnvAPISecret)
if err != nil {
return nil, fmt.Errorf("dnsla: %w", err)
}
config := NewDefaultConfig()
config.APIId = values[EnvAPIId]
config.APISecret = values[EnvAPISecret]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("dnsla: the configuration of the DNS provider is nil")
}
client, err := dnslasdk.NewClient(config.APIId, config.APISecret)
if err != nil {
return nil, err
} else {
client.SetTimeout(config.HTTPTimeout)
}
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(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)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("dnsla: %w", err)
}
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
return fmt.Errorf("dnsla: %w", err)
}
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)
}
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)
}
return nil
}
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
func (d *DNSProvider) getDNSZone(zoneName string) (*dnslasdk.DomainRecord, error) {
pageIndex := int32(1)
pageSize := int32(100)
for {
request := &dnslasdk.ListDomainsRequest{
PageIndex: &pageIndex,
PageSize: &pageSize,
}
response, err := d.client.ListDomains(request)
if err != nil {
return nil, err
}
if response.Data != nil {
for _, item := range response.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) {
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
}
if record == nil {
request := &dnslasdk.CreateRecordRequest{
DomainId: zone.Id,
Type: 16,
Host: subDomain,
Data: value,
Ttl: int32(d.config.TTL),
}
_, err := d.client.CreateRecord(request)
return err
} else {
reqType := int32(16)
reqTtl := int32(d.config.TTL)
request := &dnslasdk.UpdateRecordRequest{
Id: record.Id,
Type: &reqType,
Host: &subDomain,
Data: &value,
Ttl: &reqTtl,
}
_, 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
}
}

View File

@@ -0,0 +1,34 @@
package namedotcom
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/duckdns"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
Token string `json:"token"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := duckdns.NewDefaultConfig()
providerConfig.Token = config.Token
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
provider, err := duckdns.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,37 @@
package dynv6
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/dynv6/internal"
)
type ChallengeProviderConfig struct {
HttpToken string `json:"httpToken"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.HTTPToken = config.HttpToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := internal.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,167 @@
package internal
import (
"context"
"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"
"github.com/libdns/dynv6"
"github.com/libdns/libdns"
)
const (
envNamespace = "DYNV6_"
EnvHTTPToken = envNamespace + "HTTP_TOKEN"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
)
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
type Config struct {
HTTPToken string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
}
type DNSProvider struct {
client *dynv6.Provider
config *Config
}
func NewDefaultConfig() *Config {
return &Config{
TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
}
}
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(EnvHTTPToken)
if err != nil {
return nil, fmt.Errorf("dynv6: %w", err)
}
config := NewDefaultConfig()
config.HTTPToken = values[EnvHTTPToken]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("dynv6: the configuration of the DNS provider is nil")
}
client := &dynv6.Provider{Token: config.HTTPToken}
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
return fmt.Errorf("dynv6: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("dynv6: %w", err)
}
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
return fmt.Errorf("dynv6: %w", err)
}
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("dynv6: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("dynv6: %w", err)
}
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
return fmt.Errorf("dynv6: %w", err)
}
return nil
}
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) {
records, err := d.client.GetRecords(context.Background(), zoneName)
if err != nil {
return nil, err
}
for _, record := range records {
if record.Type == "TXT" && record.Name == subDomain {
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.Record{
Type: "TXT",
Name: subDomain,
Value: value,
TTL: time.Duration(d.config.TTL) * time.Second,
}
_, err := d.client.AppendRecords(context.Background(), zoneName, []libdns.Record{*record})
return err
} else {
record.Value = value
_, 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
}
}

View File

@@ -0,0 +1,38 @@
package gcore
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/gcore"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiToken string `json:"apiToken"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := gcore.NewDefaultConfig()
providerConfig.APIToken = config.ApiToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := gcore.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,39 @@
package gname
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/gname/internal"
)
type ChallengeProviderConfig struct {
AppId string `json:"appId"`
AppKey string `json:"appKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.AppID = config.AppId
providerConfig.AppKey = config.AppKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := internal.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,211 @@
package internal
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"
gnamesdk "github.com/usual2970/certimate/pkg/sdk3rd/gname"
xtypes "github.com/usual2970/certimate/pkg/utils/types"
)
const (
envNamespace = "GNAME_"
EnvAppID = envNamespace + "APP_ID"
EnvAppKey = envNamespace + "APP_KEY"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
)
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
type Config struct {
AppID string
AppKey string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
HTTPTimeout time.Duration
}
type DNSProvider struct {
client *gnamesdk.Client
config *Config
}
func NewDefaultConfig() *Config {
return &Config{
TTL: 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(EnvAppID, EnvAppKey)
if err != nil {
return nil, fmt.Errorf("gname: %w", err)
}
config := NewDefaultConfig()
config.AppID = values[EnvAppID]
config.AppKey = values[EnvAppKey]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("gname: the configuration of the DNS provider is nil")
}
client, err := gnamesdk.NewClient(config.AppID, config.AppKey)
if err != nil {
return nil, err
} else {
client.SetTimeout(config.HTTPTimeout)
}
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
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)
}
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
return fmt.Errorf("gname: %w", err)
}
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("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)
}
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
return fmt.Errorf("gname: %w", err)
}
return nil
}
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: xtypes.ToPtr(zoneName),
Page: xtypes.ToPtr(page),
PageSize: xtypes.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: xtypes.ToPtr(zoneName),
RecordType: xtypes.ToPtr("TXT"),
RecordName: xtypes.ToPtr(subDomain),
RecordValue: xtypes.ToPtr(value),
TTL: xtypes.ToPtr(int32(d.config.TTL)),
}
_, err := d.client.AddDomainResolution(request)
return err
} else {
recordId, _ := record.ID.Int64()
request := &gnamesdk.ModifyDomainResolutionRequest{
ID: xtypes.ToPtr(recordId),
ZoneName: xtypes.ToPtr(zoneName),
RecordType: xtypes.ToPtr("TXT"),
RecordName: xtypes.ToPtr(subDomain),
RecordValue: xtypes.ToPtr(value),
TTL: xtypes.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: xtypes.ToPtr(zoneName),
RecordID: xtypes.ToPtr(recordId),
}
_, err = d.client.DeleteDomainResolution(request)
return err
}

View File

@@ -0,0 +1,40 @@
package godaddy
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/godaddy"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiKey string `json:"apiKey"`
ApiSecret string `json:"apiSecret"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := godaddy.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey
providerConfig.APISecret = config.ApiSecret
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := godaddy.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,38 @@
package namedotcom
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/hetzner"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiToken string `json:"apiToken"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := hetzner.NewDefaultConfig()
providerConfig.APIKey = config.ApiToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := hetzner.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,48 @@
package huaweicloud
import (
"errors"
"time"
hwc "github.com/go-acme/lego/v4/providers/dns/huaweicloud"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
Region string `json:"region"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
region := config.Region
if region == "" {
// 华为云的 SDK 要求必须传一个区域,实际上 DNS-01 流程里用不到,但不传会报错
region = "cn-north-1"
}
providerConfig := hwc.NewDefaultConfig()
providerConfig.AccessKeyID = config.AccessKeyId
providerConfig.SecretAccessKey = config.SecretAccessKey
providerConfig.Region = region
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = config.DnsTTL
}
provider, err := hwc.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,238 @@
package internal
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)
client := jddnsclient.NewDomainserviceClient(clientCredentials)
clientConfig := &client.Config
clientConfig.SetTimeout(config.HTTPTimeout)
client.SetConfig(clientConfig)
client.SetLogger(jdcore.NewDefaultLogger(jdcore.LogWarn))
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
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)
}
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
return fmt.Errorf("jdcloud: %w", err)
}
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("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)
}
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); 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(zoneName string) (*jddnsmodel.DomainInfo, error) {
pageNumber := 1
pageSize := 10
for {
request := jddnsapi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize)
request.SetDomainName(zoneName)
response, err := d.client.DescribeDomains(request)
if err != nil {
return nil, err
}
for _, item := range response.Result.DataList {
if item.DomainName == zoneName {
return &item, nil
}
}
if len(response.Result.DataList) < pageSize {
break
}
pageNumber++
}
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
}
}

View File

@@ -0,0 +1,47 @@
package jdcloud
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/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) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider 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
}

View File

@@ -0,0 +1,40 @@
package namedotcom
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/namecheap"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
Username string `json:"username"`
ApiKey string `json:"apiKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := namecheap.NewDefaultConfig()
providerConfig.APIUser = config.Username
providerConfig.APIKey = config.ApiKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := namecheap.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,40 @@
package namedotcom
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/namedotcom"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
Username string `json:"username"`
ApiToken string `json:"apiToken"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := namedotcom.NewDefaultConfig()
providerConfig.Username = config.Username
providerConfig.APIToken = config.ApiToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := namedotcom.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,38 @@
package namesilo
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/namesilo"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiKey string `json:"apiKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := namesilo.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := namesilo.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,42 @@
package netcup
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/netcup"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
CustomerNumber string `json:"customerNumber"`
ApiKey string `json:"apiKey"`
ApiPassword string `json:"apiPassword"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := netcup.NewDefaultConfig()
providerConfig.Customer = config.CustomerNumber
providerConfig.Key = config.ApiKey
providerConfig.Password = config.ApiPassword
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := netcup.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,38 @@
package netcup
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/netlify"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiToken string `json:"apiToken"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := netlify.NewDefaultConfig()
providerConfig.Token = config.ApiToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := netlify.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,38 @@
package ns1
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/ns1"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiKey string `json:"apiKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := ns1.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := ns1.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,40 @@
package porkbun
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/porkbun"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiKey string `json:"apiKey"`
SecretApiKey string `json:"secretApiKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := porkbun.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey
providerConfig.SecretAPIKey = config.SecretApiKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := porkbun.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,53 @@
package powerdns
import (
"crypto/tls"
"errors"
"net/http"
"net/url"
"time"
"github.com/go-acme/lego/v4/providers/dns/pdns"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ServerUrl string `json:"serverUrl"`
ApiKey string `json:"apiKey"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
serverUrl, _ := url.Parse(config.ServerUrl)
providerConfig := pdns.NewDefaultConfig()
providerConfig.Host = serverUrl
providerConfig.APIKey = config.ApiKey
if config.AllowInsecureConnections {
providerConfig.HTTPClient.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
}
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := pdns.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,38 @@
package rainyun
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/rainyun"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiKey string `json:"apiKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := rainyun.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := rainyun.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,207 @@
package internal
import (
"errors"
"fmt"
"math"
"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"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
teo "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo/v20220901"
)
const (
envNamespace = "TENCENTCLOUDEO_"
EnvSecretID = envNamespace + "SECRET_ID"
EnvSecretKey = envNamespace + "SECRET_KEY"
EnvZoneID = envNamespace + "ZONE_ID"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
)
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
type Config struct {
SecretID string
SecretKey string
ZoneID string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int32
HTTPTimeout time.Duration
}
type DNSProvider struct {
client *teo.Client
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(EnvSecretID, EnvSecretKey, EnvZoneID)
if err != nil {
return nil, fmt.Errorf("tencentcloud-eo: %w", err)
}
config := NewDefaultConfig()
config.SecretID = values[EnvSecretID]
config.SecretKey = values[EnvSecretKey]
config.ZoneID = values[EnvSecretKey]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("tencentcloud-eo: the configuration of the DNS provider is nil")
}
credential := common.NewCredential(config.SecretID, config.SecretKey)
cpf := profile.NewClientProfile()
cpf.HttpProfile.ReqTimeout = int(math.Round(config.HTTPTimeout.Seconds()))
client, err := teo.NewClient(credential, "", cpf)
if err != nil {
return nil, err
}
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
return fmt.Errorf("tencentcloud-eo: %w", err)
}
return nil
}
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
if err := d.removeDNSRecord(dns01.UnFqdn(info.EffectiveFQDN)); err != nil {
return fmt.Errorf("tencentcloud-eo: %w", err)
}
return nil
}
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
func (d *DNSProvider) findDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error) {
pageOffset := 0
pageLimit := 1000
for {
request := teo.NewDescribeDnsRecordsRequest()
request.ZoneId = common.StringPtr(d.config.ZoneID)
request.Offset = common.Int64Ptr(int64(pageOffset))
request.Limit = common.Int64Ptr(int64(pageLimit))
request.Filters = []*teo.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 {
return record, nil
}
}
if len(response.Response.DnsRecords) < pageLimit {
break
}
pageOffset += len(response.Response.DnsRecords)
}
}
return nil, nil
}
func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error {
record, err := d.findDNSRecord(effectiveFQDN)
if err != nil {
return err
}
if record == nil {
request := teo.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 {
record.Content = common.StringPtr(value)
record.TTL = common.Int64Ptr(int64(d.config.TTL))
request := teo.NewModifyDnsRecordsRequest()
request.ZoneId = common.StringPtr(d.config.ZoneID)
request.DnsRecords = []*teo.DnsRecord{record}
if _, err := d.client.ModifyDnsRecords(request); err != nil {
return err
}
if *record.Status == "disable" {
request := teo.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 string) error {
record, err := d.findDNSRecord(effectiveFQDN)
if err != nil {
return err
}
if record == nil {
return nil
} else {
request := teo.NewDeleteDnsRecordsRequest()
request.ZoneId = common.StringPtr(d.config.ZoneID)
request.RecordIds = []*string{record.RecordId}
_, err = d.client.DeleteDnsRecords(request)
return err
}
}

View File

@@ -0,0 +1,41 @@
package tencentcloudeo
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/tencentcloud-eo/internal"
)
type ChallengeProviderConfig struct {
SecretId string `json:"secretId"`
SecretKey string `json:"secretKey"`
ZoneId string `json:"zoneId"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.SecretID = config.SecretId
providerConfig.SecretKey = config.SecretKey
providerConfig.ZoneID = config.ZoneId
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
}

View File

@@ -0,0 +1,40 @@
package tencentcloud
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
SecretId string `json:"secretId"`
SecretKey string `json:"secretKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := tencentcloud.NewDefaultConfig()
providerConfig.SecretID = config.SecretId
providerConfig.SecretKey = config.SecretKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := tencentcloud.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,166 @@
package internal
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"
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
"github.com/usual2970/certimate/pkg/sdk3rd/ucloud/udnr"
)
const (
envNamespace = "UCLOUDUDNR_"
EnvPublicKey = envNamespace + "PUBLIC_KEY"
EnvPrivateKey = envNamespace + "PRIVATE_KEY"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
)
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
type Config struct {
PrivateKey string
PublicKey string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int32
HTTPTimeout time.Duration
}
type DNSProvider struct {
client *udnr.UDNRClient
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(EnvPrivateKey, EnvPublicKey)
if err != nil {
return nil, fmt.Errorf("ucloud-udnr: %w", err)
}
config := NewDefaultConfig()
config.PrivateKey = values[EnvPrivateKey]
config.PublicKey = values[EnvPublicKey]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("ucloud-udnr: the configuration of the DNS provider is nil")
}
cfg := ucloud.NewConfig()
cfg.Timeout = config.HTTPTimeout
credential := auth.NewCredential()
credential.PrivateKey = config.PrivateKey
credential.PublicKey = config.PublicKey
client := udnr.NewClient(&cfg, &credential)
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
return fmt.Errorf("ucloud-udnr: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("ucloud-udnr: %w", err)
}
udnrDomainDNSQueryReq := d.client.NewQueryDomainDNSRequest()
udnrDomainDNSQueryReq.Dn = ucloud.String(authZone)
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 == subDomain {
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)
d.client.DeleteDomainDNS(udnrDomainDNSDeleteReq)
break
}
}
}
udnrDomainDNSAddReq := d.client.NewAddDomainDNSRequest()
udnrDomainDNSAddReq.Dn = ucloud.String(authZone)
udnrDomainDNSAddReq.DnsType = ucloud.String("TXT")
udnrDomainDNSAddReq.RecordName = ucloud.String(subDomain)
udnrDomainDNSAddReq.Content = ucloud.String(info.Value)
udnrDomainDNSAddReq.TTL = ucloud.Int(int(d.config.TTL))
if _, err := d.client.AddDomainDNS(udnrDomainDNSAddReq); err != nil {
return fmt.Errorf("ucloud-udnr: %w", err)
}
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("ucloud-udnr: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("ucloud-udnr: %w", err)
}
udnrDomainDNSQueryReq := d.client.NewQueryDomainDNSRequest()
udnrDomainDNSQueryReq.Dn = ucloud.String(authZone)
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 == subDomain {
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)
d.client.DeleteDomainDNS(udnrDomainDNSDeleteReq)
break
}
}
}
return nil
}
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}

View File

@@ -0,0 +1,39 @@
package ucloududnr
import (
"errors"
"time"
"github.com/usual2970/certimate/pkg/core"
"github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/ucloud-udnr/internal"
)
type ChallengeProviderConfig struct {
PrivateKey string `json:"privateKey"`
PublicKey string `json:"publicKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("config is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.PrivateKey = config.PrivateKey
providerConfig.PublicKey = config.PublicKey
if config.DnsTTL != 0 {
providerConfig.TTL = config.DnsTTL
}
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
provider, err := internal.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,40 @@
package vercel
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/vercel"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiAccessToken string `json:"apiAccessToken"`
TeamId string `json:"teamId,omitempty"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := vercel.NewDefaultConfig()
providerConfig.AuthToken = config.ApiAccessToken
providerConfig.TeamID = config.TeamId
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := vercel.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,40 @@
package volcengine
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/volcengine"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := volcengine.NewDefaultConfig()
providerConfig.AccessKey = config.AccessKeyId
providerConfig.SecretKey = config.SecretAccessKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := volcengine.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,40 @@
package westcn
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/westcn"
"github.com/usual2970/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
Username string `json:"username"`
ApiPassword string `json:"apiPassword"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := westcn.NewDefaultConfig()
providerConfig.Username = config.Username
providerConfig.Password = config.ApiPassword
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := westcn.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}