pebble icon indicating copy to clipboard operation
pebble copied to clipboard

Implement the "dns-account-01" Challenge in Pebble

Open sheurich opened this issue 1 year ago • 8 comments

Objective

Implement the "dns-account-01" challenge in Pebble, setting the groundwork for subsequent Boulder alignment (referencing boulder#7240).

Rationale

Establishing this feature in Pebble first is crucial for a consistent and forward-compatible testing framework.

sheurich avatar Jan 11 '24 21:01 sheurich

I found that it's nontrival for pebble to do this: because accountURL needed to calculate this is dependent to domain of ACME uses, which pebble doesn't configed and not known. and just listen on everything: Edit: solved with wfe send to what domain name it got

orangepizza avatar Jan 31 '24 11:01 orangepizza

In addition to the ongoing work from @orangepizza, I have prepared two changes to support Pebble integration tests using dns-account-01.

  • https://github.com/certbot/certbot/pull/9887
  • https://github.com/letsencrypt/pebble/pull/432

In conjunction with my proposed change to https://github.com/letsencrypt/pebble/pull/430 (see https://github.com/letsencrypt/pebble/pull/430#issuecomment-1928497141), the preceding changes to certbot/acme and pebble/test/chisel2.py enable a successful integration test between pebble, challtestsrv and chisel2.

Example run:

REQUESTS_CA_BUNDLE=pebble/test/certs/pebble.minica.pem \
  python3 -c 'import pebble.test.chisel2; pebble.test.chisel2.auth_and_issue(["foo.com"],"dns-account-01")'

…
DEBUG:urllib3.connectionpool:https://localhost:14000 "POST /authZ/AFDXL5tMBBi6GTDqetoTuPArYOTXi7_GGL0CQWCh6Qs HTTP/1.1" 200 440
DEBUG:acme.client:Received response:
HTTP 200
Cache-Control: public, max-age=0, no-cache
Content-Type: application/json; charset=utf-8
Link: <https://localhost:14000/dir>;rel="index"
Replay-Nonce: BAMHTKcD2aNAyopsB-9zZg
Date: Tue, 06 Feb 2024 00:29:17 GMT
Content-Length: 440

{
   "status": "valid",
   "identifier": {
      "type": "dns",
      "value": "foo.com"
   },
   "challenges": [
      {
         "type": "dns-account-01",
         "url": "https://localhost:14000/chalZ/lV0CIU0zxaGkt5Ib7og8d1vkQo1A2EHzMREKbgdsZOI",
         "token": "01Wsj3RQfOinMJ7-lbKRBeElrLBI8OaUv99Jioem0_w",
         "status": "valid",
         "validated": "2024-02-06T00:29:14Z"
      }
   ],
   "expires": "2024-02-06T01:29:17Z"
}
…
…
pebble-challtestsrv - 2024/02/05 19:29:14 Added DNS-01 TXT challenge for Host "_f75qxvkvtswybx6u._acme-challenge.foo.com." - Value "jjxuv--W5N1o1TujkI12Db6jakJfAXLFfDEpjsEYVGA"
pebble-challtestsrv - 2024/02/05 19:29:18 Removed DNS-01 TXT challenge for Host "_f75qxvkvtswybx6u._acme-challenge.foo.com."

sheurich avatar Feb 06 '24 00:02 sheurich

I thought that suggestion was shot down be LE employee: https://github.com/aarongable https://github.com/aaomidi/draft-ietf-acme-dns-account-challenge/issues/13#issuecomment-1456568409

At no point do the Baseline Requirements constrain what the Authorization Domain Name may be.

This is incorrect. The Authorization Domain Name is defined as the FQDN used to obtain authorization, which sounds like a wide-open definition, but there are restrictions on what FQDNs can be used to obtain authorization, and therefore there are restrictions on what FQDNs can be Authorization Domain Names (all following quotes from the definition of Authorization Domain Name in Section 1.6.1 of the BRs):

The CA may use the FQDN returned from a DNS CNAME lookup as the FQDN for the purposes of domain validation.

i.e. the Authorization Domain Name may be the FQDN to be included in the Certificate

The CA may prune zero or more Domain Labels of the FQDN from left to right until encountering a Base Domain Name and may use any one of the values that were yielded by pruning (including the Base Domain Name itself) for the purpose of domain validation.

i.e. you can convert the Certificate FQDN into an Authorization Domain Name by pruning labels from left to right.

In other words, you cannot go the other direction: you cannot convert an Authorization Domain Name into a Certificate FQDN by trimming labels from left to right.

Therefore, if the TXT Record is _foo._bar.example.com, then the Authorization Domain Name is _bar.example.com, and you can issue for *._bar_example.com but not for example.com. well I'm only looking at RFC draft IETF side so I don't know about at all. I think it'd caught by there too

orangepizza avatar Feb 06 '24 00:02 orangepizza

Added a comment at https://github.com/aaomidi/draft-ietf-acme-dns-account-challenge/issues/13#issuecomment-1928640871

sheurich avatar Feb 06 '24 01:02 sheurich

The dns-account-01 Python client implementation is ready for review in:

  • https://github.com/certbot/certbot/pull/9887

After that is approved and merged the chisel2 changes will be readied in:

  • https://github.com/letsencrypt/pebble/pull/432

sheurich avatar Feb 06 '24 21:02 sheurich

Hi all, we're getting close to publishing a new draft. Here is a preview: https://aaomidi.github.io/draft-ietf-acme-scoped-dns-challenges/

Sorry for adding a ton of changes here, but ultimately we felt like we need to incorporate the teachings in https://datatracker.ietf.org/doc/draft-ietf-dnsop-domain-verification-techniques/

I believe https://github.com/certbot/certbot/pull/9887 might need to be updated to follow up with this new draft as well.

aaomidi avatar Feb 16 '24 15:02 aaomidi

Because subdomain auth is not yet implemented in Boulder (per https://github.com/letsencrypt/boulder/issues/7050), this implementation would be:

"_" || base32(SHA-256(<ACCOUNT_RESOURCE_URL>)[0:10]) || "._acme-" || <SCOPE> || "-challenge"

for SCOPE in

  • "host"
  • "wildcard"

NOT SCOPE in

  • "domain"

based on https://github.com/aaomidi/draft-ietf-acme-scoped-dns-challenges/blob/0058e0800056698fb37f3b2cb31a727c826675fb/draft-ietf-acme-scoped-dns-challenges.mkd

sheurich avatar Feb 16 '24 21:02 sheurich

I have a fork of https://github.com/eggsampler/acme with dns-account-01 support implemented per the latest draft (https://github.com/aaomidi/draft-ietf-acme-scoped-dns-challenges/blob/0058e0800056698fb37f3b2cb31a727c826675fb/draft-ietf-acme-scoped-dns-challenges.mkd). This will be useful for the Go integration tests.

https://github.com/sheurich/eggsampler-acme/tree/add-dns-account-01

The validation label computation is:

acctHash := sha256.Sum256([]byte(acct.URL))
acctLabel := strings.ToLower(base32.StdEncoding.EncodeToString(acctHash[0:10]))
scope := "host"
if auth.Wildcard {
    scope = "wildcard"
}
host := "_" + acctLabel + "._acme-" + scope + "-challenge." + auth.Identifier.Value + "."

as seen in https://github.com/sheurich/eggsampler-acme/blob/18317c3a082d1ab3e2db62df0808ec39e09318e7/utility_test.go#L325-L332

sheurich avatar Feb 16 '24 22:02 sheurich

Solved by https://github.com/letsencrypt/pebble/pull/435

sheurich avatar Mar 19 '24 15:03 sheurich