1
0
Fork 0

fix: update lego.

This commit is contained in:
Fernandez Ludovic 2019-04-26 11:08:44 +02:00 committed by Traefiker Bot
parent b8b0c8f3e5
commit 8d848c3d60
169 changed files with 12224 additions and 605 deletions

View file

@ -6,7 +6,6 @@ import (
"crypto/elliptic"
"crypto/rsa"
"encoding/base64"
"errors"
"fmt"
"github.com/go-acme/lego/acme/api/internal/nonces"
@ -118,9 +117,6 @@ func (j *JWS) GetKeyAuthorization(token string) (string, error) {
// Generate the Key Authorization for the challenge
jwk := &jose.JSONWebKey{Key: publicKey}
if jwk == nil {
return "", errors.New("could not generate JWK from key")
}
thumbBytes, err := jwk.Thumbprint(crypto.SHA256)
if err != nil {

View file

@ -5,7 +5,7 @@ package sender
const (
// ourUserAgent is the User-Agent of this underlying library package.
ourUserAgent = "xenolf-acme/2.4.0"
ourUserAgent = "xenolf-acme/2.5.0"
// ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package.
// values: detach|release

View file

@ -114,6 +114,7 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
err = c.resolver.Solve(authz)
if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order)
return nil, err
}
@ -170,6 +171,7 @@ func (c *Certifier) ObtainForCSR(csr x509.CertificateRequest, bundle bool) (*Res
err = c.resolver.Solve(authz)
if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order)
return nil, err
}

View file

@ -27,7 +27,7 @@ func NewDefaultConfig() *Config {
return &Config{
PropagationTimeout: env.GetOrDefaultSecond("CLOUDNS_PROPAGATION_TIMEOUT", 120*time.Second),
PollingInterval: env.GetOrDefaultSecond("CLOUDNS_POLLING_INTERVAL", 4*time.Second),
TTL: env.GetOrDefaultInt("CLOUDNS_TTL", dns01.DefaultTTL),
TTL: env.GetOrDefaultInt("CLOUDNS_TTL", 60),
HTTPClient: &http.Client{
Timeout: env.GetOrDefaultSecond("CLOUDNS_HTTP_TIMEOUT", 30*time.Second),
},
@ -64,7 +64,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
client, err := internal.NewClient(config.AuthID, config.AuthPassword)
if err != nil {
return nil, err
return nil, fmt.Errorf("ClouDNS: %v", err)
}
client.HTTPClient = config.HTTPClient
@ -78,10 +78,15 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
zone, err := d.client.GetZone(fqdn)
if err != nil {
return err
return fmt.Errorf("ClouDNS: %v", err)
}
return d.client.AddTxtRecord(zone.Name, fqdn, value, d.config.TTL)
err = d.client.AddTxtRecord(zone.Name, fqdn, value, d.config.TTL)
if err != nil {
return fmt.Errorf("ClouDNS: %v", err)
}
return nil
}
// CleanUp removes the TXT record matching the specified parameters.
@ -90,15 +95,23 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
zone, err := d.client.GetZone(fqdn)
if err != nil {
return err
return fmt.Errorf("ClouDNS: %v", err)
}
record, err := d.client.FindTxtRecord(zone.Name, fqdn)
if err != nil {
return err
return fmt.Errorf("ClouDNS: %v", err)
}
return d.client.RemoveTxtRecord(record.ID, zone.Name)
if record == nil {
return nil
}
err = d.client.RemoveTxtRecord(record.ID, zone.Name)
if err != nil {
return fmt.Errorf("ClouDNS: %v", err)
}
return nil
}
// Timeout returns the timeout and interval to use when checking for DNS propagation.

View file

@ -2,6 +2,7 @@ package internal
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
@ -14,6 +15,11 @@ import (
const defaultBaseURL = "https://api.cloudns.net/dns/"
type apiResponse struct {
Status string `json:"status"`
StatusDescription string `json:"statusDescription"`
}
type Zone struct {
Name string
Type string
@ -37,11 +43,11 @@ type TXTRecords map[string]TXTRecord
// NewClient creates a ClouDNS client
func NewClient(authID string, authPassword string) (*Client, error) {
if authID == "" {
return nil, fmt.Errorf("ClouDNS: credentials missing: authID")
return nil, fmt.Errorf("credentials missing: authID")
}
if authPassword == "" {
return nil, fmt.Errorf("ClouDNS: credentials missing: authPassword")
return nil, fmt.Errorf("credentials missing: authPassword")
}
baseURL, err := url.Parse(defaultBaseURL)
@ -90,7 +96,7 @@ func (c *Client) GetZone(authFQDN string) (*Zone, error) {
if len(result) > 0 {
if err = json.Unmarshal(result, &zone); err != nil {
return nil, fmt.Errorf("ClouDNS: zone unmarshaling error: %v", err)
return nil, fmt.Errorf("zone unmarshaling error: %v", err)
}
}
@ -98,7 +104,7 @@ func (c *Client) GetZone(authFQDN string) (*Zone, error) {
return &zone, nil
}
return nil, fmt.Errorf("ClouDNS: zone %s not found for authFQDN %s", authZoneName, authFQDN)
return nil, fmt.Errorf("zone %s not found for authFQDN %s", authZoneName, authFQDN)
}
// FindTxtRecord return the TXT record a zone ID and a FQDN
@ -119,9 +125,14 @@ func (c *Client) FindTxtRecord(zoneName, fqdn string) (*TXTRecord, error) {
return nil, err
}
// the API returns [] when there is no records.
if string(result) == "[]" {
return nil, nil
}
var records TXTRecords
if err = json.Unmarshal(result, &records); err != nil {
return nil, fmt.Errorf("ClouDNS: TXT record unmarshaling error: %v", err)
return nil, fmt.Errorf("TXT record unmarshaling error: %v: %s", err, string(result))
}
for _, record := range records {
@ -130,7 +141,7 @@ func (c *Client) FindTxtRecord(zoneName, fqdn string) (*TXTRecord, error) {
}
}
return nil, fmt.Errorf("ClouDNS: no existing record found for %q", fqdn)
return nil, nil
}
// AddTxtRecord add a TXT record
@ -144,12 +155,25 @@ func (c *Client) AddTxtRecord(zoneName string, fqdn, value string, ttl int) erro
q.Add("domain-name", zoneName)
q.Add("host", host)
q.Add("record", value)
q.Add("ttl", strconv.Itoa(ttl))
q.Add("ttl", strconv.Itoa(ttlRounder(ttl)))
q.Add("record-type", "TXT")
reqURL.RawQuery = q.Encode()
_, err := c.doRequest(http.MethodPost, &reqURL)
return err
raw, err := c.doRequest(http.MethodPost, &reqURL)
if err != nil {
return err
}
resp := apiResponse{}
if err = json.Unmarshal(raw, &resp); err != nil {
return fmt.Errorf("apiResponse unmarshaling error: %v: %s", err, string(raw))
}
if resp.Status != "Success" {
return fmt.Errorf("fail to add TXT record: %s %s", resp.Status, resp.StatusDescription)
}
return nil
}
// RemoveTxtRecord remove a TXT record
@ -162,8 +186,21 @@ func (c *Client) RemoveTxtRecord(recordID int, zoneName string) error {
q.Add("record-id", strconv.Itoa(recordID))
reqURL.RawQuery = q.Encode()
_, err := c.doRequest(http.MethodPost, &reqURL)
return err
raw, err := c.doRequest(http.MethodPost, &reqURL)
if err != nil {
return err
}
resp := apiResponse{}
if err = json.Unmarshal(raw, &resp); err != nil {
return fmt.Errorf("apiResponse unmarshaling error: %v: %s", err, string(raw))
}
if resp.Status != "Success" {
return fmt.Errorf("fail to add TXT record: %s %s", resp.Status, resp.StatusDescription)
}
return nil
}
func (c *Client) doRequest(method string, url *url.URL) (json.RawMessage, error) {
@ -174,18 +211,18 @@ func (c *Client) doRequest(method string, url *url.URL) (json.RawMessage, error)
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, fmt.Errorf("ClouDNS: %v", err)
return nil, err
}
defer resp.Body.Close()
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("ClouDNS: %s", toUnreadableBodyMessage(req, content))
return nil, errors.New(toUnreadableBodyMessage(req, content))
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("ClouDNS: invalid code (%v), error: %s", resp.StatusCode, content)
return nil, fmt.Errorf("invalid code (%v), error: %s", resp.StatusCode, content)
}
return content, nil
}
@ -198,7 +235,7 @@ func (c *Client) buildRequest(method string, url *url.URL) (*http.Request, error
req, err := http.NewRequest(method, url.String(), nil)
if err != nil {
return nil, fmt.Errorf("ClouDNS: invalid request: %v", err)
return nil, fmt.Errorf("invalid request: %v", err)
}
return req, nil
@ -207,3 +244,28 @@ func (c *Client) buildRequest(method string, url *url.URL) (*http.Request, error
func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string {
return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody))
}
// https://www.cloudns.net/wiki/article/58/
// Available TTL's:
// 60 = 1 minute
// 300 = 5 minutes
// 900 = 15 minutes
// 1800 = 30 minutes
// 3600 = 1 hour
// 21600 = 6 hours
// 43200 = 12 hours
// 86400 = 1 day
// 172800 = 2 days
// 259200 = 3 days
// 604800 = 1 week
// 1209600 = 2 weeks
// 2592000 = 1 month
func ttlRounder(ttl int) int {
for _, validTTL := range []int{60, 300, 900, 1800, 3600, 21600, 43200, 86400, 172800, 259200, 604800, 1209600} {
if ttl <= validTTL {
return validTTL
}
}
return 2592000
}

