Skip to content

Commit 96c7614

Browse files
Update draft-ietf-acme-dns-persist-00 support to 01
1 parent 05635e8 commit 96c7614

9 files changed

Lines changed: 45 additions & 7 deletions

File tree

cmd/boulder-wfe2/main.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ type Config struct {
120120
// will differ in configuration for production and staging.
121121
LegacyKeyIDPrefix string `validate:"required,url"`
122122

123+
// AccountURIPrefix is used to construct the "accounturi" field of
124+
// dns-persist-01 challenges (e.g. "https://acme-v02.api.letsencrypt.org/acme/acct/").
125+
// MUST match the first entry of the VA's AccountURIPrefixes field.
126+
//
127+
// TODO(#8724): Once this field has been set in Production we can make
128+
// it required.
129+
AccountURIPrefix string `validate:"omitempty,url,endswith=/"`
130+
123131
// GoodKey is an embedded config stanza for the goodkey library.
124132
GoodKey goodkey.Config
125133

@@ -414,6 +422,7 @@ func main() {
414422
wfe.AllowOrigins = c.WFE.AllowOrigins
415423
wfe.DirectoryWebsite = c.WFE.DirectoryWebsite
416424
wfe.LegacyKeyIDPrefix = c.WFE.LegacyKeyIDPrefix
425+
wfe.AccountURIPrefix = c.WFE.AccountURIPrefix
417426

418427
if c.WFE.ListenAddress == "" {
419428
cmd.Fail("HTTP listen address is not configured")

core/objects.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ type Challenge struct {
163163
// by all challenges except dns-persist-01.
164164
Token string `json:"token,omitempty"`
165165

166+
// AccountURI is the account URI the client includes during dns-persist-01
167+
// challenge validation.
168+
AccountURI string `json:"accounturi,omitempty"`
169+
166170
// IssuerDomainNames contains the list of issuer domain name values accepted
167171
// during dns-persist-01 challenge validation.
168172
IssuerDomainNames []string `json:"issuer-domain-names,omitempty"`

core/util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ func Command() string {
402402
}
403403

404404
// NormalizeIssuerDomainName normalizes an RFC 8659 issuer-domain-name per the
405-
// recommended algorithm in draft-ietf-acme-dns-persist-00, Section 9.1.1:
405+
// recommended algorithm in draft-ietf-acme-dns-persist-01, Section 9.2:
406406
// case-fold to lowercase, apply Unicode NFC normalization, convert to A-label
407407
// (Punycode), remove any trailing dot, and ensure the result is no more than
408408
// 253 octets in length. If normalization fails, an error is returned.

ra/ra.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2211,7 +2211,7 @@ func (ra *RegistrationAuthorityImpl) NewOrder(ctx context.Context, req *rapb.New
22112211
}
22122212

22132213
// Never reuse dns-persist-01 authorizations:
2214-
// draft-ietf-acme-dns-persist-00 section 7.8 caps the reuse period to
2214+
// draft-ietf-acme-dns-persist-01 section 7.8 caps the reuse period to
22152215
// the TXT record's TTL and BRs section 3.2.2.4.22 caps it at 10 days.
22162216
// Since TTLs are typically seconds to minutes, the TTL cap is likely to
22172217
// be the binding constraint; re-validating every order is simpler.

test/config-next/wfe2.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"directoryCAAIdentity": "happy-hacker-ca.invalid",
1212
"directoryWebsite": "https://github.com/letsencrypt/boulder",
1313
"legacyKeyIDPrefix": "http://boulder.service.consul:4000/reg/",
14+
"accountURIPrefix": "http://boulder.service.consul:4001/acme/acct/",
1415
"goodkey": {},
1516
"maxContactsPerRegistration": 3,
1617
"tls": {

va/dns_persist.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func parseDNSPersistRecord(record string) (string, *dnsPersistIssueValueParams,
109109
params.persistUntil = time.Unix(persistUntilVal, 0).UTC()
110110

111111
default:
112-
// Per draft-ietf-acme-dns-persist-00, "the server MUST ignore any
112+
// Per draft-ietf-acme-dns-persist-01, "the server MUST ignore any
113113
// parameter within the issue-value that has an unrecognized tag."
114114
continue
115115
}
@@ -125,10 +125,13 @@ func parseDNSPersistRecord(record string) (string, *dnsPersistIssueValueParams,
125125
// given time. It returns nil if the record authorizes issuance, or a
126126
// berrors.Unauthorized error for authorization failures.
127127
func checkDNSPersistRecord(params *dnsPersistIssueValueParams, validAccountURI string, wildcardName bool, validatedAt time.Time) error {
128+
// Per draft-ietf-acme-dns-persist-01 section 4.1, accounturi values are
129+
// compared using Simple String Comparison per RFC 3986 section 6.2.1, with
130+
// no case-folding or other normalization.
128131
if params.accountURI != validAccountURI {
129132
return berrors.UnauthorizedError("accounturi mismatch: expected %q, got %q", validAccountURI, params.accountURI)
130133
}
131-
// Per draft-ietf-acme-dns-persist-00, the policy parameter's tag and
134+
// Per draft-ietf-acme-dns-persist-01 section 4.1, the policy parameter's
132135
// defined values MUST be treated as case-insensitive. If the policy
133136
// parameter's value is anything other than "wildcard", the CA MUST proceed
134137
// as if the policy parameter were not present.

vendor/github.com/eggsampler/acme/v3/types.go

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wfe2/wfe.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ type WebFrontEndImpl struct {
140140
// `LegacyKeyIDPrefix` for more information.
141141
LegacyKeyIDPrefix string
142142

143+
// AccountURIPrefix is required to set the "accounturi" field of
144+
// dns-persist-01 challenges. MUST match the first entry of the VA's
145+
// AccountURIPrefixes field.
146+
AccountURIPrefix string
147+
143148
// Key policy.
144149
keyPolicy goodkey.KeyPolicy
145150

@@ -1245,19 +1250,26 @@ func (wfe *WebFrontEndImpl) prepChallengeForDisplay(
12451250
}
12461251

12471252
if challenge.Type == core.ChallengeTypeDNSPersist01 {
1248-
// draft-ietf-acme-dns-persist-00 section 3.1 states, "Servers MUST NOT
1253+
// draft-ietf-acme-dns-persist-01 section 3.1 states, "Servers MUST NOT
12491254
// send more than 10 issuer domain names." Be aware of this if we ever
12501255
// support configuration of multiple CAA identities.
12511256
challenge.IssuerDomainNames = []string{wfe.DirectoryCAAIdentity}
12521257

1258+
// TODO(#8724): Once the configuration of AccountURIPrefix is required
1259+
// to be non-empty, this conditional can be removed.
1260+
if wfe.AccountURIPrefix != "" {
1261+
challenge.AccountURI = fmt.Sprintf("%s%d", wfe.AccountURIPrefix, authz.RegistrationID)
1262+
}
1263+
12531264
// dns-persist-01 does not use a token, but authorizations store a
12541265
// single token which gets unconditionally assigned to all challenge
12551266
// types during deserialization.
12561267
challenge.Token = ""
12571268
} else {
1258-
// Belt and suspenders: we don't expect this to ever be populated
1269+
// Belt and suspenders: we don't expect these to ever be populated
12591270
// outside of this function, but just in case.
12601271
challenge.IssuerDomainNames = nil
1272+
challenge.AccountURI = ""
12611273
}
12621274
}
12631275

wfe2/wfe_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ func setupWFE(t *testing.T) (WebFrontEndImpl, clock.FakeClock, requestSigner) {
448448
test.AssertNotError(t, err, "Unable to create WFE")
449449

450450
wfe.SubscriberAgreementURL = agreementURL
451+
wfe.AccountURIPrefix = "http://localhost/acme/acct/"
451452

452453
return wfe, fc, requestSigner{t, inmemNonceService.AsSource()}
453454
}
@@ -3657,9 +3658,11 @@ func TestPrepAuthzForDisplay(t *testing.T) {
36573658
if chall.Type == core.ChallengeTypeDNSPersist01 {
36583659
test.Assert(t, chall.Token == "", fmt.Sprintf("expected %s to have no token", chall.Type))
36593660
test.AssertDeepEquals(t, chall.IssuerDomainNames, []string{"letsencrypt.org"})
3661+
test.AssertEquals(t, chall.AccountURI, "http://localhost/acme/acct/1")
36603662
} else {
36613663
test.Assert(t, chall.Token != "", fmt.Sprintf("expected %s to have a token", chall.Type))
36623664
test.Assert(t, chall.IssuerDomainNames == nil, fmt.Sprintf("expected %s to have no issuer domain names", chall.Type))
3665+
test.AssertEquals(t, chall.AccountURI, "")
36633666
}
36643667
}
36653668
}

0 commit comments

Comments
 (0)