summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go
blob: 3b6b58d08ce7907db25a4aecce413d2103d4b5cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Package exoscale implements a DNS provider for solving the DNS-01 challenge
// using exoscale DNS.
package exoscale

import (
	"errors"
	"fmt"
	"os"

	"github.com/pyr/egoscale/src/egoscale"
	"github.com/xenolf/lego/acme"
)

// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
	client *egoscale.Client
}

// Credentials must be passed in the environment variables:
// EXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT.
func NewDNSProvider() (*DNSProvider, error) {
	key := os.Getenv("EXOSCALE_API_KEY")
	secret := os.Getenv("EXOSCALE_API_SECRET")
	endpoint := os.Getenv("EXOSCALE_ENDPOINT")
	return NewDNSProviderClient(key, secret, endpoint)
}

// Uses the supplied parameters to return a DNSProvider instance
// configured for Exoscale.
func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) {
	if key == "" || secret == "" {
		return nil, fmt.Errorf("Exoscale credentials missing")
	}
	if endpoint == "" {
		endpoint = "https://api.exoscale.ch/dns"
	}

	return &DNSProvider{
		client: egoscale.NewClient(endpoint, key, secret),
	}, nil
}

// Present creates a TXT record to fulfil the dns-01 challenge.
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
	fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
	zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain)
	if err != nil {
		return err
	}

	recordId, err := c.FindExistingRecordId(zone, recordName)
	if err != nil {
		return err
	}

	record := egoscale.DNSRecord{
		Name:       recordName,
		Ttl:        ttl,
		Content:    value,
		RecordType: "TXT",
	}

	if recordId == 0 {
		_, err := c.client.CreateRecord(zone, record)
		if err != nil {
			return errors.New("Error while creating DNS record: " + err.Error())
		}
	} else {
		record.Id = recordId
		_, err := c.client.UpdateRecord(zone, record)
		if err != nil {
			return errors.New("Error while updating DNS record: " + err.Error())
		}
	}

	return nil
}

// CleanUp removes the record matching the specified parameters.
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
	fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
	zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain)
	if err != nil {
		return err
	}

	recordId, err := c.FindExistingRecordId(zone, recordName)
	if err != nil {
		return err
	}

	if recordId != 0 {
		record := egoscale.DNSRecord{
			Id: recordId,
		}

		err = c.client.DeleteRecord(zone, record)
		if err != nil {
			return errors.New("Error while deleting DNS record: " + err.Error())
		}
	}

	return nil
}

// Query Exoscale to find an existing record for this name.
// Returns nil if no record could be found
func (c *DNSProvider) FindExistingRecordId(zone, recordName string) (int64, error) {
	responses, err := c.client.GetRecords(zone)
	if err != nil {
		return -1, errors.New("Error while retrievening DNS records: " + err.Error())
	}
	for _, response := range responses {
		if response.Record.Name == recordName {
			return response.Record.Id, nil
		}
	}
	return 0, nil
}

// Extract DNS zone and DNS entry name
func (c *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) {
	zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
	if err != nil {
		return "", "", err
	}
	zone = acme.UnFqdn(zone)
	name := acme.UnFqdn(fqdn)
	name = name[:len(name)-len("."+zone)]

	return zone, name, nil
}