You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In order to avoid giving a lowly bot access to writing DNS-records for some of our important domains, we CNAME the _acme-challenge.example.com to a "transient" zone where our cert-bot is allowed to create random TXT records to appease the ACME deities.
For the record: Let's Encrypt at least does follows CNAMEs for _acme-challenge.-records too.
I haven't had the time to whip up a proper Merge-request, mostly as I haven't found suitable public known-good CNAME test-targets.
diff --git a/services/kubernetes/cert-manager-webhook-pdns/main.go b/services/kubernetes/cert-manager-webhook-pdns/main.go
index ce73694d83..5473614222 100644
--- a/services/kubernetes/cert-manager-webhook-pdns/main.go+++ b/services/kubernetes/cert-manager-webhook-pdns/main.go@@ -7,6 +7,7 @@
"encoding/json"
"errors"
"fmt"
+ "net"
"net/http"
"os"
"strings"
@@ -33,6 +34,32 @@
defaultScheme = ""
)
+func findZone(permittedZones []string, fqdn string) (string, string, error) {+ // Find out if the fqdn already is in one of the permitted zones+ for _, zone := range permittedZones {+ if zone == fqdn || strings.HasSuffix(fqdn, "."+zone) {+ klog.InfoS("findZone hit", "fqdn", fqdn, "zone", zone)+ return fqdn, zone, nil+ }+ }++ // Has a CNAME been set up from _acme-challenge.something.tld to somewhere we can edit?+ cname, err := net.LookupCNAME(fqdn)+ if err != nil {+ klog.ErrorS(err, "LookupCNAME", "record", fqdn)+ return "", "", err+ } else if cname != "" {+ for _, zone := range permittedZones {+ if zone == cname || strings.HasSuffix(cname, "."+zone) {+ klog.InfoS("findZone CNAME hit", "fqdn", fqdn, "zone", zone)+ return cname, zone, nil+ }+ }+ }++ return "", "", fmt.Errorf("Could not find any way to create %s", fqdn)+}+
var GroupName = os.Getenv("GROUP_NAME")
func main() {
@@ -169,6 +196,13 @@
return fmt.Errorf("failed initializing powerdns provider: %v", err)
}
+ otherrecord, otherzone, err := findZone(cfg.AllowedZones, ch.ResolvedFQDN)+ if err != nil {+ return fmt.Errorf("Could not find editable zone for %s (allowed zones are %v / err '%s')", ch.ResolvedZone, cfg.AllowedZones, err)+ }+ ch.ResolvedFQDN = otherrecord+ ch.ResolvedZone = otherzone+
if !cfg.IsAllowedZone(ch.ResolvedZone) {
return fmt.Errorf("zone %s may not be edited per config (allowed zones are %v)", ch.ResolvedZone, cfg.AllowedZones)
}
@@ -177,6 +211,7 @@
if err != nil {
return fmt.Errorf("failed loading existing records for %s in domain %s: %v", ch.ResolvedFQDN, ch.ResolvedZone, err)
}
+ klog.InfoS("Got existing records", "resolvedFQDN", ch.ResolvedFQDN, "recordsCount", len(records))
// Add the record, only if it doesn't exist already
content := quote(ch.Key)
@@ -196,7 +231,14 @@
Records: records,
}
- return provider.Records.Patch(ctx, ch.ResolvedZone, &powerdns.RRsets{Sets: []powerdns.RRset{rrset}})+ err = provider.Records.Patch(ctx, ch.ResolvedZone, &powerdns.RRsets{Sets: []powerdns.RRset{rrset}})+ if err != nil {+ return fmt.Errorf("failed to create record: %s", err)+ }++ klog.InfoS("Created record", "resolvedFQDN", ch.ResolvedFQDN, "rrset", rrset)++ return nil
}
// CleanUp should delete the relevant TXT record from the DNS provider console.
@@ -215,6 +257,17 @@
return fmt.Errorf("failed initializing powerdns provider: %v", err)
}
+ otherrecord, otherzone, err := findZone(cfg.AllowedZones, ch.ResolvedFQDN)+ if err != nil {+ return fmt.Errorf("zone %s may not be edited per config (allowed zones are %v)", ch.ResolvedZone, cfg.AllowedZones)+ }+ ch.ResolvedFQDN = otherrecord+ ch.ResolvedZone = otherzone++ if !cfg.IsAllowedZone(ch.ResolvedZone) {+ return fmt.Errorf("zone %s may not be edited per config (allowed zones are %v)", ch.ResolvedZone, cfg.AllowedZones)+ }+
records, err := c.getExistingRecords(ctx, provider, ch.ResolvedZone, ch.ResolvedFQDN)
if err != nil {
return fmt.Errorf("failed loading existing records for %s in domain %s: %v", ch.ResolvedFQDN, ch.ResolvedZone, err)
diff --git a/services/kubernetes/cert-manager-webhook-pdns/main_test.go b/services/kubernetes/cert-manager-webhook-pdns/main_test.go
index ab0f6217bb..59f3b74f79 100644
--- a/services/kubernetes/cert-manager-webhook-pdns/main_test.go+++ b/services/kubernetes/cert-manager-webhook-pdns/main_test.go@@ -90,3 +90,36 @@
})
}
}
++func TestIsAllowedZonesCNAME(t *testing.T) {+ cfg := powerDNSProviderConfig{+ AllowedZones: []string{"transient.example.org.", "permitted.example.org."},+ }++ tests := []struct {+ fqdn string+ expectedFQDN string+ expectedZone string+ }{+ {+ "something.permitted.example.org.",+ "something.permitted.example.org.",+ "permitted.example.org.",+ },+ {+ # Relies on CNAME from `_acme-challenge.www.example.com` to `_acme-challenge.ww.example.com.transient.example.org`.+ "_acme-challenge.www.example.com.",+ "_acme-challenge.www.example.com.transient.example.org.",+ "transient.example.org",+ },+ }++ for _, tt := range tests {+ t.Run(tt.fqdn, func(t *testing.T) {+ fqdn, zone, err := findZone(cfg.AllowedZones, tt.fqdn)+ t.Log("output", fqdn, zone, err)+ if fqdn != tt.expectedFQDN || zone != tt.expectedZone {+ t.Errorf("Unexpected findZone([]..., %s) = %s, %s, %s, expected %s %s <nil>", tt.fqdn, fqdn, zone, err, tt.expectedFQDN, tt.expectedZone)+ }+ })+ }+}
The paths are a bit weird, as we ended up vendoring it to simplify internal releases.
The text was updated successfully, but these errors were encountered:
In order to avoid giving a lowly bot access to writing DNS-records for some of our important domains, we CNAME the
_acme-challenge.example.com
to a "transient" zone where our cert-bot is allowed to create random TXT records to appease the ACME deities.For the record: Let's Encrypt at least does follows CNAMEs for
_acme-challenge.
-records too.I haven't had the time to whip up a proper Merge-request, mostly as I haven't found suitable public known-good CNAME test-targets.
The paths are a bit weird, as we ended up vendoring it to simplify internal releases.
The text was updated successfully, but these errors were encountered: