Skip to content

Commit

Permalink
Merge pull request #7530 from SgtCoDFish/dns-test-integration
Browse files Browse the repository at this point in the history
Split tests using 'live' DNS into separate package
  • Loading branch information
cert-manager-prow[bot] authored Jan 28, 2025
2 parents 5e5ad2f + 8233733 commit 1747743
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 91 deletions.
9 changes: 9 additions & 0 deletions make/test.mk
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ test-ci: setup-integration-tests | $(NEEDS_GOTESTSUM) $(NEEDS_ETCD) $(NEEDS_KUBE
cd cmd/controller && $(GOTESTSUM) --junitfile $(ARTIFACTS)/junit_make-test-ci-controller.xml $(GOTESTSUM_CI_FLAGS) --post-run-command $$'bash -c "$(GO) run ../../hack/prune-junit-xml/prunexml.go $$GOTESTSUM_JUNITFILE"' -- ./...
cd cmd/webhook && $(GOTESTSUM) --junitfile $(ARTIFACTS)/junit_make-test-ci-webhook.xml $(GOTESTSUM_CI_FLAGS) --post-run-command $$'bash -c "$(GO) run ../../hack/prune-junit-xml/prunexml.go $$GOTESTSUM_JUNITFILE"' -- ./...
cd test/integration && $(GOTESTSUM) --junitfile $(ARTIFACTS)/junit_make-test-ci-integration.xml $(GOTESTSUM_CI_FLAGS) --post-run-command $$'bash -c "$(GO) run ../../hack/prune-junit-xml/prunexml.go $$GOTESTSUM_JUNITFILE"' -- ./...
$(GOTESTSUM) --junitfile $(ARTIFACTS)/junit_make-test-ci-livedns.xml $(GOTESTSUM_CI_FLAGS) --post-run-command $$'bash -c "$(GO) run ./hack/prune-junit-xml/prunexml.go $$GOTESTSUM_JUNITFILE"' -- --tags=livedns_test ./pkg/issuer/acme/dns/util/...

.PHONY: unit-test
## Same as `test` but only runs the unit tests. By "unit tests", we mean tests
Expand Down Expand Up @@ -105,6 +106,14 @@ setup-integration-tests: templated-crds
integration-test: setup-integration-tests | $(NEEDS_GOTESTSUM) $(NEEDS_ETCD) $(NEEDS_KUBECTL) $(NEEDS_KUBE-APISERVER) $(NEEDS_GO)
cd test/integration && $(GOTESTSUM) ./...

.PHONY: livedns-test
## "Live DNS" tests rely on external DNS records and are separated from other tests
## for various reasons detailed in the files tested by this target.
##
## @category Development
livedns-test: | $(NEEDS_GOTESTSUM) $(NEEDS_GO)
$(GOTESTSUM) -- --tags=livedns_test ./pkg/issuer/acme/dns/util/...

## (optional) Set this to true to run the E2E tests against an OpenShift cluster.
## When set to true, the Hashicorp Vault Helm chart will be installed with
## settings appropriate for OpenShift.
Expand Down
2 changes: 2 additions & 0 deletions pkg/issuer/acme/dns/util/dns_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !livedns_test

// +skip_license_check

package util
Expand Down
164 changes: 164 additions & 0 deletions pkg/issuer/acme/dns/util/livedns_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
//go:build livedns_test

/*
Copyright 2025 The cert-manager 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.
*/

/*
Addition to the above license statement:
This file *may* contain code directly taken from the 'xenolf/lego' project.
A copy of the license for this code can be found in the file named LICENSE in
this directory.
*/

package util

// The tests in this file connect to *live* DNS or DNS-over-HTTPS services,
// and rely on various DNS records which are out of the control of the cert-manager
// project. As such, these tests are:
// 1. More likely to flake due to network connectivity issues
// 2. Liable to break if the upstream DNS records change
// 3. Unable to run in restrictive computing environments
// (such as MitM corporate proxies which block DNS / DNS-over-HTTPS)

// Because of the above, these tests live behind a build tag so they're not
// run by mistake.

import (
"context"
"fmt"
"testing"
"time"
)

const (
standardTimeout = time.Second * 5
)

func TestPreCheckDNSOverHTTPS(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), standardTimeout)
defer cancel()

ok, err := PreCheckDNS(ctx, "google.com.", "v=spf1 include:_spf.google.com ~all", []string{"https://8.8.8.8/dns-query"}, true)
if err != nil || !ok {
t.Errorf("preCheckDNS failed for dns-over-https (authoritative): ok=%v err=%s", ok, err.Error())
}
}

