-
-
Notifications
You must be signed in to change notification settings - Fork 615
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve how we disable challenge types (#7677)
When creating an authorization, populate it with all challenges appropriate for that identifier, regardless of whether those challenge types are currently "enabled" in the config. This ensures that authorizations created during a incident for which we can temporarily disabled a single challenge type can still be validated via that challenge type after the incident is over. Also, when finalizing an order, check that the challenge type used to validation each authorization is not currently disabled. This ensures that, if we temporarily disable a single challenge due to an incident, we don't issue any more certificates using authorizations which were fulfilled using that disabled challenge. Note that standard rolling deployment of this change is not safe if any challenges are disabled at the same time, due to the possibility of an updated RA not filtering a challenge when writing it to the database, and then a non-updated RA not filtering it when reading from the database. But if all challenges are enabled then this change is safe for normal deploy. Fixes #5913
- Loading branch information
1 parent
ea62f9a
commit d58d096
Showing
8 changed files
with
220 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,16 +12,16 @@ import ( | |
"github.com/letsencrypt/boulder/features" | ||
"github.com/letsencrypt/boulder/identifier" | ||
blog "github.com/letsencrypt/boulder/log" | ||
"github.com/letsencrypt/boulder/must" | ||
"github.com/letsencrypt/boulder/test" | ||
) | ||
|
||
var enabledChallenges = map[core.AcmeChallenge]bool{ | ||
core.ChallengeTypeHTTP01: true, | ||
core.ChallengeTypeDNS01: true, | ||
} | ||
|
||
func paImpl(t *testing.T) *AuthorityImpl { | ||
enabledChallenges := map[core.AcmeChallenge]bool{ | ||
core.ChallengeTypeHTTP01: true, | ||
core.ChallengeTypeDNS01: true, | ||
core.ChallengeTypeTLSALPN01: true, | ||
} | ||
|
||
pa, err := New(enabledChallenges, blog.NewMock()) | ||
if err != nil { | ||
t.Fatalf("Couldn't create policy implementation: %s", err) | ||
|
@@ -388,52 +388,52 @@ func TestWillingToIssue_SubErrors(t *testing.T) { | |
} | ||
|
||
func TestChallengeTypesFor(t *testing.T) { | ||
t.Parallel() | ||
pa := paImpl(t) | ||
|
||
challenges, err := pa.ChallengeTypesFor(identifier.ACMEIdentifier{}) | ||
test.AssertNotError(t, err, "ChallengesFor failed") | ||
|
||
test.Assert(t, len(challenges) == len(enabledChallenges), "Wrong number of challenges returned") | ||
|
||
seenChalls := make(map[core.AcmeChallenge]bool) | ||
for _, challenge := range challenges { | ||
test.Assert(t, !seenChalls[challenge], "should not already have seen this type") | ||
seenChalls[challenge] = true | ||
|
||
test.Assert(t, enabledChallenges[challenge], "Unsupported challenge returned") | ||
testCases := []struct { | ||
name string | ||
ident identifier.ACMEIdentifier | ||
wantChalls []core.AcmeChallenge | ||
wantErr string | ||
}{ | ||
{ | ||
name: "dns", | ||
ident: identifier.DNSIdentifier("example.com"), | ||
wantChalls: []core.AcmeChallenge{ | ||
core.ChallengeTypeHTTP01, core.ChallengeTypeDNS01, core.ChallengeTypeTLSALPN01, | ||
}, | ||
}, | ||
{ | ||
name: "wildcard", | ||
ident: identifier.DNSIdentifier("*.example.com"), | ||
wantChalls: []core.AcmeChallenge{ | ||
core.ChallengeTypeDNS01, | ||
}, | ||
}, | ||
{ | ||
name: "other", | ||
ident: identifier.ACMEIdentifier{Type: "ip", Value: "1.2.3.4"}, | ||
wantErr: "unrecognized identifier type", | ||
}, | ||
} | ||
test.AssertEquals(t, len(seenChalls), len(enabledChallenges)) | ||
} | ||
|
||
func TestChallengeTypesForWildcard(t *testing.T) { | ||
// wildcardIdent is an identifier for a wildcard domain name | ||
wildcardIdent := identifier.ACMEIdentifier{ | ||
Type: identifier.DNS, | ||
Value: "*.zombo.com", | ||
} | ||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
t.Parallel() | ||
challs, err := pa.ChallengeTypesFor(tc.ident) | ||
|
||
if len(tc.wantChalls) != 0 { | ||
test.AssertNotError(t, err, "should have succeeded") | ||
test.AssertDeepEquals(t, challs, tc.wantChalls) | ||
} | ||
|
||
// First try to get a challenge for the wildcard ident without the | ||
// DNS-01 challenge type enabled. This should produce an error | ||
var enabledChallenges = map[core.AcmeChallenge]bool{ | ||
core.ChallengeTypeHTTP01: true, | ||
core.ChallengeTypeDNS01: false, | ||
if tc.wantErr != "" { | ||
test.AssertError(t, err, "should have errored") | ||
test.AssertContains(t, err.Error(), tc.wantErr) | ||
} | ||
}) | ||
} | ||
pa := must.Do(New(enabledChallenges, blog.NewMock())) | ||
_, err := pa.ChallengeTypesFor(wildcardIdent) | ||
test.AssertError(t, err, "ChallengesFor did not error for a wildcard ident "+ | ||
"when DNS-01 was disabled") | ||
test.AssertEquals(t, err.Error(), "Challenges requested for wildcard "+ | ||
"identifier but DNS-01 challenge type is not enabled") | ||
|
||
// Try again with DNS-01 enabled. It should not error and | ||
// should return only one DNS-01 type challenge | ||
enabledChallenges[core.ChallengeTypeDNS01] = true | ||
pa = must.Do(New(enabledChallenges, blog.NewMock())) | ||
challenges, err := pa.ChallengeTypesFor(wildcardIdent) | ||
test.AssertNotError(t, err, "ChallengesFor errored for a wildcard ident "+ | ||
"unexpectedly") | ||
test.AssertEquals(t, len(challenges), 1) | ||
test.AssertEquals(t, challenges[0], core.ChallengeTypeDNS01) | ||
} | ||
|
||
// TestMalformedExactBlocklist tests that loading a YAML policy file with an | ||
|
@@ -483,3 +483,83 @@ func TestValidEmailError(t *testing.T) { | |
err = ValidEmail("[email protected]") | ||
test.AssertEquals(t, err.Error(), "contact email \"[email protected]\" has invalid domain : Domain name contains an invalid character") | ||
} | ||
|
||
func TestCheckAuthzChallenges(t *testing.T) { | ||
t.Parallel() | ||
|
||
testCases := []struct { | ||
name string | ||
authz core.Authorization | ||
enabled map[core.AcmeChallenge]bool | ||
wantErr string | ||
}{ | ||
{ | ||
name: "unrecognized identifier", | ||
authz: core.Authorization{ | ||
Identifier: identifier.ACMEIdentifier{Type: "oops", Value: "example.com"}, | ||
Challenges: []core.Challenge{{Type: core.ChallengeTypeDNS01, Status: core.StatusValid}}, | ||
}, | ||
wantErr: "unrecognized identifier type", | ||
}, | ||
{ | ||
name: "no challenges", | ||
authz: core.Authorization{ | ||
Identifier: identifier.ACMEIdentifier{Type: identifier.DNS, Value: "example.com"}, | ||
Challenges: []core.Challenge{}, | ||
}, | ||
wantErr: "has no challenges", | ||
}, | ||
{ | ||
name: "no valid challenges", | ||
authz: core.Authorization{ | ||
Identifier: identifier.ACMEIdentifier{Type: identifier.DNS, Value: "example.com"}, | ||
Challenges: []core.Challenge{{Type: core.ChallengeTypeDNS01, Status: core.StatusPending}}, | ||
}, | ||
wantErr: "not solved by any challenge", | ||
}, | ||
{ | ||
name: "solved by disabled challenge", | ||
authz: core.Authorization{ | ||
Identifier: identifier.ACMEIdentifier{Type: identifier.DNS, Value: "example.com"}, | ||
Challenges: []core.Challenge{{Type: core.ChallengeTypeDNS01, Status: core.StatusValid}}, | ||
}, | ||
enabled: map[core.AcmeChallenge]bool{core.ChallengeTypeHTTP01: true}, | ||
wantErr: "disabled challenge type", | ||
}, | ||
{ | ||
name: "solved by wrong kind of challenge", | ||
authz: core.Authorization{ | ||
Identifier: identifier.ACMEIdentifier{Type: identifier.DNS, Value: "*.example.com"}, | ||
Challenges: []core.Challenge{{Type: core.ChallengeTypeHTTP01, Status: core.StatusValid}}, | ||
}, | ||
wantErr: "inapplicable challenge type", | ||
}, | ||
{ | ||
name: "valid authz", | ||
authz: core.Authorization{ | ||
Identifier: identifier.ACMEIdentifier{Type: identifier.DNS, Value: "example.com"}, | ||
Challenges: []core.Challenge{{Type: core.ChallengeTypeTLSALPN01, Status: core.StatusValid}}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
t.Parallel() | ||
pa := paImpl(t) | ||
|
||
if tc.enabled != nil { | ||
pa.enabledChallenges = tc.enabled | ||
} | ||
|
||
err := pa.CheckAuthzChallenges(&tc.authz) | ||
|
||
if tc.wantErr == "" { | ||
test.AssertNotError(t, err, "should have succeeded") | ||
} else { | ||
test.AssertError(t, err, "should have errored") | ||
test.AssertContains(t, err.Error(), tc.wantErr) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.