Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC2136: Allow multiple zones #3976

Merged
merged 8 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/tutorials/rfc2136.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ spec:
- --rfc2136-host=192.168.0.1
- --rfc2136-port=53
- --rfc2136-zone=k8s.example.org
- --rfc2136-zone=k8s.your-zone.org
- --rfc2136-tsig-secret=96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8=
- --rfc2136-tsig-secret-alg=hmac-sha256
- --rfc2136-tsig-keyname=externaldns-key
Expand Down Expand Up @@ -269,6 +270,7 @@ spec:
- --rfc2136-host=192.168.0.1
- --rfc2136-port=53
- --rfc2136-zone=k8s.example.org
- --rfc2136-zone=k8s.your-zone.org
- --rfc2136-tsig-secret=96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8=
- --rfc2136-tsig-secret-alg=hmac-sha256
- --rfc2136-tsig-keyname=externaldns-key
Expand Down Expand Up @@ -299,6 +301,7 @@ You'll want to configure `external-dns` similarly to the following:
- --rfc2136-host=192.168.0.1
- --rfc2136-port=53
- --rfc2136-zone=k8s.example.org
- --rfc2136-zone=k8s.your-zone.org
- --rfc2136-insecure
- --rfc2136-tsig-axfr # needed to enable zone transfers, which is required for deletion of records.
...
Expand Down Expand Up @@ -384,6 +387,7 @@ You'll want to configure `external-dns` similarly to the following:
- --rfc2136-host=dns-host.yourdomain.com
- --rfc2136-port=53
- --rfc2136-zone=your-zone.com
- --rfc2136-zone=your-secondary-zone.com
- --rfc2136-kerberos-username=your-domain-account
- --rfc2136-kerberos-password=your-domain-password
- --rfc2136-kerberos-realm=your-domain.com
Expand Down
6 changes: 3 additions & 3 deletions pkg/apis/externaldns/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ type Config struct {
ResolveServiceLoadBalancerHostname bool
RFC2136Host string
RFC2136Port int
RFC2136Zone string
RFC2136Zone []string
RFC2136Insecure bool
RFC2136GSSTSIG bool
RFC2136KerberosRealm string
Expand Down Expand Up @@ -330,7 +330,7 @@ var defaultConfig = &Config{
CFPassword: "",
RFC2136Host: "",
RFC2136Port: 0,
RFC2136Zone: "",
RFC2136Zone: []string{},
RFC2136Insecure: false,
RFC2136GSSTSIG: false,
RFC2136KerberosRealm: "",
Expand Down Expand Up @@ -558,7 +558,7 @@ func (cfg *Config) ParseFlags(args []string) error {
// Flags related to RFC2136 provider
app.Flag("rfc2136-host", "When using the RFC2136 provider, specify the host of the DNS server").Default(defaultConfig.RFC2136Host).StringVar(&cfg.RFC2136Host)
app.Flag("rfc2136-port", "When using the RFC2136 provider, specify the port of the DNS server").Default(strconv.Itoa(defaultConfig.RFC2136Port)).IntVar(&cfg.RFC2136Port)
app.Flag("rfc2136-zone", "When using the RFC2136 provider, specify the zone entry of the DNS server to use").Default(defaultConfig.RFC2136Zone).StringVar(&cfg.RFC2136Zone)
app.Flag("rfc2136-zone", "When using the RFC2136 provider, specify zone entries of the DNS server to use").StringsVar(&cfg.RFC2136Zone)
app.Flag("rfc2136-insecure", "When using the RFC2136 provider, specify whether to attach TSIG or not (default: false, requires --rfc2136-tsig-keyname and rfc2136-tsig-secret)").Default(strconv.FormatBool(defaultConfig.RFC2136Insecure)).BoolVar(&cfg.RFC2136Insecure)
app.Flag("rfc2136-tsig-keyname", "When using the RFC2136 provider, specify the TSIG key to attached to DNS messages (required when --rfc2136-insecure=false)").Default(defaultConfig.RFC2136TSIGKeyName).StringVar(&cfg.RFC2136TSIGKeyName)
app.Flag("rfc2136-tsig-secret", "When using the RFC2136 provider, specify the TSIG (base64) value to attached to DNS messages (required when --rfc2136-insecure=false)").Default(defaultConfig.RFC2136TSIGSecret).StringVar(&cfg.RFC2136TSIGSecret)
Expand Down
86 changes: 57 additions & 29 deletions provider/rfc2136/rfc2136.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"net"
"sort"
"strconv"
"strings"
"time"
Expand All @@ -45,7 +46,7 @@ const (
type rfc2136Provider struct {
provider.BaseProvider
nameserver string
zoneName string
zoneNames []string
tsigKeyName string
tsigSecret string
tsigSecretAlg string
Expand Down Expand Up @@ -81,19 +82,25 @@ type rfc2136Actions interface {
}

// NewRfc2136Provider is a factory function for OpenStack rfc2136 providers
func NewRfc2136Provider(host string, port int, zoneName string, insecure bool, keyName string, secret string, secretAlg string, axfr bool, domainFilter endpoint.DomainFilter, dryRun bool, minTTL time.Duration, gssTsig bool, krb5Username string, krb5Password string, krb5Realm string, batchChangeSize int, actions rfc2136Actions) (provider.Provider, error) {
func NewRfc2136Provider(host string, port int, zoneNames []string, insecure bool, keyName string, secret string, secretAlg string, axfr bool, domainFilter endpoint.DomainFilter, dryRun bool, minTTL time.Duration, gssTsig bool, krb5Username string, krb5Password string, krb5Realm string, batchChangeSize int, actions rfc2136Actions) (provider.Provider, error) {
secretAlgChecked, ok := tsigAlgs[secretAlg]
if !ok && !insecure && !gssTsig {
return nil, errors.Errorf("%s is not supported TSIG algorithm", secretAlg)
}

if krb5Realm == "" {
krb5Realm = strings.ToUpper(zoneName)
// Set zone to root if no set
if len(zoneNames) == 0 {
zoneNames = append(zoneNames, ".")
}

// Sort zones
sort.Slice(zoneNames, func(i, j int) bool {
return len(strings.Split(zoneNames[i], ".")) > len(strings.Split(zoneNames[j], "."))
})

r := &rfc2136Provider{
nameserver: net.JoinHostPort(host, strconv.Itoa(port)),
zoneName: dns.Fqdn(zoneName),
zoneNames: zoneNames,
insecure: insecure,
gssTsig: gssTsig,
krb5Username: krb5Username,
Expand All @@ -117,7 +124,7 @@ func NewRfc2136Provider(host string, port int, zoneName string, insecure bool, k
r.tsigSecretAlg = secretAlgChecked
}

log.Infof("Configured RFC2136 with zone '%s' and nameserver '%s'", r.zoneName, r.nameserver)
log.Infof("Configured RFC2136 with zone '%s' and nameserver '%s'", r.zoneNames, r.nameserver)
return r, nil
}

Expand Down Expand Up @@ -209,30 +216,32 @@ func (r rfc2136Provider) List() ([]dns.RR, error) {
return make([]dns.RR, 0), nil
}

log.Debugf("Fetching records for '%s'", r.zoneName)
records := make([]dns.RR, 0)
for _, zone := range r.zoneNames {
log.Debugf("Fetching records for '%q'", zone)

m := new(dns.Msg)
m.SetAxfr(r.zoneName)
if !r.insecure && !r.gssTsig {
m.SetTsig(r.tsigKeyName, r.tsigSecretAlg, clockSkew, time.Now().Unix())
}
m := new(dns.Msg)
m.SetAxfr(dns.Fqdn(zone))
if !r.insecure && !r.gssTsig {
m.SetTsig(r.tsigKeyName, r.tsigSecretAlg, clockSkew, time.Now().Unix())
}

env, err := r.actions.IncomeTransfer(m, r.nameserver)
if err != nil {
return nil, fmt.Errorf("failed to fetch records via AXFR: %v", err)
}
env, err := r.actions.IncomeTransfer(m, r.nameserver)
if err != nil {
return nil, fmt.Errorf("failed to fetch records via AXFR: %w", err)
}

records := make([]dns.RR, 0)
for e := range env {
if e.Error != nil {
if e.Error == dns.ErrSoa {
log.Error("AXFR error: unexpected response received from the server")
} else {
log.Errorf("AXFR error: %v", e.Error)
for e := range env {
if e.Error != nil {
if e.Error == dns.ErrSoa {
log.Error("AXFR error: unexpected response received from the server")
} else {
log.Errorf("AXFR error: %v", e.Error)
}
continue
}
continue
records = append(records, e.RR...)
}
records = append(records, e.RR...)
}

return records, nil
Expand All @@ -248,14 +257,16 @@ func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes
log.Debugf("Processing batch %d of create changes", c)

m := new(dns.Msg)
m.SetUpdate(r.zoneName)

for _, ep := range chunk {
if !r.domainFilter.Match(ep.DNSName) {
log.Debugf("Skipping record %s because it was filtered out by the specified --domain-filter", ep.DNSName)
continue
}

zone := findMsgZone(ep, r.zoneNames)
r.krb5Realm = strings.ToUpper(zone)
m.SetUpdate(zone)

r.AddRecord(m, ep)
}

Expand All @@ -274,14 +285,17 @@ func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes
log.Debugf("Processing batch %d of update changes", c)

m := new(dns.Msg)
m.SetUpdate(r.zoneName)

for i, ep := range chunk {
if !r.domainFilter.Match(ep.DNSName) {
log.Debugf("Skipping record %s because it was filtered out by the specified --domain-filter", ep.DNSName)
continue
}

zone := findMsgZone(ep, r.zoneNames)
r.krb5Realm = strings.ToUpper(zone)
m.SetUpdate(zone)

r.UpdateRecord(m, changes.UpdateOld[i], ep)
}

Expand All @@ -300,14 +314,17 @@ func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes
log.Debugf("Processing batch %d of delete changes", c)

m := new(dns.Msg)
m.SetUpdate(r.zoneName)

for _, ep := range chunk {
if !r.domainFilter.Match(ep.DNSName) {
log.Debugf("Skipping record %s because it was filtered out by the specified --domain-filter", ep.DNSName)
continue
}

zone := findMsgZone(ep, r.zoneNames)
r.krb5Realm = strings.ToUpper(zone)
m.SetUpdate(zone)

r.RemoveRecord(m, ep)
}

Expand Down Expand Up @@ -439,3 +456,14 @@ func chunkBy(slice []*endpoint.Endpoint, chunkSize int) [][]*endpoint.Endpoint {

return chunks
}

func findMsgZone(ep *endpoint.Endpoint, zoneNames []string) string {
for _, zone := range zoneNames {
if strings.HasSuffix(ep.DNSName, zone) {
return dns.Fqdn(zone)
}
}

log.Warnf("No available zone found for %s, set it to 'root'", ep.DNSName)
return dns.Fqdn(".")
}
2 changes: 1 addition & 1 deletion provider/rfc2136/rfc2136_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (r *rfc2136Stub) IncomeTransfer(m *dns.Msg, a string) (env chan *dns.Envelo
}

func createRfc2136StubProvider(stub *rfc2136Stub) (provider.Provider, error) {
return NewRfc2136Provider("", 0, "", false, "key", "secret", "hmac-sha512", true, endpoint.DomainFilter{}, false, 300*time.Second, false, "", "", "", 50, stub)
return NewRfc2136Provider("", 0, nil, false, "key", "secret", "hmac-sha512", true, endpoint.DomainFilter{}, false, 300*time.Second, false, "", "", "", 50, stub)
}

func extractAuthoritySectionFromMessage(msg fmt.Stringer) []string {
Expand Down
Loading