func TestPreCheckDNSOverHTTPSNoAuthoritative(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), standardTimeout)
defer cancel()

ok, err := PreCheckDNS(ctx, "google.com.", "v=spf1 include:_spf.google.com ~all", []string{"https://1.1.1.1/dns-query"}, false)
if err != nil || !ok {
t.Errorf("preCheckDNS failed for dns-over-https (non-authoritative): ok=%v err=%s", ok, err.Error())
}
}

func TestPreCheckDNS(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), standardTimeout)
defer cancel()

ok, err := PreCheckDNS(ctx, "google.com.", "v=spf1 include:_spf.google.com ~all", []string{"8.8.8.8:53"}, true)
if err != nil || !ok {
t.Errorf("preCheckDNS failed for dns on port 53 (authoritative): ok=%v err=%s", ok, err.Error())
}
}

func TestPreCheckDNSNonAuthoritative(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), standardTimeout)
defer cancel()

ok, err := PreCheckDNS(ctx, "google.com.", "v=spf1 include:_spf.google.com ~all", []string{"1.1.1.1:53"}, false)
if err != nil || !ok {
t.Errorf("preCheckDNS failed for dns on port 53 (non-authoritative): ok=%v err=%s", ok, err.Error())
}
}

func TestCheckAuthoritativeNss(t *testing.T) {
checkAuthoritativeNssTests := []struct {
fqdn, value string
ns []string
ok bool
}{
// TXT RR w/ expected value
{"8.8.8.8.asn.routeviews.org.", "151698.8.8.024", []string{"asnums.routeviews.org.:53"},
true,
},
// No TXT RR
{"ns1.google.com.", "", []string{"ns2.google.com.:53"},
false,
},
// TXT RR w/ unexpected value
{"8.8.8.8.asn.routeviews.org.", "fe01=", []string{"asnums.routeviews.org.:53"},
false,
},
}

for i, tt := range checkAuthoritativeNssTests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), standardTimeout)
defer cancel()

ok, _ := checkAuthoritativeNss(ctx, tt.fqdn, tt.value, tt.ns)
if ok != tt.ok {
t.Errorf("%s: got %t; want %t", tt.fqdn, ok, tt.ok)
}
})
}
}

func TestValidateCAA(t *testing.T) {
// TODO: find a website which uses issuewild?

for i, nameservers := range [][]string{RecursiveNameservers, {"https://1.1.1.1/dns-query"}, {"https://8.8.8.8/dns-query"}} {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
// use a longer timeout since we have more external calls
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()

// google installs a CAA record at google.com
// ask for the www.google.com record to test that
// we recurse up the labels
err := ValidateCAA(ctx, "www.google.com", []string{"letsencrypt", "pki.goog"}, false, nameservers)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}

// now ask, expecting a CA that won't match
err = ValidateCAA(ctx, "www.google.com", []string{"daniel.homebrew.ca"}, false, nameservers)
if err == nil {
t.Fatalf("expected err, got success")
}

// if the CAA record allows non-wildcards then it has an `issue` tag,
// and it is known that it has no issuewild tags, then wildcard certificates
// will also be allowed
err = ValidateCAA(ctx, "www.google.com", []string{"pki.goog"}, true, nameservers)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}

// ask for a domain you know does not have CAA records.
// it should succeed
err = ValidateCAA(ctx, "www.example.org", []string{"daniel.homebrew.ca"}, false, nameservers)
if err != nil {
t.Fatalf("expected err, got %s", err)
}
})
}
}
93 changes: 2 additions & 91 deletions pkg/issuer/acme/dns/util/wait_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !livedns_test

// +skip_license_check

