Skip to content

Commit

Permalink
Merge pull request Cloud-Foundations#41 from rgooch/refactor-dns
Browse files Browse the repository at this point in the history
Refactor dns
  • Loading branch information
rgooch authored Apr 15, 2021
2 parents 3588339 + 905265c commit fd78f17
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 113 deletions.
12 changes: 12 additions & 0 deletions pkg/crypto/certmanager/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ type CertificateManager struct {
certificate *Certificate
}

// DnsRecordDeleteWriter is an interface to a DNS record manager.
type DnsRecordDeleteWriter interface {
DeleteRecords(fqdn, recType string) error
WriteRecords(fqdn, recType string, recs []string, ttl time.Duration) error
}

type keyMakerFunc func() (crypto.Signer, error)

// Locker is an interface to a remote locking mechanism.
Expand Down Expand Up @@ -136,3 +142,9 @@ func (cm *CertificateManager) GetCertificate(hello *tls.ClientHelloInfo) (
func (cm *CertificateManager) GetWriteNotifier() <-chan struct{} {
return cm.writeNotifier
}

// MakeDnsResponder will create a dns-01 Responder from a DNS record manager.
func MakeDnsResponder(rdw DnsRecordDeleteWriter,
logger log.DebugLogger) (Responder, error) {
return makeDnsResponder(rdw, logger)
}
45 changes: 45 additions & 0 deletions pkg/crypto/certmanager/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@ package certmanager

import (
"golang.org/x/crypto/acme"
"time"

"github.com/Cloud-Foundations/golib/pkg/log"
)

type dnsResponder struct {
rdw DnsRecordDeleteWriter
logger log.DebugLogger
// Mutable data follow.
records map[string]string
}

func (cm *CertificateManager) respondDNS(domain string,
challenge *acme.Challenge) error {
response, err := cm.acmeClient.DNS01ChallengeRecord(challenge.Token)
Expand All @@ -12,3 +22,38 @@ func (cm *CertificateManager) respondDNS(domain string,
}
return cm.responder.Respond("_acme-challenge."+domain, response)
}

func makeDnsResponder(rdw DnsRecordDeleteWriter,
logger log.DebugLogger) (Responder, error) {
return &dnsResponder{
rdw: rdw,
logger: logger,
records: make(map[string]string),
}, nil
}

func (r *dnsResponder) Cleanup() {
if len(r.records) < 1 {
return
}
for fqdn := range r.records {
if err := r.rdw.DeleteRecords(fqdn, "TXT"); err != nil {
r.logger.Println(err)
} else {
delete(r.records, fqdn)
}
}
}

func (r *dnsResponder) Respond(key, value string) error {
if r.records[key] == value {
return nil
}
r.logger.Debugf(1, "publishing %s TXT=\"%s\"\n", key, value)
err := r.rdw.WriteRecords(key, "TXT", []string{value}, time.Second*15)
if err != nil {
return err
}
r.records[key] = value
return nil
}
20 changes: 2 additions & 18 deletions pkg/crypto/certmanager/dns/route53/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,13 @@ Package route53 implements a dns-01 ACME protocol responder using AWS Route 53.
package route53

import (
"github.com/Cloud-Foundations/golib/pkg/crypto/certmanager"
"github.com/Cloud-Foundations/golib/pkg/log"
"github.com/aws/aws-sdk-go/service/route53"
)

type Responder struct {
awsService *route53.Route53
hostedZoneId *string
logger log.DebugLogger
// Mutable data follow.
records map[string]string
}

// New creates a DNS responder for ACME dns-01 challenges.
// The logger is used for logging messages.
func New(hostedZoneId string,
logger log.DebugLogger) (*Responder, error) {
logger log.DebugLogger) (certmanager.Responder, error) {
return newResponder(hostedZoneId, logger)
}

func (r *Responder) Cleanup() {
r.cleanup()
}

func (r *Responder) Respond(key, value string) error {
return r.respond(key, value)
}
99 changes: 6 additions & 93 deletions pkg/crypto/certmanager/dns/route53/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,16 @@ package route53

import (
"errors"
"time"

"github.com/Cloud-Foundations/golib/pkg/crypto/certmanager"
"github.com/Cloud-Foundations/golib/pkg/dns/route53"
"github.com/Cloud-Foundations/golib/pkg/log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
)

// const defaultRegion = "us-west-2"

func makeTXT(action, fqdn, txtValue string) *route53.Change {
return &route53.Change{
Action: aws.String(action),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: aws.String(fqdn),
ResourceRecords: []*route53.ResourceRecord{{
Value: aws.String(`"` + txtValue + `"`),
}},
TTL: aws.Int64(15),
Type: aws.String("TXT"),
},
}
}