View file

@ -47,13 +47,13 @@ func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl
return err
}
message := &apiResponse{}
err = d.do(req, message)
message := apiResponse{}
err = d.do(req, &message)
if err != nil {
return fmt.Errorf("unable to create TXT record for domain %s and name %s: %v", domain, name, err)
}
if message != nil && len(message.Message) > 0 {
if len(message.Message) > 0 {
log.Infof("API response: %s", message.Message)
}
@ -87,13 +87,13 @@ func (d *DNSProvider) deleteTXTRecord(domain string, name string) error {
return err
}
message := &apiResponse{}
err = d.do(req, message)
message := apiResponse{}
err = d.do(req, &message)
if err != nil {
return fmt.Errorf("unable to delete TXT record for domain %s and name %s: %v", domain, name, err)
}
if message != nil && len(message.Message) > 0 {
if len(message.Message) > 0 {
log.Infof("API response: %s", message.Message)
}

View file

@ -18,6 +18,7 @@ import (
"golang.org/x/oauth2/google"
"google.golang.org/api/dns/v1"
"google.golang.org/api/googleapi"
"google.golang.org/api/option"
)
const (
@ -139,8 +140,11 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("googlecloud: the configuration of the DNS provider is nil")
}
if config.HTTPClient == nil {
return nil, fmt.Errorf("googlecloud: unable to create Google Cloud DNS service: client is nil")
}
svc, err := dns.New(config.HTTPClient)
svc, err := dns.NewService(context.Background(), option.WithHTTPClient(config.HTTPClient))
if err != nil {
return nil, fmt.Errorf("googlecloud: unable to create Google Cloud DNS service: %v", err)
}
@ -306,7 +310,13 @@ func (d *DNSProvider) getHostedZone(domain string) (string, error) {
return "", fmt.Errorf("no matching domain found for domain %s", authZone)
}
return zones.ManagedZones[0].Name, nil
for _, z := range zones.ManagedZones {
if z.Visibility == "public" {
return z.Name, nil
}
}
return "", fmt.Errorf("no public zone found for domain %s", authZone)
}
func (d *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) {

View file

@ -0,0 +1,106 @@
package sakuracloud
import (
"fmt"
"net/http"
"strings"
"github.com/go-acme/lego/challenge/dns01"
"github.com/sacloud/libsacloud/api"
"github.com/sacloud/libsacloud/sacloud"
)
const sacloudAPILockKey = "lego/dns/sacloud"
func (d *DNSProvider) addTXTRecord(fqdn, domain, value string, ttl int) error {
sacloud.LockByKey(sacloudAPILockKey)
defer sacloud.UnlockByKey(sacloudAPILockKey)
zone, err := d.getHostedZone(domain)
if err != nil {
return fmt.Errorf("sakuracloud: %v", err)
}
name := d.extractRecordName(fqdn, zone.Name)
zone.AddRecord(zone.CreateNewRecord(name, "TXT", value, ttl))
_, err = d.client.Update(zone.ID, zone)
if err != nil {
return fmt.Errorf("sakuracloud: API call failed: %v", err)
}
return nil
}
func (d *DNSProvider) cleanupTXTRecord(fqdn, domain string) error {
sacloud.LockByKey(sacloudAPILockKey)
defer sacloud.UnlockByKey(sacloudAPILockKey)
zone, err := d.getHostedZone(domain)
if err != nil {
return fmt.Errorf("sakuracloud: %v", err)
}
records := d.findTxtRecords(fqdn, zone)
for _, record := range records {
var updRecords []sacloud.DNSRecordSet
for _, r := range zone.Settings.DNS.ResourceRecordSets {
if !(r.Name == record.Name && r.Type == record.Type && r.RData == record.RData) {
updRecords = append(updRecords, r)
}
}
zone.Settings.DNS.ResourceRecordSets = updRecords
}
_, err = d.client.Update(zone.ID, zone)
if err != nil {
return fmt.Errorf("sakuracloud: API call failed: %v", err)
}
return nil
}
func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) {
authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain))
if err != nil {
return nil, err
}
zoneName := dns01.UnFqdn(authZone)
res, err := d.client.Reset().WithNameLike(zoneName).Find()
if err != nil {
if notFound, ok := err.(api.Error); ok && notFound.ResponseCode() == http.StatusNotFound {
return nil, fmt.Errorf("zone %s not found on SakuraCloud DNS: %v", zoneName, err)
}
return nil, fmt.Errorf("API call failed: %v", err)
}
for _, zone := range res.CommonServiceDNSItems {
if zone.Name == zoneName {
return &zone, nil
}
}
return nil, fmt.Errorf("zone %s not found", zoneName)
}
func (d *DNSProvider) findTxtRecords(fqdn string, zone *sacloud.DNS) []sacloud.DNSRecordSet {
recordName := d.extractRecordName(fqdn, zone.Name)
var res []sacloud.DNSRecordSet
for _, record := range zone.Settings.DNS.ResourceRecordSets {
if record.Name == recordName && record.Type == "TXT" {
res = append(res, record)
}
}
return res
}
func (d *DNSProvider) extractRecordName(fqdn, domain string) string {
name := dns01.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx]
}
return name
}

