summaryrefslogtreecommitdiff
path: root/modules/caddytls
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2020-05-02 17:23:36 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2020-05-02 17:23:36 -0600
commit8e4266106034819fa0f4be8f0efbd628eb3e1634 (patch)
treece92801fb98aaa3ff0bd16575fded67b9fbfcb78 /modules/caddytls
parent86a4f2c9f4e7f85f2d59e2f1713aacf98ab8bc1a (diff)
caddytls: Finish upgrading to libdns DNS providers for ACME challenges
Until we finish the migration to the new acme library, we have to bring the solver type in-house. It's small and temporary.
Diffstat (limited to 'modules/caddytls')
-rw-r--r--modules/caddytls/acmeissuer.go16
-rw-r--r--modules/caddytls/dnssolver.go113
2 files changed, 128 insertions, 1 deletions
diff --git a/modules/caddytls/acmeissuer.go b/modules/caddytls/acmeissuer.go
index dcdfc13..4295cda 100644
--- a/modules/caddytls/acmeissuer.go
+++ b/modules/caddytls/acmeissuer.go
@@ -91,7 +91,21 @@ func (m *ACMEIssuer) Provision(ctx caddy.Context) error {
if err != nil {
return fmt.Errorf("loading DNS provider module: %v", err)
}
- m.Challenges.DNS.provider = val.(challenge.Provider)
+ // TODO: For a temporary amount of time, we are allowing the use of
+ // DNS providers from go-acme/lego since there are so many implemented
+ // for it -- they are adapted as Caddy modules in this repository:
+ // https://github.com/caddy-dns/lego-deprecated - that module is
+ // a challenge.Provider value, so we use it directly. The user must set
+ // environment variables to configure it. Remove this shim once a sufficient
+ // number of DNS providers are implemented for the libdns APIs instead.
+ if grandfatheredProvider, ok := val.(challenge.Provider); ok {
+ m.Challenges.DNS.provider = grandfatheredProvider
+ } else {
+ m.Challenges.DNS.provider = &solver{
+ recordManager: val.(recordManager),
+ TTL: time.Duration(m.Challenges.DNS.TTL),
+ }
+ }
}
// add any custom CAs to trust store
diff --git a/modules/caddytls/dnssolver.go b/modules/caddytls/dnssolver.go
new file mode 100644
index 0000000..c8a9c3a
--- /dev/null
+++ b/modules/caddytls/dnssolver.go
@@ -0,0 +1,113 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package caddytls
+
+import (
+ "context"
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/go-acme/lego/v3/challenge"
+ "github.com/go-acme/lego/v3/challenge/dns01"
+ "github.com/libdns/libdns"
+)
+
+// TODO: this is borrowed from https://github.com/mholt/acme - once we
+// switch to that acme library, this file will go away
+
+// solver is a type that makes libdns providers usable as ACME challenge solvers.
+type solver struct {
+ recordManager
+
+ TTL time.Duration
+
+ txtRecords map[string]libdns.Record // keyed by challenge token
+ txtRecordsMu sync.Mutex
+}
+
+func (s *solver) Present(domain, token, keyAuth string) error {
+ fqdn, value := dns01.GetRecord(domain, keyAuth)
+
+ rec := libdns.Record{
+ Type: "TXT",
+ Name: fqdn,
+ Value: value,
+ TTL: s.TTL,
+ }
+
+ zone, err := dns01.FindZoneByFqdn(fqdn)
+ if err != nil {
+ return fmt.Errorf("could not determine zone for domain %q: %v", fqdn, err)
+ }
+
+ results, err := s.recordManager.AppendRecords(context.TODO(), zone, []libdns.Record{rec})
+ if err != nil {
+ return err
+ }
+ if len(results) != 1 {
+ return fmt.Errorf("expected one record, got %d: %v", len(results), results)
+ }
+
+ // keep this record handy so we can clean it up more efficiently
+ s.txtRecordsMu.Lock()
+ if s.txtRecords == nil {
+ s.txtRecords = make(map[string]libdns.Record)
+ }
+ s.txtRecords[keyAuth] = results[0]
+ s.txtRecordsMu.Unlock()
+
+ // TODO: check for record propagation before continuing (accordig to config)
+
+ return nil
+}
+
+func (s *solver) CleanUp(domain, token, keyAuth string) error {
+ fqdn, _ := dns01.GetRecord(domain, keyAuth)
+ authZone, err := dns01.FindZoneByFqdn(fqdn)
+ if err != nil {
+ return err
+ }
+
+ // retrieve the record we created
+ s.txtRecordsMu.Lock()
+ txtRec, ok := s.txtRecords[keyAuth]
+ if !ok {
+ s.txtRecordsMu.Unlock()
+ return fmt.Errorf("no memory of presenting a DNS record for %v", domain)
+ }
+ s.txtRecordsMu.Unlock()
+
+ // clean up the record
+ _, err = s.recordManager.DeleteRecords(context.TODO(), authZone, []libdns.Record{txtRec})
+ if err != nil {
+ return err
+ }
+
+ // once it has been successfully cleaned up, we can forget about it
+ s.txtRecordsMu.Lock()
+ delete(s.txtRecords, keyAuth)
+ s.txtRecordsMu.Unlock()
+
+ return nil
+}
+
+// recordManager defines the set of operations required for ACME challenges.
+type recordManager interface {
+ libdns.RecordAppender
+ libdns.RecordDeleter
+}
+
+var _ challenge.Provider = (*solver)(nil)