hashicorp / terraform-provider-azurerm

Terraform provider for Azure Resource Manager
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
Mozilla Public License 2.0
4.46k stars 4.54k forks source link

Unable to create key vault certificate. Reports invalid PKCS#12 format #15914

Open anuradhaarao opened 2 years ago

anuradhaarao commented 2 years ago

Community Note

Terraform (and AzureRM Provider) Version

Affected Resource(s)

Terraform Configuration Files

resource "azurerm_key_vault_certificate" "certificate" {
  name         = "wildcard-${local.certificate_name}" #replaces dot with dashes.
  key_vault_id = module.key_vault.key_vault_id
  depends_on = [module.key_vault.terraform_managed_identity_policy]
  certificate {
    contents = data.azurerm_key_vault_secret.cert.value
    password = data.azurerm_key_vault_secret.cert_password.value
  }
  certificate_policy {
    issuer_parameters {
      name = "Unknown"
    }
    key_properties {
      exportable = true
      key_size   = 2048
      key_type   = "RSA"
      reuse_key  = false
    }
    secret_properties {
      content_type = "application/x-pkcs12"
    }
  }
}

Expected Behaviour

resource successfully created

Actual Behaviour

keyvault.BaseClient#ImportCertificate: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="BadParameter" Message="The specified PKCS#12 X.509 certificate content can not be read. Please check if certificate is in valid PKCS#12 format."

Steps to Reproduce

  1. terraform apply

Important Factoids

  1. pfx certificate is stored in another azure keyvault as a secret, which is being used to create this certificate
  2. Import of pfx using secret and password stored in keyvault checks out

References

myc2h6o commented 2 years ago

Hi @anuradhaarao According to the error message, looks like the cert content is not in correct format. There is one possibility I could think of is the format is application/x-pem-file, maybe you can give it a try.

anuradhaarao commented 2 years ago

@myc2h6o - The secret is base64encoded pfx value too, and using powershell I can confirm that this can be imported as a pfx file successfully. But I am not able to do the same thing through terraform and was wondering if any additional parameters are required.

myc2h6o commented 2 years ago

Hi @anuradhaarao Since it's probably not possible to share the cert, I'm sharing what I've done which can generate the cert successfully. Hope this could help you troubleshoot the issue.

Terraform version: v1.1.5 AzureRM provider version: v2.99.0

I used below command to generate a cert: New-SelfSignedCertificate -DnsName "www.contoso.com" -CertStoreLocation "cert:\LocalMachine\My"

Thumbprint                                Subject
----------                                -------
0557CBA10E47B975532B453918285777855F04D6  CN=www.contoso.com

And export to pfx with the password: $CertPassword = ConvertTo-SecureString -String "123456" -Force -AsPlainText; Export-PfxCertificate -Cert cert:\LocalMachine\My\0557CBA10E47B975532B453918285777855F04D6 -FilePath C:\test.pfx -Password $CertPassword

