keyvault-acmebot: Unable to connect to a 3rd party provider that supports ACME
Issue description
We are trying to connect to Entrust’s ACME API. We have obtained EAB credentials from the provider and we have been able to verify the API works as expected using certbot. As ZeroSSL is supported by vault-acmebot, this provider should work as well.
The README didn’t provide any information on how to use ZeroSSL, so we did a little digging to understand how EAB support worked. When trying to connect to the provider we are adding these configuration parameters:
"Acmebot:Endpoint": "https://acme.entrust.net/acme2/directory"
"Acmebot:ExternalAccountBinding:KeyId": "XXXXXXXX"
"Acmebot:ExternalAccountBinding:HmacKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
However it seems we hit an error inside CreateClientAsync() in line 60 of AcmeProtocolClientFactory.cs. The full error trace is included below, maybe we’re not providing all of the necessary parameters?
Any help will be greatly appreciated!
To Reproduce
Steps to reproduce the behavior:
- Deploy the function (using default settings)
- Update the parameters as highlighted above
- Restart the function
- Create a new certificate using the web UI
Environment (please complete the following information):
- Certificate Type: Sub-domain
- Certificate Deploy Target: Azure Application Gateway
Additional context
Error trace:
18deb201ac0a49f1ae507d1127a554e1: Function 'IssueCertificate (Orchestrator)' failed with an error. Reason: Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionFailedException: The activity function 'Order' failed: "Failed to deserialize exception from TaskActivity: {"$type":"ACMESharp.Protocol.AcmeProtocolException, ACMESharp","ProblemType":0,"ProblemTypeRaw":null,"ProblemDetail":null,"ProblemStatus":-1,"Message":"Unexpected response status code [InternalServerError] for [CreateAccountAsync]","Data":{"$type":"System.Collections.ListDictionaryInternal, System.Private.CoreLib"},"InnerException":null,"HelpLink":null,"Source":"ACMESharp","HResult":-2146233088,"StackTrace":" at ACMESharp.Protocol.AcmeProtocolClient.SendAcmeAsync(Uri uri, HttpMethod method, Object message, HttpStatusCode[] expectedStatuses, Boolean skipNonce, Boolean skipSigning, Boolean includePublicKey, CancellationToken cancel, String opName)\r\n at ACMESharp.Protocol.AcmeProtocolClient.CreateAccountAsync(IEnumerable`1 contacts, Boolean termsOfServiceAgreed, Object externalAccountBinding, Boolean throwOnExistingAccount, CancellationToken cancel)\r\n at KeyVault.Acmebot.Internal.AcmeProtocolClientFactory.CreateClientAsync() in /home/runner/work/keyvault-acmebot/keyvault-acmebot/KeyVault.Acmebot/Internal/AcmeProtocolClientFactory.cs:line 62\r\n at KeyVault.Acmebot.Functions.SharedActivity.Order(IReadOnlyList`1 dnsNames) in /home/runner/work/keyvault-acmebot/keyvault-acmebot/KeyVault.Acmebot/Functions/SharedActivity.cs:line 145\r\n at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.InvokeAsync(Object instance, Object[] arguments) in D:\\a\\_work\\1\\s\\src\\Microsoft.Azure.WebJobs.Host\\Executors\\FunctionInvoker.cs:line 52\r\n at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.InvokeWithTimeoutAsync(IFunctionInvoker invoker, ParameterHelper parameterHelper, CancellationTokenSource timeoutTokenSource, CancellationTokenSource functionCancellationTokenSource, Boolean throwOnTimeout, TimeSpan timerInterval, IFunctionInstance instance) in D:\\a\\_work\\1\\s\\src\\Microsoft.Azure.WebJobs.Host\\Executors\\FunctionExecutor.cs:line 581\r\n at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithWatchersAsync(IFunctionInstanceEx instance, ParameterHelper parameterHelper, ILogger logger, CancellationTokenSource functionCancellationTokenSource) in D:\\a\\_work\\1\\s\\src\\Microsoft.Azure.WebJobs.Host\\Executors\\FunctionExecutor.cs:line 527\r\n at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithLoggingAsync(IFunctionInstanceEx instance, FunctionStartedMessage message, FunctionInstanceLogEntry instanceLogEntry, ParameterHelper parameterHelper, ILogger logger, CancellationToken cancellationToken) in D:\\a\\_work\\1\\s\\src\\Microsoft.Azure.WebJobs.Host\\Executors\\FunctionExecutor.cs:line 306"}". See the function execution logs for additional details.
---> DurableTask.Core.Exceptions.TaskFailedExceptionDeserializationException: Failed to deserialize exception from TaskActivity: {"$type":"ACMESharp.Protocol.AcmeProtocolException, ACMESharp","ProblemType":0,"ProblemTypeRaw":null,"ProblemDetail":null,"ProblemStatus":-1,"Message":"Unexpected response status code [InternalServerError] for [CreateAccountAsync]","Data":{"$type":"System.Collections.ListDictionaryInternal, System.Private.CoreLib"},"InnerException":null,"HelpLink":null,"Source":"ACMESharp","HResult":-2146233088,"StackTrace":" at ACMESharp.Protocol.AcmeProtocolClient.SendAcmeAsync(Uri uri, HttpMethod method, Object message, HttpStatusCode[] expectedStatuses, Boolean skipNonce, Boolean skipSigning, Boolean includePublicKey, CancellationToken cancel, String opName)\r\n at ACMESharp.Protocol.AcmeProtocolClient.CreateAccountAsync(IEnumerable`1 contacts, Boolean termsOfServiceAgreed, Object externalAccountBinding, Boolean throwOnExistingAccount, CancellationToken cancel)\r\n at KeyVault.Acmebot.Internal.AcmeProtocolClientFactory.CreateClientAsync() in /home/runner/work/keyvault-acmebot/keyvault-acmebot/KeyVault.Acmebot/Internal/AcmeProtocolClientFactory.cs:line 62\r\n at KeyVault.Acmebot.Functions.SharedActivity.Order(IReadOnlyList`1 dnsNames) in /home/runner/work/keyvault-acmebot/keyvault-acmebot/KeyVault.Acmebot/Functions/SharedActivity.cs:line 145\r\n at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.InvokeAsync(Object instance, Object[] arguments) in D:\\a\\_work\\1\\s\\src\\Microsoft.Azure.WebJobs.Host\\Executors\\FunctionInvoker.cs:line 52\r\n at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.InvokeWithTimeoutAsync(IFunctionInvoker invoker, ParameterHelper parameterHelper, CancellationTokenSource timeoutTokenSource, CancellationTokenSource functionCancellationTokenSource, Boolean throwOnTimeout, TimeSpan timerInterval, IFunctionInstance instance) in D:\\a\\_work\\1\\s\\src\\Microsoft.Azure.WebJobs.Host\\Executors\\FunctionExecutor.cs:line 581\r\n at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithWatchersAsync(IFunctionInstanceEx instance, ParameterHelper parameterHelper, ILogger logger, CancellationTokenSource functionCancellationTokenSource) in D:\\a\\_work\\1\\s\\src\\Microsoft.Azure.WebJobs.Host\\Executors\\FunctionExecutor.cs:line 527\r\n at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithLoggingAsync(IFunctionInstanceEx instance, FunctionStartedMessage message, FunctionInstanceLogEntry instanceLogEntry, ParameterHelper parameterHelper, ILogger logger, CancellationToken cancellationToken) in D:\\a\\_work\\1\\s\\src\\Microsoft.Azure.WebJobs.Host\\Executors\\FunctionExecutor.cs:line 306"}
---> Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type ACMESharp.Protocol.AcmeProtocolException. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'ProblemType', line 1, position 77.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at DurableTask.Core.Serializing.JsonDataConverter.Deserialize(String data, Type objectType) in /_/src/DurableTask.Core/Serializing/JsonDataConverter.cs:line 110
at DurableTask.Core.Serializing.DataConverter.Deserialize[T](String data) in /_/src/DurableTask.Core/Serializing/DataConverter.cs:line 54
at DurableTask.Core.Common.Utils.RetrieveCause(String details, DataConverter converter) in /_/src/DurableTask.Core/Common/Utils.cs:line 511
--- End of inner exception stack trace ---
--- End of inner exception stack trace ---
at Microsoft.Azure.WebJobs.Extensions.DurableTask.DurableOrchestrationContext.CallDurableTaskFunctionAsync[TResult](String functionName, FunctionType functionType, Boolean oneWay, String instanceId, String operation, RetryOptions retryOptions, Object input, Nullable`1 scheduledTimeUtc) in D:\a\_work\1\s\src\WebJobs.Extensions.DurableTask\ContextImplementations\DurableOrchestrationContext.cs:line 751
at KeyVault.Acmebot.Functions.SharedOrchestrator.IssueCertificate(IDurableOrchestrationContext context) in /home/runner/work/keyvault-acmebot/keyvault-acmebot/KeyVault.Acmebot/Functions/SharedOrchestrator.cs:line 26
at Microsoft.Azure.WebJobs.Host.Executors.VoidTaskMethodInvoker`2.InvokeAsync(TReflected instance, Object[] arguments) in D:\a\_work\1\s\src\Microsoft.Azure.WebJobs.Host\Executors\VoidTaskMethodInvoker.cs:line 20
at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.InvokeAsync(Object instance, Object[] arguments) in D:\a\_work\1\s\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionInvoker.cs:line 52
at Microsoft.Azure.WebJobs.Extensions.DurableTask.TaskOrchestrationShim.InvokeUserCodeAndHandleResults(RegisteredFunctionInfo orchestratorInfo, OrchestrationContext innerContext) in D:\a\_work\1\s\src\WebJobs.Extensions.DurableTask\Listener\TaskOrchestrationShim.cs:line 150. IsReplay: False. State: Failed. HubName: funcdfoentrusti5at. AppName: func-dfo-entrust-i5at. SlotName: Production. ExtensionVersion: 2.10.0. SequenceNumber: 86. TaskEventId: -1
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 36 (14 by maintainers)
Yes! Thanks so much @shibayan!
We’re actually working on the same team, but on different timezones, so we were doing a relay race.
@shibayan I’m happy to share that it successfully issued a certificate using Entrust as the EAB. We @gmakker @palo-manel appreciate all of your work and really value your assistance. I believe you can merge the code into the main branch sans any issues.
@gmakker I added a new option in PR #619 to allow switching between RSA / EC, and will consider merging if I can confirm that it works correctly with Entrust.
I’m going to get in touch with Entrust because I’d like to check compatibility for https://certifytheweb.com (which I develop) - if I get any extra useful implementation information I’ll share it here.
@pahunisaharan Thanks for the nice report. I am working on merging the modified PR.
@palo-manel Did the new Acmebot version I published in my comment issue the certificate correctly?
Confirmed the issue is related to the provider’s implementation… apparently only the RSA algorithm has been implemented, HS256 will becoming available in the near future 🤦♂️
I’m guessing you intentionally chose to leave RSA out 🙂 sorry for wasting your time.
If it is a bug in Acmebot’s EAB implementation, we would like to address it, so please leave the Issue as is.
@palo-manel looks like you can configure the algorithm when setting up EAB, but the typical default for all ACME clients is HS256 and it’s rare to have to specify it in a client.
I also tried to get a new ZeroSSL and Google Public CA certificate using EAB and it worked fine. it seems that the problem only occurs when combined with Entrust.
The operation of EAB is checked only with ZeroSSL. Only three parameters are required, as described in the following Wiki. https://github.com/shibayan/keyvault-acmebot/wiki/External-Account-Binding
I am checking the details of the shared exception, but it is failing because the account creation API is returning an Internal Server Error, not sure why it is returning an Internal Server Error.