lemur icon indicating copy to clipboard operation
lemur copied to clipboard

ACME CA certificate request is failing with externalAccountBinding error ...

Open ghost opened this issue 5 years ago • 7 comments

Using the documentation, I was able to create ACME CA with the URL provided by Sectigo. Then when I tried sample cert request it failed with the following error:

acme.messages.Error: urn:ietf:params:acme:error:externalAccountRequired :: The server requires external account binding :: The request must include a value for the "externalAccountBinding" field 2020-09-09 00:49:01,065 DEBUG: {'function': 'lemur.common.celery.sync_source_destination', 'message': 'syncing AWS destinations and sources', 'task_id': 'ef39db61-def1-4d8c-9210-86559f4e1c63'} [in /opt/app/lemur/lemur/common/celery.py:582]

I am using /opt/app/lemur as installation folder.

I understand, I may have to have the CONFIG details for EAB ID and EAB Key for External Account Binding.

What I need help is to how to pass these values in lemur.conf.py or other location? I do not see these options when ACME accounts are created. How do we handle multiple ACME providers and their configurations.

ghost avatar Sep 09 '20 01:09 ghost

Hi @rb5acgusr,

you can find the configuration parameters for the ACME client here:

https://github.com/Netflix/lemur/blob/983f9beacb85cb1ecb8ce10a5028d2664dffed23/lemur/plugins/lemur_acme/plugin.py#L237-L240

Lemur uses acme.client.BackwardsCompatibleClientV2 to create an ACME client: https://acme-python.readthedocs.io/en/stable/api/client.html#acme.client.BackwardsCompatibleClientV2

you would need to provide the private key under ACME_PRIVATE_KEY and the registration under ACME_REGR, for instance:

ACME_REGR = '{"body": {}, "uri": "https://acme-v02.api.letsencrypt.org/acme/acct/123456779"}'

hosssha avatar Sep 10 '20 23:09 hosssha

I tried the following:

Pass EAB HMAC key for ACME_PRIVATE_KEY and

ACME_REGR = '{"body": {}, "uri": "https://acme.sectigo.com/v2/InCommonRSAOV/acct/EAB_KID"}'

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "/opt/app/lemur/lemur/certificates/service.py", line 348, in create cert_body, private_key, cert_chain, external_id, csr = mint(**kwargs) File "/opt/app/lemur/lemur/certificates/service.py", line 298, in mint cert_body, cert_chain, external_id = issuer.create_certificate(csr, kwargs) File "/opt/app/lemur/lemur/plugins/lemur_acme/plugin.py", line 637, in create_certificate acme_client, registration = self.acme.setup_acme_client(authority) File "/opt/app/lemur/lib/python3.6/site-packages/retrying.py", line 49, in wrapped_f return Retrying(*dargs, **dkw).call(f, *args, **kw) File "/opt/app/lemur/lib/python3.6/site-packages/retrying.py", line 212, in call raise attempt.get() File "/opt/app/lemur/lib/python3.6/site-packages/retrying.py", line 247, in get six.reraise(self.value[0], self.value[1], self.value[2]) File "/opt/app/lemur/lib/python3.6/site-packages/six.py", line 703, in reraise raise value File "/opt/app/lemur/lib/python3.6/site-packages/retrying.py", line 200, in call attempt = Attempt(fn(*args, **kwargs), attempt_number, False) File "/opt/app/lemur/lemur/plugins/lemur_acme/plugin.py", line 244, in setup_acme_client key = jose.JWK.json_loads(existing_key) File "/opt/app/lemur/lib/python3.6/site-packages/josepy/interfaces.py", line 182, in json_loads raise errors.DeserializationError(error) josepy.errors.DeserializationError: Deserialization error: Expecting value: line 1 column 1 (char 0) 2020-09-11 16:18:12,439 ERROR: Exception minting certificate [in /opt/app/lemur/lemur/certificates/service.py:350] Traceback (most recent call last): File "/opt/app/lemur/lib/python3.6/site-packages/josepy/interfaces.py", line 180, in json_loads loads = json.loads(json_string) File "/usr/lib/python3.6/json/init.py", line 354, in loads return _default_decoder.decode(s) File "/usr/lib/python3.6/json/decoder.py", line 339, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/usr/lib/python3.6/json/decoder.py", line 357, in raw_decode raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

I am also checking with my CA support on the correct URI to pass.

rb5acgusr avatar Sep 11 '20 16:09 rb5acgusr

When I register with certbot client, it registers with the following information:

{"body": {}, "uri": "https://acme.sectigo.com/v2/InCommonRSAOV/account/EAB_KID"}

Where EAB_KID is my account ID. I tried recreating the Authority by passing this URL. I still get above Python error. Now, I believe, passing ACME_PRIVATE_KEY in lemur.conf.py resolved externalAccountBinding error.