View file

@ -5,13 +5,11 @@ import (
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/go-acme/lego/challenge/dns01"
"github.com/go-acme/lego/platform/config/env"
"github.com/sacloud/libsacloud/api"
"github.com/sacloud/libsacloud/sacloud"
)
// Config is used to configure the creation of the DNSProvider
@ -21,6 +19,7 @@ type Config struct {
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
HTTPClient *http.Client
}
// NewDefaultConfig returns a default configuration for the DNSProvider
@ -29,13 +28,16 @@ func NewDefaultConfig() *Config {
TTL: env.GetOrDefaultInt("SAKURACLOUD_TTL", dns01.DefaultTTL),
PropagationTimeout: env.GetOrDefaultSecond("SAKURACLOUD_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout),
PollingInterval: env.GetOrDefaultSecond("SAKURACLOUD_POLLING_INTERVAL", dns01.DefaultPollingInterval),
HTTPClient: &http.Client{
Timeout: env.GetOrDefaultSecond("SAKURACLOUD_HTTP_TIMEOUT", 10*time.Second),
},
}
}
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
config *Config
client *api.Client
client *api.DNSAPI
}
// NewDNSProvider returns a DNSProvider instance configured for SakuraCloud.
@ -67,58 +69,29 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
return nil, errors.New("sakuracloud: AccessSecret is missing")
}
client := api.NewClient(config.Token, config.Secret, "tk1a")
apiClient := api.NewClient(config.Token, config.Secret, "is1a")
if config.HTTPClient == nil {
apiClient.HTTPClient = http.DefaultClient
} else {
apiClient.HTTPClient = config.HTTPClient
}
return &DNSProvider{client: client, config: config}, nil
return &DNSProvider{
client: apiClient.GetDNSAPI(),
config: config,
}, nil
}
// 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)
zone, err := d.getHostedZone(domain)
if err != nil {
return fmt.Errorf("sakuracloud: %v", err)
}
name := d.extractRecordName(fqdn, zone.Name)
zone.AddRecord(zone.CreateNewRecord(name, "TXT", value, d.config.TTL))
_, err = d.client.GetDNSAPI().Update(zone.ID, zone)
if err != nil {
return fmt.Errorf("sakuracloud: API call failed: %v", err)
}
return nil
return d.addTXTRecord(fqdn, domain, value, d.config.TTL)
}
// CleanUp removes the TXT record matching the specified parameters.
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _ := dns01.GetRecord(domain, keyAuth)
zone, err := d.getHostedZone(domain)
if err != nil {
return fmt.Errorf("sakuracloud: %v", err)
}
records := d.findTxtRecords(fqdn, zone)
for _, record := range records {
var updRecords []sacloud.DNSRecordSet
for _, r := range zone.Settings.DNS.ResourceRecordSets {
if !(r.Name == record.Name && r.Type == record.Type && r.RData == record.RData) {
updRecords = append(updRecords, r)
}
}
zone.Settings.DNS.ResourceRecordSets = updRecords
}
_, err = d.client.GetDNSAPI().Update(zone.ID, zone)
if err != nil {
return fmt.Errorf("sakuracloud: API call failed: %v", err)
}
return nil
return d.cleanupTXTRecord(fqdn, domain)
}
// Timeout returns the timeout and interval to use when checking for DNS propagation.
@ -126,48 +99,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) {
authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain))
if err != nil {
return nil, err
}
zoneName := dns01.UnFqdn(authZone)
res, err := d.client.GetDNSAPI().WithNameLike(zoneName).Find()
if err != nil {
if notFound, ok := err.(api.Error); ok && notFound.ResponseCode() == http.StatusNotFound {
return nil, fmt.Errorf("zone %s not found on SakuraCloud DNS: %v", zoneName, err)
}
return nil, fmt.Errorf("API call failed: %v", err)
}
for _, zone := range res.CommonServiceDNSItems {
if zone.Name == zoneName {
return &zone, nil
}
}
return nil, fmt.Errorf("zone %s not found", zoneName)
}
func (d *DNSProvider) findTxtRecords(fqdn string, zone *sacloud.DNS) []sacloud.DNSRecordSet {
recordName := d.extractRecordName(fqdn, zone.Name)
var res []sacloud.DNSRecordSet
for _, record := range zone.Settings.DNS.ResourceRecordSets {
if record.Name == recordName && record.Type == "TXT" {
res = append(res, record)
}
}
return res
}
func (d *DNSProvider) extractRecordName(fqdn, domain string) string {
name := dns01.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx]
}
return name
}