func waitForChange(awsService *route53.Route53, id *string,
logger log.DebugLogger) error {
timer := time.NewTimer(time.Minute * 2)
errorChannel := make(chan error, 1)
go func() {
errorChannel <- awsService.WaitUntilResourceRecordSetsChanged(
&route53.GetChangeInput{Id: id})
}()
select {
case <-timer.C:
output, err := awsService.GetChange(&route53.GetChangeInput{Id: id})
if err != nil {
logger.Printf("timed out waiting for change: %s, hoping for the best, error from GetChange(): %s\n",
*id, err)
return nil
}
logger.Printf(
"timed out waiting for change: %s, hoping for the best, status: %s\n",
id, *output.ChangeInfo.Status)
return nil
case err := <-errorChannel:
return err
}
}

func newResponder(hostedZoneId string,
logger log.DebugLogger) (*Responder, error) {
logger log.DebugLogger) (certmanager.Responder, error) {
if hostedZoneId == "" {
return nil, errors.New("no hosted zone ID specified")
}
Expand All @@ -63,55 +22,9 @@ func newResponder(hostedZoneId string,
if awsSession == nil {
return nil, errors.New("awsSession == nil")
}
return &Responder{
awsService: route53.New(awsSession),
hostedZoneId: aws.String(hostedZoneId),
logger: logger,
records: make(map[string]string),
}, nil
}

func (r *Responder) cleanup() {
if len(r.records) < 1 {
return
}
changeBatch := make([]*route53.Change, 0, len(r.records))
for fqdn, txtValue := range r.records {
changeBatch = append(changeBatch, makeTXT("DELETE", fqdn, txtValue))
}
input := route53.ChangeResourceRecordSetsInput{
ChangeBatch: &route53.ChangeBatch{Changes: changeBatch},
HostedZoneId: r.hostedZoneId,
}
_, err := r.awsService.ChangeResourceRecordSets(&input)
if err != nil {
return
}
r.records = make(map[string]string)
}

func (r *Responder) respond(key, value string) error {
if r.records[key] == value {
return nil
}
r.logger.Debugf(1, "publishing %s TXT=\"%s\"\n", key, value)
input := route53.ChangeResourceRecordSetsInput{
ChangeBatch: &route53.ChangeBatch{Changes: []*route53.Change{
makeTXT("UPSERT", key, value)},
},
HostedZoneId: r.hostedZoneId,
}
output, err := r.awsService.ChangeResourceRecordSets(&input)
rdw, err := route53.New(awsSession, hostedZoneId, logger)
if err != nil {
return err
}
r.logger.Debugf(1, "waiting for change: %s to complete\n",
*output.ChangeInfo.Id)
err = waitForChange(r.awsService, output.ChangeInfo.Id, r.logger)
if err != nil {
return err
return nil, err
}
r.records[key] = value
r.logger.Debugf(1, "change: %s completed\n", *output.ChangeInfo.Id)
return nil
return certmanager.MakeDnsResponder(rdw, logger)
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,31 @@ func stripQuotes(value string) string {
return value
}

func waitForChange(awsService *route53.Route53, id *string,
logger log.DebugLogger) error {
timer := time.NewTimer(time.Minute * 2)
errorChannel := make(chan error, 1)
go func() {
errorChannel <- awsService.WaitUntilResourceRecordSetsChanged(
&route53.GetChangeInput{Id: id})
}()
select {
case <-timer.C:
output, err := awsService.GetChange(&route53.GetChangeInput{Id: id})
if err != nil {
logger.Printf("timed out waiting for change: %s, hoping for the best, error from GetChange(): %s\n",
*id, err)
return nil
}
logger.Printf(
"timed out waiting for change: %s, hoping for the best, status: %s\n",
id, *output.ChangeInfo.Status)
return nil
case err := <-errorChannel:
return err
}
}

func (rrw *RecordReadWriter) deleteRecords(fqdn, recType string) error {
if fqdn[len(fqdn)-1] != '.' {
fqdn += "."
Expand Down Expand Up @@ -127,6 +152,16 @@ func (rrw *RecordReadWriter) writeRecords(fqdn, recType string,
},
HostedZoneId: rrw.hostedZoneId,
}
_, err := rrw.awsService.ChangeResourceRecordSets(input)
output, err := rrw.awsService.ChangeResourceRecordSets(input)
if err != nil {
return err
}
rrw.logger.Debugf(1, "waiting for change: %s to complete\n",
*output.ChangeInfo.Id)
err = waitForChange(rrw.awsService, output.ChangeInfo.Id, rrw.logger)
if err != nil {
return err
}
rrw.logger.Debugf(1, "change: %s completed\n", *output.ChangeInfo.Id)
return err
}
2 changes: 1 addition & 1 deletion pkg/loadbalancing/dnslb/config/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"time"

"github.com/Cloud-Foundations/golib/pkg/awsutil/metadata"
"github.com/Cloud-Foundations/golib/pkg/dns/route53"
"github.com/Cloud-Foundations/golib/pkg/loadbalancing/dnslb"
"github.com/Cloud-Foundations/golib/pkg/loadbalancing/dnslb/ec2"
"github.com/Cloud-Foundations/golib/pkg/loadbalancing/dnslb/route53"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
Expand Down

0 comments on commit fd78f17

Please sign in to comment.