github.com/letsencrypt/boulder@v0.20251208.0/test/integration/dns_account_01_test.go (about) 1 //go:build integration 2 3 package integration 4 5 import ( 6 "fmt" 7 "os" 8 "strings" 9 "testing" 10 11 "github.com/eggsampler/acme/v3" 12 ) 13 14 func TestDNSAccount01HappyPath(t *testing.T) { 15 t.Parallel() 16 17 if os.Getenv("BOULDER_CONFIG_DIR") == "test/config" { 18 t.Skip("Test requires dns-account-01 to be enabled") 19 } 20 21 domain := random_domain() 22 c, err := makeClient() 23 if err != nil { 24 t.Fatalf("creating client: %s", err) 25 } 26 27 idents := []acme.Identifier{{Type: "dns", Value: domain}} 28 29 order, err := c.Client.NewOrder(c.Account, idents) 30 if err != nil { 31 t.Fatalf("creating new order: %s", err) 32 } 33 34 authzURL := order.Authorizations[0] 35 auth, err := c.Client.FetchAuthorization(c.Account, authzURL) 36 if err != nil { 37 t.Fatalf("fetching authorization: %s", err) 38 } 39 40 chal, ok := auth.ChallengeMap[acme.ChallengeTypeDNSAccount01] 41 if !ok { 42 t.Fatal("dns-account-01 challenge not offered by server") 43 } 44 45 _, err = testSrvClient.AddDNSAccount01Response(c.Account.URL, domain, chal.KeyAuthorization) 46 if err != nil { 47 t.Fatalf("adding DNS response: %s", err) 48 } 49 t.Cleanup(func() { 50 _, _ = testSrvClient.RemoveDNSAccount01Response(c.Account.URL, domain) 51 }) 52 53 chal, err = c.Client.UpdateChallenge(c.Account, chal) 54 if err != nil { 55 t.Fatalf("updating challenge: %s", err) 56 } 57 58 // Check that the authorization status has changed 59 auth, err = c.Client.FetchAuthorization(c.Account, authzURL) 60 if err != nil { 61 t.Fatalf("fetching authorization after challenge update: %s", err) 62 } 63 64 if auth.Status != "valid" { 65 t.Fatalf("expected authorization status to be 'valid', got '%s'", auth.Status) 66 } 67 } 68 69 func TestDNSAccount01WrongTXTRecord(t *testing.T) { 70 t.Parallel() 71 72 if os.Getenv("BOULDER_CONFIG_DIR") == "test/config" { 73 t.Skip("Test requires dns-account-01 to be enabled") 74 } 75 76 domain := random_domain() 77 c, err := makeClient() 78 if err != nil { 79 t.Fatalf("creating client: %s", err) 80 } 81 82 idents := []acme.Identifier{{Type: "dns", Value: domain}} 83 84 order, err := c.Client.NewOrder(c.Account, idents) 85 if err != nil { 86 t.Fatalf("creating new order: %s", err) 87 } 88 89 authzURL := order.Authorizations[0] 90 auth, err := c.Client.FetchAuthorization(c.Account, authzURL) 91 if err != nil { 92 t.Fatalf("fetching authorization: %s", err) 93 } 94 95 chal, ok := auth.ChallengeMap[acme.ChallengeTypeDNSAccount01] 96 if !ok { 97 t.Fatal("dns-account-01 challenge not offered by server") 98 } 99 100 // Add a wrong TXT record 101 _, err = testSrvClient.AddDNSAccount01Response(c.Account.URL, domain, "wrong-digest") 102 if err != nil { 103 t.Fatalf("adding DNS response: %s", err) 104 } 105 t.Cleanup(func() { 106 _, _ = testSrvClient.RemoveDNSAccount01Response(c.Account.URL, domain) 107 }) 108 109 _, err = c.Client.UpdateChallenge(c.Account, chal) 110 if err == nil { 111 t.Fatalf("updating challenge: expected error, got nil") 112 } 113 prob, ok := err.(acme.Problem) 114 if !ok { 115 t.Fatalf("updating challenge: expected acme.Problem error, got %T", err) 116 } 117 if prob.Type != "urn:ietf:params:acme:error:unauthorized" { 118 t.Fatalf("updating challenge: expected unauthorized error, got %s", prob.Type) 119 } 120 if !strings.Contains(prob.Detail, "Incorrect TXT record") { 121 t.Fatalf("updating challenge: expected Incorrect TXT record error, got %s", prob.Detail) 122 } 123 } 124 125 func TestDNSAccount01NoTXTRecord(t *testing.T) { 126 t.Parallel() 127 128 if os.Getenv("BOULDER_CONFIG_DIR") == "test/config" { 129 t.Skip("Test requires dns-account-01 to be enabled") 130 } 131 132 domain := random_domain() 133 c, err := makeClient() 134 if err != nil { 135 t.Fatalf("creating client: %s", err) 136 } 137 138 idents := []acme.Identifier{{Type: "dns", Value: domain}} 139 140 order, err := c.Client.NewOrder(c.Account, idents) 141 if err != nil { 142 t.Fatalf("creating new order: %s", err) 143 } 144 145 authzURL := order.Authorizations[0] 146 auth, err := c.Client.FetchAuthorization(c.Account, authzURL) 147 if err != nil { 148 t.Fatalf("fetching authorization: %s", err) 149 } 150 151 chal, ok := auth.ChallengeMap[acme.ChallengeTypeDNSAccount01] 152 if !ok { 153 t.Fatal("dns-account-01 challenge not offered by server") 154 } 155 156 // Skip adding a TXT record 157 158 _, err = c.Client.UpdateChallenge(c.Account, chal) 159 if err == nil { 160 t.Fatalf("updating challenge: expected error, got nil") 161 } 162 prob, ok := err.(acme.Problem) 163 if !ok { 164 t.Fatalf("updating challenge: expected acme.Problem error, got %T", err) 165 } 166 if prob.Type != "urn:ietf:params:acme:error:unauthorized" { 167 t.Fatalf("updating challenge: expected unauthorized error, got %s", prob.Type) 168 } 169 if !strings.Contains(prob.Detail, "No TXT record found") { 170 t.Fatalf("updating challenge: expected No TXT record found error, got %s", prob.Detail) 171 } 172 } 173 174 func TestDNSAccount01MultipleTXTRecordsNoneMatch(t *testing.T) { 175 t.Parallel() 176 177 if os.Getenv("BOULDER_CONFIG_DIR") == "test/config" { 178 t.Skip("Test requires dns-account-01 to be enabled") 179 } 180 181 domain := random_domain() 182 c, err := makeClient() 183 if err != nil { 184 t.Fatalf("creating client: %s", err) 185 } 186 187 idents := []acme.Identifier{{Type: "dns", Value: domain}} 188 189 order, err := c.Client.NewOrder(c.Account, idents) 190 if err != nil { 191 t.Fatalf("creating new order: %s", err) 192 } 193 194 authzURL := order.Authorizations[0] 195 auth, err := c.Client.FetchAuthorization(c.Account, authzURL) 196 if err != nil { 197 t.Fatalf("fetching authorization: %s", err) 198 } 199 200 chal, ok := auth.ChallengeMap[acme.ChallengeTypeDNSAccount01] 201 if !ok { 202 t.Fatal("dns-account-01 challenge not offered by server") 203 } 204 205 // Add multiple wrong TXT records 206 _, err = testSrvClient.AddDNSAccount01Response(c.Account.URL, domain, "wrong-digest-1") 207 if err != nil { 208 t.Fatalf("adding DNS response: %s", err) 209 } 210 _, err = testSrvClient.AddDNSAccount01Response(c.Account.URL, domain, "wrong-digest-2") 211 if err != nil { 212 t.Fatalf("adding DNS response: %s", err) 213 } 214 t.Cleanup(func() { 215 _, _ = testSrvClient.RemoveDNSAccount01Response(c.Account.URL, domain) 216 }) 217 218 _, err = c.Client.UpdateChallenge(c.Account, chal) 219 if err == nil { 220 t.Fatalf("updating challenge: expected error, got nil") 221 } 222 prob, ok := err.(acme.Problem) 223 if !ok { 224 t.Fatalf("updating challenge: expected acme.Problem error, got %T", err) 225 } 226 if prob.Type != "urn:ietf:params:acme:error:unauthorized" { 227 t.Fatalf("updating challenge: expected unauthorized error, got %s", prob.Type) 228 } 229 if !strings.Contains(prob.Detail, "Incorrect TXT record") { 230 t.Fatalf("updating challenge: expected Incorrect TXT record error, got %s", prob.Detail) 231 } 232 } 233 234 func TestDNSAccount01MultipleTXTRecordsOneMatches(t *testing.T) { 235 t.Parallel() 236 237 if os.Getenv("BOULDER_CONFIG_DIR") == "test/config" { 238 t.Skip("Test requires dns-account-01 to be enabled") 239 } 240 241 domain := random_domain() 242 c, err := makeClient() 243 if err != nil { 244 t.Fatalf("creating client: %s", err) 245 } 246 247 idents := []acme.Identifier{{Type: "dns", Value: domain}} 248 249 order, err := c.Client.NewOrder(c.Account, idents) 250 if err != nil { 251 t.Fatalf("creating new order: %s", err) 252 } 253 254 authzURL := order.Authorizations[0] 255 auth, err := c.Client.FetchAuthorization(c.Account, authzURL) 256 if err != nil { 257 t.Fatalf("fetching authorization: %s", err) 258 } 259 260 chal, ok := auth.ChallengeMap[acme.ChallengeTypeDNSAccount01] 261 if !ok { 262 t.Fatal("dns-account-01 challenge not offered by server") 263 } 264 265 // Add multiple TXT records, one of which is correct 266 _, err = testSrvClient.AddDNSAccount01Response(c.Account.URL, domain, "wrong-digest-1") 267 if err != nil { 268 t.Fatalf("adding DNS response: %s", err) 269 } 270 _, err = testSrvClient.AddDNSAccount01Response(c.Account.URL, domain, chal.KeyAuthorization) 271 if err != nil { 272 t.Fatalf("adding DNS response: %s", err) 273 } 274 _, err = testSrvClient.AddDNSAccount01Response(c.Account.URL, domain, "wrong-digest-2") 275 if err != nil { 276 t.Fatalf("adding DNS response: %s", err) 277 } 278 t.Cleanup(func() { 279 _, _ = testSrvClient.RemoveDNSAccount01Response(c.Account.URL, domain) 280 }) 281 282 chal, err = c.Client.UpdateChallenge(c.Account, chal) 283 if err != nil { 284 t.Fatalf("updating challenge: expected no error, got %s", err) 285 } 286 287 // Check that the authorization status has changed 288 auth, err = c.Client.FetchAuthorization(c.Account, authzURL) 289 if err != nil { 290 t.Fatalf("fetching authorization after challenge update: %s", err) 291 } 292 293 if auth.Status != "valid" { 294 t.Fatalf("expected authorization status to be 'valid', got '%s'", auth.Status) 295 } 296 } 297 298 func TestDNSAccount01WildcardDomain(t *testing.T) { 299 t.Parallel() 300 301 if os.Getenv("BOULDER_CONFIG_DIR") == "test/config" { 302 t.Skip("Test requires dns-account-01 to be enabled") 303 } 304 305 hostDomain := randomDomain(t) 306 wildcardDomain := fmt.Sprintf("*.%s", randomDomain(t)) 307 308 c, err := makeClient() 309 if err != nil { 310 t.Fatalf("creating client: %s", err) 311 } 312 313 idents := []acme.Identifier{ 314 {Type: "dns", Value: hostDomain}, 315 {Type: "dns", Value: wildcardDomain}, 316 } 317 318 order, err := c.Client.NewOrder(c.Account, idents) 319 if err != nil { 320 t.Fatalf("creating new order: %s", err) 321 } 322 323 for _, authzURL := range order.Authorizations { 324 auth, err := c.Client.FetchAuthorization(c.Account, authzURL) 325 if err != nil { 326 t.Fatalf("fetching authorization: %s", err) 327 } 328 329 isWildcard := strings.HasPrefix(auth.Identifier.Value, "*.") 330 domain := auth.Identifier.Value 331 if isWildcard { 332 domain = strings.TrimPrefix(domain, "*.") 333 } 334 335 chal, ok := auth.ChallengeMap[acme.ChallengeTypeDNSAccount01] 336 if !ok { 337 t.Fatal("dns-account-01 challenge not offered by server") 338 } 339 340 _, err = testSrvClient.AddDNSAccount01Response(c.Account.URL, domain, chal.KeyAuthorization) 341 if err != nil { 342 t.Fatalf("adding DNS response: %s", err) 343 } 344 t.Cleanup(func() { 345 _, _ = testSrvClient.RemoveDNSAccount01Response(c.Account.URL, domain) 346 }) 347 348 chal, err = c.Client.UpdateChallenge(c.Account, chal) 349 if err != nil { 350 t.Fatalf("updating challenge: %s", err) 351 } 352 353 // Check that the authorization status has changed 354 auth, err = c.Client.FetchAuthorization(c.Account, authzURL) 355 if err != nil { 356 t.Fatalf("fetching authorization after challenge update: %s", err) 357 } 358 359 if auth.Status != "valid" { 360 t.Fatalf("expected authorization status to be 'valid', got '%s'", auth.Status) 361 } 362 } 363 }