/*
Expand Down Expand Up @@ -57,25 +59,6 @@ var findZoneByFqdnTests = []struct {
{"cross-zone-example.assets.sh.", "assets.sh."}, // domain is a cross-zone CNAME
}

var checkAuthoritativeNssTests = []struct {
fqdn, value string
ns []string
ok bool
}{
// TXT RR w/ expected value
{"8.8.8.8.asn.routeviews.org.", "151698.8.8.024", []string{"asnums.routeviews.org.:53"},
true,
},
// No TXT RR
{"ns1.google.com.", "", []string{"ns2.google.com.:53"},
false,
},
// TXT RR /w unexpected value
{"8.8.8.8.asn.routeviews.org.", "fe01=", []string{"asnums.routeviews.org.:53"},
false,
},
}

var checkAuthoritativeNssTestsErr = []struct {
fqdn, value string
ns []string
Expand Down Expand Up @@ -172,36 +155,6 @@ func TestMatchCAA(t *testing.T) {
}
}

func TestPreCheckDNSOverHTTPSNoAuthoritative(t *testing.T) {
ok, err := PreCheckDNS(context.TODO(), "google.com.", "v=spf1 include:_spf.google.com ~all", []string{"https://1.1.1.1/dns-query"}, false)
if err != nil || !ok {
t.Errorf("preCheckDNS failed for acme-staging.api.letsencrypt.org: %s", err.Error())
}
}

func TestPreCheckDNSOverHTTPS(t *testing.T) {
ok, err := PreCheckDNS(context.TODO(), "google.com.", "v=spf1 include:_spf.google.com ~all", []string{"https://8.8.8.8/dns-query"}, true)
if err != nil || !ok {
t.Errorf("preCheckDNS failed for acme-staging.api.letsencrypt.org: %s", err.Error())
}
}

func TestPreCheckDNS(t *testing.T) {
// TODO: find a better TXT record to use in tests
ok, err := PreCheckDNS(context.TODO(), "google.com.", "v=spf1 include:_spf.google.com ~all", []string{"8.8.8.8:53"}, true)
if err != nil || !ok {
t.Errorf("preCheckDNS failed for acme-staging.api.letsencrypt.org: %s", err.Error())
}
}

func TestPreCheckDNSNonAuthoritative(t *testing.T) {
// TODO: find a better TXT record to use in tests
ok, err := PreCheckDNS(context.TODO(), "google.com.", "v=spf1 include:_spf.google.com ~all", []string{"1.1.1.1:53"}, false)
if err != nil || !ok {
t.Errorf("preCheckDNS failed for acme-staging.api.letsencrypt.org: %s", err.Error())
}
}

func TestLookupNameserversOK(t *testing.T) {
for _, tt := range lookupNameserversTestsOK {
nss, err := lookupNameservers(context.TODO(), tt.fqdn, RecursiveNameservers)
Expand Down Expand Up @@ -244,15 +197,6 @@ func TestFindZoneByFqdn(t *testing.T) {
}
}

func TestCheckAuthoritativeNss(t *testing.T) {
for _, tt := range checkAuthoritativeNssTests {
ok, _ := checkAuthoritativeNss(context.TODO(), tt.fqdn, tt.value, tt.ns)
if ok != tt.ok {
t.Errorf("%s: got %t; want %t", tt.fqdn, ok, tt.ok)
}
}
}

func TestCheckAuthoritativeNssErr(t *testing.T) {
for _, tt := range checkAuthoritativeNssTestsErr {
_, err := checkAuthoritativeNss(context.TODO(), tt.fqdn, tt.value, tt.ns)
Expand All @@ -278,39 +222,6 @@ func TestResolveConfServers(t *testing.T) {
}
}

// TODO: find a website which uses issuewild?
func TestValidateCAA(t *testing.T) {

for _, nameservers := range [][]string{RecursiveNameservers, {"https://1.1.1.1/dns-query"}, {"https://8.8.8.8/dns-query"}} {

// google installs a CAA record at google.com
// ask for the www.google.com record to test that
// we recurse up the labels
err := ValidateCAA(context.TODO(), "www.google.com", []string{"letsencrypt", "pki.goog"}, false, nameservers)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// now ask, expecting a CA that won't match
err = ValidateCAA(context.TODO(), "www.google.com", []string{"daniel.homebrew.ca"}, false, nameservers)
if err == nil {
t.Fatalf("expected err, got success")
}
// if the CAA record allows non-wildcards then it has an `issue` tag,
// and it is known that it has no issuewild tags, then wildcard certificates
// will also be allowed
err = ValidateCAA(context.TODO(), "www.google.com", []string{"pki.goog"}, true, nameservers)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// ask for a domain you know does not have CAA records.
// it should succeed
err = ValidateCAA(context.TODO(), "www.example.org", []string{"daniel.homebrew.ca"}, false, nameservers)
if err != nil {
t.Fatalf("expected err, got %s", err)
}
}
}

func Test_followCNAMEs(t *testing.T) {
dnsQuery = func(ctx context.Context, fqdn string, rtype uint16, nameservers []string, recursive bool) (in *dns.Msg, err error) {
msg := &dns.Msg{}
Expand Down

0 comments on commit 1747743

Please sign in to comment.