forked from TrueCloudLab/lego
971541dc0a
This will prevent indefinitely-hanging requests in case some service or middle box is malfunctioning. Fix vet errors and lint warnings Add vet to CI check Only get issuer certificate if it would be used No need to make a GET request if the OCSP server is not specified in leaf certificate Fix CI tests Make tests verbose
145 lines
3.5 KiB
Go
145 lines
3.5 KiB
Go
package acme
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/crackcomm/cloudflare"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
// DNSProviderCloudFlare is an implementation of the DNSProvider interface
|
|
type DNSProviderCloudFlare struct {
|
|
client *cloudflare.Client
|
|
ctx context.Context
|
|
}
|
|
|
|
// NewDNSProviderCloudFlare returns a DNSProviderCloudFlare instance with a configured cloudflare client.
|
|
// Authentication is either done using the passed credentials or - when empty - using the environment
|
|
// variables CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY.
|
|
func NewDNSProviderCloudFlare(cloudflareEmail, cloudflareKey string) (*DNSProviderCloudFlare, error) {
|
|
if cloudflareEmail == "" || cloudflareKey == "" {
|
|
cloudflareEmail, cloudflareKey = cloudflareEnvAuth()
|
|
if cloudflareEmail == "" || cloudflareKey == "" {
|
|
return nil, fmt.Errorf("CloudFlare credentials missing")
|
|
}
|
|
}
|
|
|
|
c := &DNSProviderCloudFlare{
|
|
client: cloudflare.New(&cloudflare.Options{Email: cloudflareEmail, Key: cloudflareKey}),
|
|
ctx: context.Background(),
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// Present creates a TXT record to fulfil the dns-01 challenge
|
|
func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error {
|
|
fqdn, value, ttl := DNS01Record(domain, keyAuth)
|
|
zoneID, err := c.getHostedZoneID(fqdn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
record := newTxtRecord(zoneID, fqdn, value, ttl)
|
|
err = c.client.Records.Create(c.ctx, record)
|
|
if err != nil {
|
|
return fmt.Errorf("CloudFlare API call failed: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CleanUp removes the TXT record matching the specified parameters
|
|
func (c *DNSProviderCloudFlare) CleanUp(domain, token, keyAuth string) error {
|
|
fqdn, _, _ := DNS01Record(domain, keyAuth)
|
|
records, err := c.findTxtRecords(fqdn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, rec := range records {
|
|
err := c.client.Records.Delete(c.ctx, rec.ZoneID, rec.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *DNSProviderCloudFlare) findTxtRecords(fqdn string) ([]*cloudflare.Record, error) {
|
|
zoneID, err := c.getHostedZoneID(fqdn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var records []*cloudflare.Record
|
|
result, err := c.client.Records.List(c.ctx, zoneID)
|
|
if err != nil {
|
|
return records, fmt.Errorf("CloudFlare API call has failed: %v", err)
|
|
}
|
|
|
|
name := unFqdn(fqdn)
|
|
for _, rec := range result {
|
|
if rec.Name == name && rec.Type == "TXT" {
|
|
records = append(records, rec)
|
|
}
|
|
}
|
|
|
|
return records, nil
|
|
}
|
|
|
|
func (c *DNSProviderCloudFlare) getHostedZoneID(fqdn string) (string, error) {
|
|
zones, err := c.client.Zones.List(c.ctx)
|
|
if err != nil {
|
|
return "", fmt.Errorf("CloudFlare API call failed: %v", err)
|
|
}
|
|
|
|
var hostedZone cloudflare.Zone
|
|
for _, zone := range zones {
|
|
name := toFqdn(zone.Name)
|
|
if strings.HasSuffix(fqdn, name) {
|
|
if len(zone.Name) > len(hostedZone.Name) {
|
|
hostedZone = *zone
|
|
}
|
|
}
|
|
}
|
|
if hostedZone.ID == "" {
|
|
return "", fmt.Errorf("No matching CloudFlare zone found for domain %s", fqdn)
|
|
}
|
|
|
|
return hostedZone.ID, nil
|
|
}
|
|
|
|
func newTxtRecord(zoneID, fqdn, value string, ttl int) *cloudflare.Record {
|
|
name := unFqdn(fqdn)
|
|
return &cloudflare.Record{
|
|
Type: "TXT",
|
|
Name: name,
|
|
Content: value,
|
|
TTL: sanitizeTTL(ttl),
|
|
ZoneID: zoneID,
|
|
}
|
|
}
|
|
|
|
// TTL must be between 120 and 86400 seconds
|
|
func sanitizeTTL(ttl int) int {
|
|
switch {
|
|
case ttl < 120:
|
|
return 120
|
|
case ttl > 86400:
|
|
return 86400
|
|
default:
|
|
return ttl
|
|
}
|
|
}
|
|
|
|
func cloudflareEnvAuth() (email, apiKey string) {
|
|
email = os.Getenv("CLOUDFLARE_EMAIL")
|
|
apiKey = os.Getenv("CLOUDFLARE_API_KEY")
|
|
if len(email) == 0 || len(apiKey) == 0 {
|
|
return "", ""
|
|
}
|
|
return
|
|
}
|