ACME CA certificate request is failing with externalAccountBinding error ...
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.
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"}'
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.
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..
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())
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
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
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 :-)