github.com/letsencrypt/boulder@v0.20251208.0/va/dns_account_test.go (about)

     1  // dns_account_test.go
     2  package va
     3  
     4  import (
     5  	"errors"
     6  	"net/netip"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/jmhodges/clock"
    12  
    13  	"github.com/letsencrypt/boulder/bdns"
    14  	berrors "github.com/letsencrypt/boulder/errors"
    15  	"github.com/letsencrypt/boulder/identifier"
    16  	"github.com/letsencrypt/boulder/metrics"
    17  	"github.com/letsencrypt/boulder/probs"
    18  	"github.com/letsencrypt/boulder/test"
    19  )
    20  
    21  // Use a consistent test account URI, matching the example in the draft
    22  const testAccountURI = "https://example.com/acme/acct/ExampleAccount"
    23  
    24  func TestDNSAccount01Validation(t *testing.T) {
    25  	testCases := []struct {
    26  		name        string
    27  		ident       identifier.ACMEIdentifier
    28  		wantErrType berrors.ErrorType
    29  		wantErrMsg  string
    30  	}{
    31  		{
    32  			name:        "wrong TXT record",
    33  			ident:       identifier.NewDNS("wrong-dns01.com"),
    34  			wantErrType: berrors.Unauthorized,
    35  			wantErrMsg:  "Incorrect TXT record",
    36  		},
    37  		{
    38  			name:        "wrong TXT record with multiple values",
    39  			ident:       identifier.NewDNS("wrong-many-dns01.com"),
    40  			wantErrType: berrors.Unauthorized,
    41  			wantErrMsg:  "Incorrect TXT record",
    42  		},
    43  		{
    44  			name:        "wrong long TXT record",
    45  			ident:       identifier.NewDNS("long-dns01.com"),
    46  			wantErrType: berrors.Unauthorized,
    47  			wantErrMsg:  "Incorrect TXT record",
    48  		},
    49  		{
    50  			name:        "DNS failure on localhost",
    51  			ident:       identifier.NewDNS("localhost"),
    52  			wantErrType: berrors.Unauthorized,
    53  			wantErrMsg:  "Incorrect TXT record",
    54  		},
    55  		{
    56  			name:        "IP identifier not supported",
    57  			ident:       identifier.NewIP(netip.MustParseAddr("127.0.0.1")),
    58  			wantErrType: berrors.Malformed,
    59  			wantErrMsg:  "Identifier type for DNS-ACCOUNT-01 challenge was not DNS",
    60  		},
    61  		{
    62  			name: "invalid identifier type",
    63  			ident: identifier.ACMEIdentifier{
    64  				Type:  identifier.IdentifierType("iris"),
    65  				Value: "790DB180-A274-47A4-855F-31C428CB1072",
    66  			},
    67  			wantErrType: berrors.Malformed,
    68  			wantErrMsg:  "Identifier type for DNS-ACCOUNT-01 challenge was not DNS",
    69  		},
    70  		{
    71  			name:        "DNS server failure",
    72  			ident:       identifier.NewDNS("servfail.com"),
    73  			wantErrType: berrors.DNS,
    74  			wantErrMsg:  "SERVFAIL",
    75  		},
    76  		{
    77  			name:  "valid DNS record",
    78  			ident: identifier.NewDNS("good-dns01.com"),
    79  		},
    80  		{
    81  			name:  "valid DNS record with no authority",
    82  			ident: identifier.NewDNS("no-authority-dns01.com"),
    83  		},
    84  	}
    85  
    86  	for _, tc := range testCases {
    87  		t.Run(tc.name, func(t *testing.T) {
    88  			va, _ := setup(nil, "", nil, nil)
    89  			_, err := va.validateDNSAccount01(ctx, tc.ident, expectedKeyAuthorization, testAccountURI)
    90  
    91  			if tc.wantErrMsg != "" {
    92  				if err == nil {
    93  					t.Fatalf("validateDNSAccount01(%q) = success, but want error %q", tc.ident.Value, tc.wantErrMsg)
    94  				}
    95  				if !errors.Is(err, tc.wantErrType) {
    96  					t.Errorf("validateDNSAccount01(%q) = error type %T, but want error type %T", tc.ident.Value, err, tc.wantErrType)
    97  				}
    98  				prob := detailedError(err)
    99  				if !strings.Contains(prob.String(), tc.wantErrMsg) {
   100  					t.Errorf("validateDNSAccount01(%q) = %q, but want error containing %q", tc.ident.Value, prob.String(), tc.wantErrMsg)
   101  				}
   102  			} else {
   103  				if err != nil {
   104  					t.Errorf("validateDNSAccount01(%q) = %v, but want success", tc.ident.Value, err)
   105  				}
   106  			}
   107  		})
   108  	}
   109  }
   110  
   111  func TestDNSAccount01ValidationNoServer(t *testing.T) {
   112  	va, log := setup(nil, "", nil, nil)
   113  	staticProvider, err := bdns.NewStaticProvider([]string{})
   114  	test.AssertNotError(t, err, "Couldn't make new static provider")
   115  
   116  	va.dnsClient = bdns.NewTest(
   117  		time.Second*5,
   118  		staticProvider,
   119  		metrics.NoopRegisterer,
   120  		clock.New(),
   121  		1,
   122  		"",
   123  		log,
   124  		nil)
   125  
   126  	_, err = va.validateDNSAccount01(ctx, identifier.NewDNS("localhost"), expectedKeyAuthorization, testAccountURI)
   127  	prob := detailedError(err)
   128  	test.AssertEquals(t, prob.Type, probs.DNSProblem)
   129  }
   130  
   131  func TestDNSAccount01ValidationEmptyAccountURI(t *testing.T) {
   132  	va, _ := setup(nil, "", nil, nil)
   133  
   134  	// The specific domain doesn't matter, as the function should
   135  	// reject the empty accountURI before DNS lookup.
   136  	ident := identifier.NewDNS("empty-uri-test.com")
   137  
   138  	// Call the validation function with an empty accountURI
   139  	_, err := va.validateDNSAccount01(ctx, ident, expectedKeyAuthorization, "")
   140  
   141  	// Assert that an error was returned
   142  	if err == nil {
   143  		t.Errorf("validateDNSAccount01(%q) = success, but want error", ident.Value)
   144  		return
   145  	}
   146  
   147  	// Assert the specific error type
   148  	test.AssertErrorIs(t, err, berrors.InternalServer)
   149  
   150  	// Assert the specific error message using strings.Contains
   151  	wantErrMsg := "accountURI must be provided for dns-account-01"
   152  	if !strings.Contains(err.Error(), wantErrMsg) {
   153  		t.Errorf("validateDNSAccount01(%q) = %q, but want error containing %q", ident.Value, err.Error(), wantErrMsg)
   154  	}
   155  }