The uniqueness of challenges (Boulder specific)

I can't find an answer that helps me proceed in the RFC, so while I'd love this to work with all ACME - I am most concerned with Boulder. Hoping someone where can help again!

My client is centralized process/server that functions as both a Certificate Manager and ACME client. As an ACME client, it can only have one active challenge per-domain. As a Certificate Manager, it can queue up multiple certificates with shared domains for processing. To reconcile these two concerns, an active Acme Challenge can block new Acme Orders from processing.

My problem concerns "what is an active vs inactive" Challenge.

To make things simple for now, I'm just handling the blocking via the Order's status. This is somewhat fine, but creates bottleneck concerns as some orders can take longer to process than others.

I'd like to make this determination more granular, by tying blocking activity to the Challenge or `Authorization.

The RFC states a Challenge is created in pending, transitions to processing when triggered and is then either valid or invalid based on the result. Ok. pending and processing block, the others don't.

Also great, the RFC states that a Challenge transition will further transition the state of the Authorization if it is deterministic (a challenge transitions to valid or invalid); and non-valid Authorization will make an Order invalid. However, an invalid Order will leave the Authorizationsaspending`.

This leaves me with a few edge cases that I can't figure out:

  1. When an Authorization transitions to valid or invalid, what happens to the remnant Challenges on the Authorization, if any exist? The RFC states on page 55:

When finalizing an authorization, the server MAY remove
challenges other than the one that was completed, and it may modify
the "expires" field. The server SHOULD NOT remove challenges with
status "invalid".

  1. If a pending Authorization is deactivated by the client (or expired or revoked), what happens to the component Challenges?

The RFC on page 29:

 For pending authorizations,
 the challenges that the client can fulfill in order to prove
 possession of the identifier.  For valid authorizations, the
 challenge that was validated.  For invalid authorizations, the
 challenge that was attempted and failed.  

The RFC doesn't address the other potential states.

I'm trying to cobble together what-to-do based on Pebble/Boulder behaviors. It seems the setting to re-use valid authorizations keeps everything serverside - the client is never shown the Authorization/Challenge (at least on Pebble).

The Challenge object does't have an expiry timestamp itself, which implies to me it's functional expiration is tied to the Authorization, and that a Challenge is not going to be reused across authorizations. (Though I thought the DNS auth reuses something). In any event, the RFC implies a Challenge is forever pending until triggered - which just seems wrong.

It looks like it will be safe to block on this combination:

Challenge["pending" or "processing"]
+ Authorization ["pending"]
+ Order ["pending"]

However, I'd like to clean up the database and figure out if Challenges can be associated with different orders and what do to with them (if anything) once an authorization is deactivated.


This is related to an earlier question I had (Unique identifiers for authorization objects and challenges? - #2 by jvanasco)

1 Like

Isn't it? In both Pebble and Boulder, re-used authorizations (i.e. ones that are already valid) are definitely listed in the authorizations field of an order resource.

This seems answered in the text:

challenges (required, array of objects): For pending authorizations,
the challenges that the client can fulfill in order to prove
possession of the identifier. For valid authorizations, the
challenge that was validated.
For invalid authorizations, the
challenge that was attempted and failed

The other challenges in the authorization no longer exist - at least, they are detached from the authorization.

I think this is a good question. It's not clear on what a deactivated authz should do to its challenges.

In practice, the behavior varies, apparently.

In Pebble, they disappear from the authorization resource:

[ ACME ] > newOrder -identifiers deactivate.org
2020/04/23 15:02:48 Created new order with ID "https://localhost:14000/my-order/-XDnADJ0qOPpEpP-4vvjlp5V3qRx_74M3UFa87vN2LE"
[ ACME ] > getAuthz -identifier deactivate.org -order 1
{
  "ID": "https://localhost:14000/authZ/iZ_-juKVGXaNpzJEsoHnrcfM89Lg20W6pJqgmdUbGAo",
  "Status": "pending",
  "Identifier": {
    "Type": "dns",
    "Value": "deactivate.org"
  },
  "Challenges": [
    {
      "Type": "tls-alpn-01",
      "URL": "https://localhost:14000/chalZ/C9qfhELbEgw8Kb6J4GSS_1EIeFnG0v1zxAPtWi5xdQk",
      "Token": "2eLIOu5uSI7EdcUr8FvOnL22zV-Ev7DKGAGtuOnEEVY",
      "Status": "pending"
    },
    {
      "Type": "http-01",
      "URL": "https://localhost:14000/chalZ/FgjIJOvXYS-NzDPj06wjnDLXIf54m344YXqkS-MJ4OE",
      "Token": "bHs6sh_alhMrmSLz-spIDeLeuBdRsfW3zLo-TZu8Ex0",
      "Status": "pending"
    },
    {
      "Type": "dns-01",
      "URL": "https://localhost:14000/chalZ/fTGSKKVQZpYjNwS3R4y1iWQJpgaMb-8y3QkDq1wxafs",
      "Token": "eOJGum2eicD0qdzkwMjnNVsEcnsnYWch1ZRu2F_kVww",
      "Status": "pending"
    }
  ],
  "Expires": "2020-04-23T06:02:48Z",
  "Wildcard": false
}
[ ACME ] > deactivateAuthz -identifier deactivate.org -order 1
Authz "https://localhost:14000/authZ/iZ_-juKVGXaNpzJEsoHnrcfM89Lg20W6pJqgmdUbGAo" deactivated
[ ACME ] > getAuthz -identifier deactivate.org -order 1
{
  "ID": "https://localhost:14000/authZ/iZ_-juKVGXaNpzJEsoHnrcfM89Lg20W6pJqgmdUbGAo",
  "Status": "deactivated",
  "Identifier": {
    "Type": "dns",
    "Value": "deactivate.org"
  },
  "Challenges": [],
  "Expires": "2020-04-23T06:02:48Z",
  "Wildcard": false
}

In Boulder, they remain listed, with "status": "pending":

[ ACME ] > newOrder -identifiers deactivate.org
2020/04/23 15:06:54 Created new order with ID "http://localhost:4001/acme/order/37/53"
[ ACME ] > getAuthz -identifier deactivate.org -order 0
{
  "ID": "http://localhost:4001/acme/authz-v3/47",
  "Status": "pending",
  "Identifier": {
    "Type": "dns",
    "Value": "deactivate.org"
  },
  "Challenges": [
    {
      "Type": "http-01",
      "URL": "http://localhost:4001/acme/chall-v3/47/_wVGBw",
      "Token": "LLODmb8m1Lw_E_k4WO-gLUSn3vqXIiSzqKwR0-Jq09s",
      "Status": "pending"
    },
    {
      "Type": "dns-01",
      "URL": "http://localhost:4001/acme/chall-v3/47/93OX-Q",
      "Token": "LLODmb8m1Lw_E_k4WO-gLUSn3vqXIiSzqKwR0-Jq09s",
      "Status": "pending"
    },
    {
      "Type": "tls-alpn-01",
      "URL": "http://localhost:4001/acme/chall-v3/47/H1jLfw",
      "Token": "LLODmb8m1Lw_E_k4WO-gLUSn3vqXIiSzqKwR0-Jq09s",
      "Status": "pending"
    }
  ],
  "Expires": "2020-04-30T05:06:54Z",
  "Wildcard": false
}
[ ACME ] > deactivateAuthz -identifier deactivate.org -order 0
Authz "http://localhost:4001/acme/authz-v3/47" deactivated
[ ACME ] > getAuthz -identifier deactivate.org -order 0
{
  "ID": "http://localhost:4001/acme/authz-v3/47",
  "Status": "deactivated",
  "Identifier": {
    "Type": "dns",
    "Value": "deactivate.org"
  },
  "Challenges": [
    {
      "Type": "http-01",
      "URL": "http://localhost:4001/acme/chall-v3/47/_wVGBw",
      "Token": "LLODmb8m1Lw_E_k4WO-gLUSn3vqXIiSzqKwR0-Jq09s",
      "Status": "pending"
    },
    {
      "Type": "dns-01",
      "URL": "http://localhost:4001/acme/chall-v3/47/93OX-Q",
      "Token": "LLODmb8m1Lw_E_k4WO-gLUSn3vqXIiSzqKwR0-Jq09s",
      "Status": "pending"
    },
    {
      "Type": "tls-alpn-01",
      "URL": "http://localhost:4001/acme/chall-v3/47/H1jLfw",
      "Token": "LLODmb8m1Lw_E_k4WO-gLUSn3vqXIiSzqKwR0-Jq09s",
      "Status": "pending"
    }
  ],
  "Expires": "2020-04-30T05:06:54Z",
  "Wildcard": false
}

Perhaps Boulder should be doing something with the challenges in a deactivated authorization, like hiding them.

1 Like

Ok, so i was looking at the wrong acme-log and you found the bug in how I process the payload. Thank you immensely!

Those are the ones I am concerned about. I understood the server "may" remove them from the RFC, but those deactivated/etc ones are what have been puzzling me.

It looks like I will keep tracking them, and just introduce a cleanup routine in the future. They don't seem to re-used across Orders, so I think it is safe.

Our client was designed to host a scalable number of domains; the point on tracking all this info is to figure out what mistakes happened when - so I'm trying to ensure I don't have challenges/auths overlapping orders.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.