Migrate to go-acme/lego.
This commit is contained in:
parent
4a68d29ce2
commit
87da7520de
286 changed files with 14021 additions and 2501 deletions
127
vendor/github.com/go-acme/lego/providers/dns/hostingde/client.go
generated
vendored
Normal file
127
vendor/github.com/go-acme/lego/providers/dns/hostingde/client.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
package hostingde
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
)
|
||||
|
||||
const defaultBaseURL = "https://secure.hosting.de/api/dns/v1/json"
|
||||
|
||||
// https://www.hosting.de/api/?json#list-zoneconfigs
|
||||
func (d *DNSProvider) listZoneConfigs(findRequest ZoneConfigsFindRequest) (*ZoneConfigsFindResponse, error) {
|
||||
uri := defaultBaseURL + "/zoneConfigsFind"
|
||||
|
||||
findResponse := &ZoneConfigsFindResponse{}
|
||||
|
||||
rawResp, err := d.post(uri, findRequest, findResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(findResponse.Response.Data) == 0 {
|
||||
return nil, fmt.Errorf("%v: %s", err, toUnreadableBodyMessage(uri, rawResp))
|
||||
}
|
||||
|
||||
if findResponse.Status != "success" && findResponse.Status != "pending" {
|
||||
return findResponse, errors.New(toUnreadableBodyMessage(uri, rawResp))
|
||||
}
|
||||
|
||||
return findResponse, nil
|
||||
}
|
||||
|
||||
// https://www.hosting.de/api/?json#updating-zones
|
||||
func (d *DNSProvider) updateZone(updateRequest ZoneUpdateRequest) (*ZoneUpdateResponse, error) {
|
||||
uri := defaultBaseURL + "/zoneUpdate"
|
||||
|
||||
// but we'll need the ID later to delete the record
|
||||
updateResponse := &ZoneUpdateResponse{}
|
||||
|
||||
rawResp, err := d.post(uri, updateRequest, updateResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if updateResponse.Status != "success" && updateResponse.Status != "pending" {
|
||||
return nil, errors.New(toUnreadableBodyMessage(uri, rawResp))
|
||||
}
|
||||
|
||||
return updateResponse, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getZone(findRequest ZoneConfigsFindRequest) (*ZoneConfig, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
var zoneConfig *ZoneConfig
|
||||
|
||||
operation := func() error {
|
||||
findResponse, err := d.listZoneConfigs(findRequest)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
if findResponse.Response.Data[0].Status != "active" {
|
||||
return fmt.Errorf("unexpected status: %q", findResponse.Response.Data[0].Status)
|
||||
}
|
||||
|
||||
zoneConfig = &findResponse.Response.Data[0]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
bo := backoff.NewExponentialBackOff()
|
||||
bo.InitialInterval = 3 * time.Second
|
||||
bo.MaxInterval = 10 * bo.InitialInterval
|
||||
bo.MaxElapsedTime = 100 * bo.InitialInterval
|
||||
|
||||
// retry in case the zone was edited recently and is not yet active
|
||||
err := backoff.Retry(operation, backoff.WithContext(bo, ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return zoneConfig, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) post(uri string, request interface{}, response interface{}) ([]byte, error) {
|
||||
body, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, uri, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := d.config.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error querying API: %v", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New(toUnreadableBodyMessage(uri, content))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(content, response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%v: %s", err, toUnreadableBodyMessage(uri, content))
|
||||
}
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func toUnreadableBodyMessage(uri string, rawBody []byte) string {
|
||||
return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", uri, string(rawBody))
|
||||
}
|
183
vendor/github.com/go-acme/lego/providers/dns/hostingde/hostingde.go
generated
vendored
Normal file
183
vendor/github.com/go-acme/lego/providers/dns/hostingde/hostingde.go
generated
vendored
Normal file
|
@ -0,0 +1,183 @@
|
|||
// Package hostingde implements a DNS provider for solving the DNS-01 challenge using hosting.de.
|
||||
package hostingde
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/challenge/dns01"
|
||||
"github.com/go-acme/lego/platform/config/env"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider
|
||||
type Config struct {
|
||||
APIKey string
|
||||
ZoneName string
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt("HOSTINGDE_TTL", dns01.DefaultTTL),
|
||||
PropagationTimeout: env.GetOrDefaultSecond("HOSTINGDE_PROPAGATION_TIMEOUT", 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond("HOSTINGDE_POLLING_INTERVAL", 2*time.Second),
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: env.GetOrDefaultSecond("HOSTINGDE_HTTP_TIMEOUT", 30*time.Second),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
recordIDs map[string]string
|
||||
recordIDsMu sync.Mutex
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for hosting.de.
|
||||
// Credentials must be passed in the environment variables:
|
||||
// HOSTINGDE_ZONE_NAME and HOSTINGDE_API_KEY
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get("HOSTINGDE_API_KEY", "HOSTINGDE_ZONE_NAME")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hostingde: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.APIKey = values["HOSTINGDE_API_KEY"]
|
||||
config.ZoneName = values["HOSTINGDE_ZONE_NAME"]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for hosting.de.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("hostingde: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.APIKey == "" {
|
||||
return nil, errors.New("hostingde: API key missing")
|
||||
}
|
||||
|
||||
if config.ZoneName == "" {
|
||||
return nil, errors.New("hostingde: Zone Name missing")
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
config: config,
|
||||
recordIDs: make(map[string]string),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||
// Adjusting here to cope with spikes in propagation times.
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill the dns-01 challenge
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||
|
||||
// get the ZoneConfig for that domain
|
||||
zonesFind := ZoneConfigsFindRequest{
|
||||
Filter: Filter{
|
||||
Field: "zoneName",
|
||||
Value: domain,
|
||||
},
|
||||
Limit: 1,
|
||||
Page: 1,
|
||||
}
|
||||
zonesFind.AuthToken = d.config.APIKey
|
||||
|
||||
zoneConfig, err := d.getZone(zonesFind)
|
||||
if err != nil {
|
||||
return fmt.Errorf("hostingde: %v", err)
|
||||
}
|
||||
zoneConfig.Name = d.config.ZoneName
|
||||
|
||||
rec := []DNSRecord{{
|
||||
Type: "TXT",
|
||||
Name: dns01.UnFqdn(fqdn),
|
||||
Content: value,
|
||||
TTL: d.config.TTL,
|
||||
}}
|
||||
|
||||
req := ZoneUpdateRequest{
|
||||
ZoneConfig: *zoneConfig,
|
||||
RecordsToAdd: rec,
|
||||
}
|
||||
req.AuthToken = d.config.APIKey
|
||||
|
||||
resp, err := d.updateZone(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("hostingde: %v", err)
|
||||
}
|
||||
|
||||
for _, record := range resp.Response.Records {
|
||||
if record.Name == dns01.UnFqdn(fqdn) && record.Content == fmt.Sprintf(`"%s"`, value) {
|
||||
d.recordIDsMu.Lock()
|
||||
d.recordIDs[fqdn] = record.ID
|
||||
d.recordIDsMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
if d.recordIDs[fqdn] == "" {
|
||||
return fmt.Errorf("hostingde: error getting ID of just created record, for domain %s", domain)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||
|
||||
rec := []DNSRecord{{
|
||||
Type: "TXT",
|
||||
Name: dns01.UnFqdn(fqdn),
|
||||
Content: `"` + value + `"`,
|
||||
}}
|
||||
|
||||
// get the ZoneConfig for that domain
|
||||
zonesFind := ZoneConfigsFindRequest{
|
||||
Filter: Filter{
|
||||
Field: "zoneName",
|
||||
Value: domain,
|
||||
},
|
||||
Limit: 1,
|
||||
Page: 1,
|
||||
}
|
||||
zonesFind.AuthToken = d.config.APIKey
|
||||
|
||||
zoneConfig, err := d.getZone(zonesFind)
|
||||
if err != nil {
|
||||
return fmt.Errorf("hostingde: %v", err)
|
||||
}
|
||||
zoneConfig.Name = d.config.ZoneName
|
||||
|
||||
req := ZoneUpdateRequest{
|
||||
ZoneConfig: *zoneConfig,
|
||||
RecordsToDelete: rec,
|
||||
}
|
||||
req.AuthToken = d.config.APIKey
|
||||
|
||||
// Delete record ID from map
|
||||
d.recordIDsMu.Lock()
|
||||
delete(d.recordIDs, fqdn)
|
||||
d.recordIDsMu.Unlock()
|
||||
|
||||
_, err = d.updateZone(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("hostingde: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
139
vendor/github.com/go-acme/lego/providers/dns/hostingde/model.go
generated
vendored
Normal file
139
vendor/github.com/go-acme/lego/providers/dns/hostingde/model.go
generated
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
package hostingde
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// APIError represents an error in an API response.
|
||||
// https://www.hosting.de/api/?json#warnings-and-errors
|
||||
type APIError struct {
|
||||
Code int `json:"code"`
|
||||
ContextObject string `json:"contextObject"`
|
||||
ContextPath string `json:"contextPath"`
|
||||
Details []string `json:"details"`
|
||||
Text string `json:"text"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Filter is used to filter FindRequests to the API.
|
||||
// https://www.hosting.de/api/?json#filter-object
|
||||
type Filter struct {
|
||||
Field string `json:"field"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Sort is used to sort FindRequests from the API.
|
||||
// https://www.hosting.de/api/?json#filtering-and-sorting
|
||||
type Sort struct {
|
||||
Field string `json:"zoneName"`
|
||||
Order string `json:"order"`
|
||||
}
|
||||
|
||||
// Metadata represents the metadata in an API response.
|
||||
// https://www.hosting.de/api/?json#metadata-object
|
||||
type Metadata struct {
|
||||
ClientTransactionID string `json:"clientTransactionId"`
|
||||
ServerTransactionID string `json:"serverTransactionId"`
|
||||
}
|
||||
|
||||
// ZoneConfig The ZoneConfig object defines a zone.
|
||||
// https://www.hosting.de/api/?json#the-zoneconfig-object
|
||||
type ZoneConfig struct {
|
||||
ID string `json:"id"`
|
||||
AccountID string `json:"accountId"`
|
||||
Status string `json:"status"`
|
||||
Name string `json:"name"`
|
||||
NameUnicode string `json:"nameUnicode"`
|
||||
MasterIP string `json:"masterIp"`
|
||||
Type string `json:"type"`
|
||||
EMailAddress string `json:"emailAddress"`
|
||||
ZoneTransferWhitelist []string `json:"zoneTransferWhitelist"`
|
||||
LastChangeDate string `json:"lastChangeDate"`
|
||||
DNSServerGroupID string `json:"dnsServerGroupId"`
|
||||
DNSSecMode string `json:"dnsSecMode"`
|
||||
SOAValues *SOAValues `json:"soaValues,omitempty"`
|
||||
TemplateValues json.RawMessage `json:"templateValues,omitempty"`
|
||||
}
|
||||
|
||||
// SOAValues The SOA values object contains the time (seconds) used in a zone’s SOA record.
|
||||
// https://www.hosting.de/api/?json#the-soa-values-object
|
||||
type SOAValues struct {
|
||||
Refresh int `json:"refresh"`
|
||||
Retry int `json:"retry"`
|
||||
Expire int `json:"expire"`
|
||||
TTL int `json:"ttl"`
|
||||
NegativeTTL int `json:"negativeTtl"`
|
||||
}
|
||||
|
||||
// DNSRecord The DNS Record object is part of a zone. It is used to manage DNS resource records.
|
||||
// https://www.hosting.de/api/?json#the-record-object
|
||||
type DNSRecord struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
ZoneID string `json:"zoneId,omitempty"`
|
||||
RecordTemplateID string `json:"recordTemplateId,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
Priority int `json:"priority,omitempty"`
|
||||
LastChangeDate string `json:"lastChangeDate,omitempty"`
|
||||
}
|
||||
|
||||
// Zone The Zone Object.
|
||||
// https://www.hosting.de/api/?json#the-zone-object
|
||||
type Zone struct {
|
||||
Records []DNSRecord `json:"records"`
|
||||
ZoneConfig ZoneConfig `json:"zoneConfig"`
|
||||
}
|
||||
|
||||
// ZoneUpdateRequest represents a API ZoneUpdate request.
|
||||
// https://www.hosting.de/api/?json#updating-zones
|
||||
type ZoneUpdateRequest struct {
|
||||
BaseRequest
|
||||
ZoneConfig `json:"zoneConfig"`
|
||||
RecordsToAdd []DNSRecord `json:"recordsToAdd"`
|
||||
RecordsToDelete []DNSRecord `json:"recordsToDelete"`
|
||||
}
|
||||
|
||||
// ZoneUpdateResponse represents a response from the API.
|
||||
// https://www.hosting.de/api/?json#updating-zones
|
||||
type ZoneUpdateResponse struct {
|
||||
BaseResponse
|
||||
Response Zone `json:"response"`
|
||||
}
|
||||
|
||||
// ZoneConfigsFindRequest represents a API ZonesFind request.
|
||||
// https://www.hosting.de/api/?json#list-zoneconfigs
|
||||
type ZoneConfigsFindRequest struct {
|
||||
BaseRequest
|
||||
Filter Filter `json:"filter"`
|
||||
Limit int `json:"limit"`
|
||||
Page int `json:"page"`
|
||||
Sort *Sort `json:"sort,omitempty"`
|
||||
}
|
||||
|
||||
// ZoneConfigsFindResponse represents the API response for ZoneConfigsFind.
|
||||
// https://www.hosting.de/api/?json#list-zoneconfigs
|
||||
type ZoneConfigsFindResponse struct {
|
||||
BaseResponse
|
||||
Response struct {
|
||||
Limit int `json:"limit"`
|
||||
Page int `json:"page"`
|
||||
TotalEntries int `json:"totalEntries"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
Type string `json:"type"`
|
||||
Data []ZoneConfig `json:"data"`
|
||||
} `json:"response"`
|
||||
}
|
||||
|
||||
// BaseResponse Common response struct.
|
||||
// https://www.hosting.de/api/?json#responses
|
||||
type BaseResponse struct {
|
||||
Errors []APIError `json:"errors"`
|
||||
Metadata Metadata `json:"metadata"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// BaseRequest Common request struct.
|
||||
type BaseRequest struct {
|
||||
AuthToken string `json:"authToken"`
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue