From d190fb15c986362d6a09fdecc6352a1683538b56 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Tue, 18 Nov 2025 09:20:22 -0800 Subject: [PATCH] Add test to reproduce the problem --- backend/bdd/features/pki/acme/account.feature | 23 ++++++++++++++-- backend/bdd/features/steps/pki_acme.py | 27 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/backend/bdd/features/pki/acme/account.feature b/backend/bdd/features/pki/acme/account.feature index 589c5ab24..04b1f29c6 100644 --- a/backend/bdd/features/pki/acme/account.feature +++ b/backend/bdd/features/pki/acme/account.feature @@ -11,8 +11,27 @@ Feature: Account When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account And I memorize acme_account.uri as account_uri - And I find the existing ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account - And the value acme_account.uri should be equal to "{account_uri}" + And I find the existing ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as retrieved_account + And the value retrieved_account.uri should be equal to "{account_uri}" + + # Note: This is a very special case for cert-manager. + # There's a bug in their ACME client implementation, they don't take the account KID value they have + # and relying on a '{"onlyReturnExisting": true}' new-account request to find out their KID value. + # But the problem is, that new-account request doesn't come with EAB. And while the get existing account operation + # fails, they just discard the error and proceed to request a new order. Since no KID provided, their ACME + # client will send JWK instead. As a result, we are seeing KID not provide in header error for the new-order + # endpoint. + # + # To solve the problem, we lose the check for EAB a bit for the onlyReturnExisting new account request + # ref: https://github.com/cert-manager/cert-manager/issues/7388#issuecomment-3535630925 + Scenario: Create a new account with EAB then retrieve it without EAB + Given I have an ACME cert profile as "acme_profile" + When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" + Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account + And I memorize acme_account.uri as account_uri + And I find the existing ACME account without EAB as retrieved_account + And the value error with should be absent + And the value retrieved_account.uri should be equal to "{account_uri}" Scenario: Create a new account without EAB Given I have an ACME cert profile as "acme_profile" diff --git a/backend/bdd/features/steps/pki_acme.py b/backend/bdd/features/steps/pki_acme.py index 5043b0e7d..e194fdb84 100644 --- a/backend/bdd/features/steps/pki_acme.py +++ b/backend/bdd/features/steps/pki_acme.py @@ -434,6 +434,20 @@ def step_impl(context: Context, email: str, kid: str, secret: str, account_var: ) +@then("I find the existing ACME account without EAB as {account_var}") +def step_impl(context: Context, account_var: str): + acme_client = context.acme_client + registration = messages.NewRegistration.from_data( + only_return_existing=True, + ) + # Reset account so that it will send JWK instead of KID + acme_client.net.account = None + try: + context.vars[account_var] = acme_client.new_account(registration) + except Exception as exp: + context.vars["error"] = exp + + @then("I register a new ACME account with email {email} without EAB") def step_impl(context: Context, email: str): acme_client = context.acme_client @@ -600,6 +614,19 @@ def step_impl(context: Context, var_path: str, jq_query: str): ) +@then("the value {var_path} with should be absent") +def step_impl(context: Context, var_path: str): + try: + value = eval_var(context, var_path) + except Exception as exp: + if isinstance(exp, KeyError): + return + raise + assert False, ( + f"value at {var_path!r} should be absent, but we got this instead: {value!r}" + ) + + @then('the value {var_path} with jq "{jq_query}" should be equal to {expected}') def step_impl(context: Context, var_path: str, jq_query: str, expected: str): value, result = apply_value_with_jq(