Skip to content

bad-key-revoker: also revoke accounts with blocked keys#8837

Open
aarongable wants to merge 2 commits into
mainfrom
bkr-account-keys
Open

bad-key-revoker: also revoke accounts with blocked keys#8837
aarongable wants to merge 2 commits into
mainfrom
bkr-account-keys

Conversation

@aarongable

@aarongable aarongable commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

When bad-key-revoker is processing a new row from the blockedKeys table, also revoke any accounts whose key matches the newly-blocked key. "Revoking" is the equivalent of "deactivating" under RFC 8555, but initiated by the ACME Server instead of at the request of the Client. This ensures that a key compromise doesn't allow a bad actor to take over an ACME account.

Put the new behavior behind a feature flag, because it requires a database permission change that will have to be deployed separately. Also add a new helper function to core/util.go, to ensure that the way we calculate account key hashes never diverges from the way we compute certificate key hashes.

Fixes #5683

IN-12841 tracks the corresponding SRE-side database and config changes

CPS Compliance Review: our statement regarding our behavior when compromise is demonstrated is "When key compromise is demonstrated, ISRG blocks the key from use in future issuance and revokes at least all unexpired non-Short-lived Subscriber Certificates that used that key." This does not preclude us from taking further actions, such as revoking accounts using the same key. We may want to document this new behavior in our CP/CPS in the future, but doing so is not necessary.

@aarongable aarongable marked this pull request as ready for review July 1, 2026 23:24
@aarongable aarongable requested a review from a team as a code owner July 1, 2026 23:24
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

@aarongable, this PR adds one or more new feature flags: DeactivateBadKeyAccounts. As such, this PR must be accompanied by a review of the Let's Encrypt CP/CPS to ensure that our behavior both before and after this flag is flipped is compliant with that document.

Please conduct such a review, then add your findings to the PR description in a paragraph beginning with "CPS Compliance Review:".

Comment thread cmd/bad-key-revoker/main.go
@beautifulentropy beautifulentropy requested review from ezekiel and jsha July 9, 2026 20:17
@aarongable aarongable changed the title bad-key-revoker: also deactivate accounts with blocked keys bad-key-revoker: also revoke accounts with blocked keys Jul 10, 2026

@beautifulentropy beautifulentropy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, updating the PR to use the "revoked" status gives us a clear understanding of the source of the state change.

Looking through Boulder at how we treat account status, and especially revoked, this will be the first use of revoked for us! I don't think there's any immediate problems with this but there's some defense in depth changes that we should consider:

  1. Inside of wfe.lookupJWK() we make the correct comparison: core.AcmeStatus(account.Status) != core.StatusValid but the comment above it // Verify the account is not deactivated should be updated to indicate what this clause is guarding against.
  2. Inside of the anonymous function returnExistingAcct() (inside of wfe.NewAccount()) we perform the wrong comparison if core.AcmeStatus(acctPB.Status) == core.StatusDeactivated and the comment here should be updated. Today this is unreachable thanks to the earlier call to wfe.validSelfAuthenticatedPOST() which uses GoodKey() to check for blocked keys.
  3. sa.UpdateRegistrationKey() performs UPDATE registrations SET jwk = ?, jwk_sha256 = ? WHERE id = ? LIMIT 1 with no status guard, unlike DeactivateRegistration() which performs UPDATE registrations SET status = ? WHERE status = ? AND id = ? LIMIT 1.
  4. Lastly, I think there's a race we can narrow here. When we rollover an account key in the WFE we do not check the current account key against blockedKeys, only the new one. Once we add a key to blockedKeys we have ~bad-key-revoker backlog + maxReplicationTime + interval before the bad-key-revoker will revoke an account with that key. What would you think about checking the old key as well?

Comment thread features/features.go
// DeactivateBadKeyAccounts controls whether bad-key-revoker will attempt
// to find and deactivate accounts using keys which have been added to the
// blockedKeys table.
DeactivateBadKeyAccounts bool

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of this feature flag and the comment needs to be updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bad-key-revoker: Check & deactivate account keys

2 participants