Next battle is with this Python error..

rb5acgusr avatar Sep 11 '20 19:09 rb5acgusr

ACME_PRIVATE_KEY needs to be in the JWK format (e.g. similar to this)

{
    "kty": "RSA",
    "n": "yr1qBwHizA7ME_iV32bY10ILp.....",
    "e": "AQAB",
    "d": "llBlYhil3I.....",
    "p": "-5LW2Lewogo.........",
    "q": "zk6dHqHfHksd.........",
    "dp": "qfe9fFIu3mu.......",
    "dq": "cXFO-loeOyU.......",
    "qi": "AfK1sh0_8sLTb..........."
}

Using python-jwt this can be done with a simple script

import python_jwt as jwt, jwcrypto.jwk as jwk, datetime

priv_key = jwk.JWK.from_pem(b"""-----BEGIN RSA PRIVATE KEY-----
.......
-----END RSA PRIVATE KEY-----""")

print(priv_key.export())

peschmae avatar Sep 22 '20 16:09 peschmae

I have certbot client installed on the same machine. When I registered Sectigo ACME server it created private key json file.

It is in this format: { "n": "PwIOkViO ......", "e": "AQAB", "d": "NWX .... ", "p": "6-x3 ... ", "q": "3j- .....", "dp": "HhXQfDWRLIE ....", "dq": "3kj43ZTzvsE ....", "qi": "AlLaMQBcky8 ....", "kty": "RSA" }

After using this I got ill formatted URL for

ACME_REGR = '{"body": {}, "uri": "https://acme.sectigo.com/v2/InCommonRSAOV/acct/EAB_KID"}'

Then I changed this to

ACME_REGR = '{"body": {}, "uri": "https://acme.sectigo.com/v2/InCommonRSAOV"}'

and

{ "n": "PwIOkViO ......", "e": "AQAB", "d": "NWX .... ", "p": "6-x3 ... ", "q": "3j- .....", "dp": "HhXQfDWRLIE ....", "dq": "3kj43ZTzvsE ....", "qi": "AlLaMQBcky8 ....", "kid": "pW4 ...", "kty": "RSA" }

Passing EAB_KID in JWK format. I still get error:

acme.messages.Error: urn:ietf:params:acme:error:externalAccountRequired :: The server requires external account binding :: The request must include a value for the "externalAccountBinding" field

ACME_REGR = '{"body": {}, "uri": "https://acme.sectigo.com/v2/InCommonRSAOV/acct/EAB_KID"}' is giving ill format error

rb5acgusr avatar Sep 23 '20 06:09 rb5acgusr

It looks like acme.sectigo.com is using the externalAccountBinding described in the acme spec 7.3.4. External Account Binding, but LetsEncrypt doesn't use it, and the current implementation of the acme plugin, doesn't support it either.

I would check with sectigo.com, which account uri that needs to be used. Normally you should be able to get the account details (including the account uri), when trying to create a new account, using the existing private key. I've used ansible to do that, and get the account uri

    - name: get acme account info
      acme_account_info:
        acme_version: 2
        acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
        account_key_src: ./acme.key
      register: account_data

    - name: display account information
      debug:
        var: account_data

You would need to replace the acme_directory parameter with the directory provided by sectigo.

Or you can implement it, in lemur/lemur/plugins/lemur_acme/plugin.py https://github.com/Netflix/lemur/blob/772894c414baca916c44ce1f91fb3fe6b33c88f1/lemur/plugins/lemur_acme/plugin.py#L260-L265

You would need to add an externalAccountBinding to the from_data call, which seems to support this parameter according to the acme-python documentation

peschmae avatar Sep 23 '20 08:09 peschmae

When I register with Certbot against InCommon with EAB KID and EAB Key, it provides the ACME_PRIVATE_KEY in JWK format. I then used those details along with regr.json info for ACME_REGR. Finally, by using InCommon ACME URL for ACME_DIRECTORY_URL I am not getting malformed URL error.

Now, I get the following error: raise Exception("Unable to determine DNS challenges from authorizations") Exception: Unable to determine DNS challenges from authorizations 2020-09-24 19:46:44,388 ERROR: {'function': 'lemur.common.celery.fetch_acme_cert', 'message': 'Pending certificate creation failure', 'task_id': '84b6997f-1729-464e-be42-bbdad5b24436', 'id': 3, 'pending_cert_id': 3, 'last_error': Exception('Unable to determine DNS challenges from authorizations',), 'cn': 'Servername'} [in /opt/app/lemur/lemur/common/celery.py:320]

What I observed is that InCommon ACME does not do DNS validations as long as EAB Kid and Key are matching. This looks like another enhancement to make DNS validation as optional for externalAccountBinding ACME providers :-)

rb5acgusr avatar Sep 24 '20 19:09 rb5acgusr