Then convert it to base64 string: [Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\test.pfx")) > base64pfx.txt

I then stored the output from base64pfx.txt and the password 123456 to Azure Key Vault Secrets. After that, I'm able to create the cert with the config file you shared (with module.key_vault.key_vault_id updated to my key vault id and depends_on removed)

You mentioned you are able to import the cert using PowerShell, maybe there is some different validation between the Azure API called by PowerShell and Terraform. For Terraform, you can set environment variable TF_LOG to DEBUG (https://www.terraform.io/internals/debugging) to see the API request detail, it may have some useful info which you can compare with the property values of certificate created from PowerShell

yanehi commented 2 years ago

Unfortunately I get the same error as @anuradhaarao with the certificate in pfx format. But for me the error only occurs when I run terraform apply in my Gitlab-CI (ubuntu:jammy). As soon as I run the same command from my Mac laptop, I didnt get the error. Manually I also can import the same certificate successfully in Azure KeyVault.

In the Gitlab-CI my Ubuntu Image uses the Terraform version: v1.2.4 and the AzureRM provider version: v3.11.0. Locally I use the Terraform version: v1.1.8.

myc2h6o commented 1 year ago

@yanehi sorry for the late reply. The error StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="BadParameter" Message="The specified PKCS#12 X.509 certificate content can not be read. Please check if certificate is in valid PKCS#12 format." is returned from the service side so probably the generated request body is different in these two environments. Since it's running fine on your Mac but not on the Gitlab-CI, would you mind turn on the Debug logging as mentioned at https://www.terraform.io/internals/debugging to see the API request detail, it may help with the troubleshooting.

vermegi commented 1 year ago

I have the same issue in importing a pfx/pem file through Terraform, getting the same error message. I tried setting the TF_LOG environment variable to TRACE, however, it didn't produce any additional log output for Terraform. I also tried importing the same cert directly through the portal: this was successful. I also tried importing the same certificate through az cli statement, this also failed. Here I did get debug output:

az keyvault certificate import --vault-name "kv_name" -n "ExampleCertificate" -f "C:/Users/myname/OneDrive - Microsoft/Personal/star_myname_com/wildcard_myname_com.pfx" --debug ←[36mcli.knack.cli: Command arguments: ['keyvault', 'certificate', 'import', '--vault-name', 'kv_name', '-n', 'ExampleCertificate', '-f', 'C:/Users/myname/OneDrive - Microsoft/Personal/star_myname_com/wildcard_myname_com.pfx', '--debug']←[0m ←[36mcli.knack.cli: init debug log: Enable color in terminal.←[0m ←[36mcli.knack.cli: Event: Cli.PreExecute []←[0m ←[36mcli.knack.cli: Event: CommandParser.OnGlobalArgumentsCreate [<function CLILogging.on_global_arguments at 0x0352BD60>, <function OutputProducer.on_global_arguments at 0x0371E6A0>, <function CLIQuery.on_global_arguments at 0x037312F8>]←[0m

←[36mcli.knack.cli: Event: CommandInvoker.OnPreCommandTableCreate []←[0m ←[36mcli.azure.cli.core: Modules found from index for 'keyvault': ['azure.cli.command_modules.keyvault']←[0m ←[36mcli.azure.cli.core: Loading command modules:←[0m ←[36mcli.azure.cli.core: Name Load Time Groups Commands←[0m ←[36mcli.azure.cli.core: keyvault 0.081 20 122←[0m ←[36mcli.azure.cli.core: Total (1) 0.081 20 122←[0m ←[36mcli.azure.cli.core: These extensions are not installed and will be skipped: ['azext_ai_examples', 'azext_next']←[0m ←[36mcli.azure.cli.core: Loading extensions:←[0m ←[36mcli.azure.cli.core: Name Load Time Groups Commands Directory←[0m ←[36mcli.azure.cli.core: Total (0) 0.000 0 0 ←[0m ←[36mcli.azure.cli.core: Loaded 20 groups, 122 commands.←[0m ←[36mcli.azure.cli.core: Found a match in the command table.←[0m ←[36mcli.azure.cli.core: Raw command : keyvault certificate import←[0m ←[36mcli.azure.cli.core: Command table: keyvault certificate import←[0m ←[36mcli.knack.cli: Event: CommandInvoker.OnPreCommandTableTruncate [<function AzCliLogging.init_command_file_logging at 0x042A1F10>]←[0m ←[36mcli.azure.cli.core.azlogging: metadata file logging enabled - writing logs to 'C:\Users\myname.azure\commands\2022-10-07.08-30-36.keyvault_certificate_import.11848.log'.←[0m ←[32maz_command_data_logger: command args: keyvault certificate import --vault-name {} -n {} -f {} --debug←[0m ←[36mcli.knack.cli: Event: CommandInvoker.OnPreArgumentLoad [<function register_global_subscription_argument..add_subscription_parameter at 0x042DCEC8>]←[0m ←[36mcli.knack.cli: Event: CommandInvoker.OnPostArgumentLoad []←[0m ←[36mcli.knack.cli: Event: CommandInvoker.OnPostCommandTableCreate [<function register_ids_argument..add_ids_arguments at 0x042DCFA0>, <function register_cache_arguments..add_cache_arguments at 0x04306538>]←[0m ←[36mcli.knack.cli: Event: CommandInvoker.OnCommandTableLoaded []←[0m ←[36mcli.knack.cli: Event: CommandInvoker.OnPreParseArgs []←[0m ←[36mcli.knack.cli: Event: CommandInvoker.OnPostParseArgs [<function OutputProducer.handle_output_argument at 0x0371E6E8>, <function CLIQuery.handle_query_parameter at 0x03731340>, <function register_ids_argument..parse_ids_arguments at 0x043064F0>]←[0m ←[36mmsrest.universal_http.requests: Configuring retry: max_retries=4, backoff_factor=0.8, max_backoff=90←[0m ←[36mcli.azure.cli.core.util: azure.cli.core.util.handle_exception is called with an exception:←[0m ←[36mcli.azure.cli.core.util: Traceback (most recent call last): File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/keyvault/custom.py", line 1622, in import_certificate File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\OpenSSL/crypto.py", line 3067, in load_pkcs12
File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\OpenSSL/_util.py", line 54, in exception_from_error_queue OpenSSL.crypto.Error: [('PKCS12 routines', 'PKCS12_parse', 'mac verify failure')]

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/keyvault/_command_type.py", line 112, in keyvault_command_handler File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/keyvault/custom.py", line 1626, in import_certificate knack.util.CLIError: We could not parse the provided certificate as .pem or .pfx. Please verify the certificate with OpenSSL.

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\knack/cli.py", line 231, in invoke File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/init.py", line 663, in execute File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/init.py", line 726, in _run_jobs_serially File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/init.py", line 697, in _run_job File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/init.py", line 333, in call File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/keyvault/_command_type.py", line 138, in keyvault_command_handler File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/keyvault/_command_type.py", line 51, in keyvault_exception_handler knack.util.CLIError: We could not parse the provided certificate as .pem or .pfx. Please verify the certificate with OpenSSL. ←[0m ←[91mcli.azure.cli.core.azclierror: We could not parse the provided certificate as .pem or .pfx. Please verify the certificate with OpenSSL.←[0m ←[91maz_command_data_logger: We could not parse the provided certificate as .pem or .pfx. Please verify the certificate with OpenSSL.←[0m ←[36mcli.knack.cli: Event: Cli.PostExecute [<function AzCliLogging.deinit_cmd_metadata_logging at 0x042A4070>]←[0m
←[32maz_command_data_logger: exit code: 1←[0m ←[32mcli.main: Command ran in 6.951 seconds (init: 3.745, invoke: 3.206)←[0m ←[32mtelemetry.save: Save telemetry record of length 3065 in cache←[0m ←[32mtelemetry.check: Returns Positive.←[0m ←[32mtelemetry.main: Begin creating telemetry upload process.←[0m ←[32mtelemetry.process: Creating upload process: "C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\python.exe C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\Lib\site-packages\azure\cli\telemetry__init__.pyc C:\Users\myname.azure"←[0m
←[32mtelemetry.process: Return from creating process←[0m ←[32mtelemetry.main: Finish creating telemetry upload process.←[0m

My Terraform environment is running in a GH codespace, there I have versions:

I tried the upload both on from Linux (GH codespace using debian) and from my local laptop, both failing with the same error.

So summing up:

vermegi commented 1 year ago

Update: I was able to import the certificate with az cli, by specifying the password as an extra parameter: az keyvault certificate import --vault-name "kv_name" -n "ExampleCertificate" -f "../wildcard_myname_com.pfx" --password $CERT_PASSWORD

The same with TF is still failing though. Relevant TF part: resource "azurerm_key_vault_certificate" "uploaded_cert" { count = var.use_self_signed_cert ? 0 : 1 name = var.cert_name key_vault_id = azurerm_key_vault.kv.id

certificate { contents = filebase64(var.cert_path) password = var.cert_password } }

Error message: Message="The specified PKCS#12 X.509 certificate content can not be read. Please check if certificate is in valid PKCS#12 format."

Happy to try out the TF logging, if someone can give me pointers on how to set it up TF_LOG=TRACE did not work for me.

vermegi commented 1 year ago

So, I fixed the issue now in my TF as well. Took me a null_resource with local_exec to execute the az cli directly. This also outputted the password I was using for the certificate, and with this, quotes do matter!

My plan statement that now makes my apply work:

terraform plan -var-file="myvars.tfvars" -out=plan.tfplan -var='git_repo_passwords=["$GIT_REPO_PASSWORD","$GIT_REPO_PASSWORD"]' -var="cert_password=$CERT_PASSWORD"

Mind the quotes!

myc2h6o commented 1 year ago

@vermegi thanks for sharing the experience. setting TF_LOG to DEBUG shall give you the API request details.