Can not retrieve serial from imported certificate

I am having problems with the PKI backend from vault, especially importing existing CAs and certificates.
vault version: 1.12.3

I have successfully created:

  1. root ca (k3s-ca)
  2. intermediate ca (k3s-client-ca)
  3. intermediate ca (k3s-server-ca)

I used the type exported when creating each of those.
I can now issue certificates with a role with the k3s-client-ca with the python library like so:

c = hvac.Client(url="http://localhost:8200", token="123456"

c.secrets.pki.generate_certificate(
  name="k3s-client",
  mount_point="k3s-client-ca",
  common_name="sustem:admin",
  extra_params={"ip_sans": f"192.168.168.2"},
)

I can also fetch the certificate via this library:

c.secrtes.pki.read_certificate(
   ...: serial="MY_SERIAL",
   ...: mount_point="k3s-ca")

Everything works fine for now.

Setup process from scratch for reference

Setup k3s-ca

Enable the mount point:


vault secrets enable -path=k3s-ca pki

Create a CA and retrieve the certifacte and private key.


vault write -format=json k3s-ca/root/generate/exported common_name="Root, k3s CA" issuer_name="k3s-ca" ttl=87600h key_name="k3s-ca" > k3s-ca-root.json

Create the root k3s-bundle:


jq -r .data.private_key k3s-ca-root.json > k3s-ca-root.pem
jq -r .data.certificate k3s-ca-root.json >> k3s-ca-root.pem

Setup k3s-client-ca

Enable mount point for k3s-client-ca


vault secrets enable -path=k3s-client-ca pki

adjust default lease time to 5 years


vault secrets tune -max-lease-ttl=43800h k3s-client-ca

Create intermediate certifcate signing request (CSR) and retrieve the response from vault as a json file.


vault write -format=json k3s-client-ca/intermediate/generate/exported common_name="k3s-client-ca" > k3s-client-ca-intermediate.csr.json

Export the CSR from the json and store as a file


jq -r .data.csr k3s-client-ca-intermediate.csr.json > k3s-client-ca-intermediate.csr

Export the private key from the json and store as a file


jq -r .data.private_key k3s-client-ca-intermediate.csr.json > k3s-client-ca-intermediate.pem

Take the intermediate certificate CSR and sign it with another Certifcate authority (our root/k3s-ca).

Also store the response as a json file.

The response contains the signed certificate.


vault write -format=json k3s-ca/root/sign-intermediate csr=@k3s-client-ca-intermediate.csr format=pem_bundle ttl=43800h > k3s-client-ca-intermediate.json

Export the signed certificate from json


jq -r .data.certificate k3s-client-ca-intermediate.json > k3s-client-ca-signed-certificate.pem

Import the intermediate certificate k3s-client-ca into vault.
For some reason creating all this with vault does not store this in vault.
Also store the response as a json file which contains the issuer id.

Each certificate has an issuer id.


vault write -format=json k3s-client-ca/intermediate/set-signed certificate=@k3s-client-ca-signed-certificate.pem > k3s-client-ca-import.json

We need the issuer id from k3s-client-ca-import.json later for the role.

I checked and each certificate in the chain will get an id.

I think it depends on the order in the file.

So in this process:

  • The first issuer [0] is the intermediate CA

  • the second issuer [1] is the root CA

That mean we can get the issuer id for our intermediate CA like this:


jq -r '.data.imported_issuers[0]' k3s-client-ca-import.json

set some configs (maybe not needed?)


vault write k3s-client-ca/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki_int/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki_int/crl"

Create roles to allow issuing client certificates.

Here we have to reference the correct issuer id otherwise we will not get a certificate from our intermediate CA.


vault write k3s-client-ca/roles/k3s-client allowed_domains=system:admin organization=system:masters key_usage=DigitalSignature,KeyAgreement,KeyEncipherment issuer_ref=$(jq -r '.data.imported_issuers[0]' k3s-client-ca-import.json) allow_any_name=true allow_bare_domains=false allow_glob_domains=false enforce_hostnames=false max_ttl=86400


Now I clear my vault instance and create an empty one and want to import everything I just created.
For the local testing I just run a local vault with docker:

docker run -it -p 8200:8200 --rm --name vault vault:1.12.3 server -dev -dev-root-token-id=123456

I can import the root ca k3s-ca and the intermediate CA k3s-client-ca and issue certificates just fine.

Working part with importing the k3s-ca and the k3s-client-ca:

Importing root CA with certifcate and key (bundle)

Create the pki mountpoint for the root CA


vault secrets enable -path=k3s-ca pki

Import the root CA bundle:


vault write -format=json k3s-ca/config/ca pem_bundle=@k3s-ca-root.pem > k3s-ca-root-bundle.json

Set the issue name for the root CA.

Not really needed but it is nice to have.


vault write k3s-ca/issuer/$(jq -r '.data.imported_issuers[0]' k3s-ca-root-bundle.json) issuer_name="k3s-root-ca"

Set configurations:


vault write k3s-ca/config/urls issuing_certificates="http://vault.example.com:8200/v1/pki/ca" crl_distribution_points="http://vault.example.com:8200/v1/pki/crl"

Import the intermediate CA bundle k3s-client-ca

Create the pki mountpoint for the intermediate k3s-client-ca


vault secrets enable -path=k3s-client-ca pki

Set the lifetime of the certificates to 5 years.

Needs to be less than the lifetime of the root CA.


vault secrets tune -max-lease-ttl=43800h k3s-client-ca

Import the intermediate CA bundle for k3s-client-ca and save the response to a file:


vault write -format=json k3s-client-ca/issuers/import/bundle pem_bundle=@k3s-client-ca-bundle > k3s-ca-client-bundle-respone.json

Set some configs:


vault write k3s-client-ca/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki_int/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki_int/crl"

Create a role to allow issuing client certificates with the respective issuer.

The issuer id is taken from the response of the import.


vault write k3s-client-ca/roles/k3s-client allowed_domains=system:admin organization=system:masters key_usage=DigitalSignature,KeyAgreement,KeyEncipherment issuer_ref=$(jq -r '.data.imported_issuers[0]' k3s-ca-client-bundle-respone.json) allow_any_name=true allow_bare_domains=false allow_glob_domains=false enforce_hostnames=false max_ttl=86400


Now the part that does not work as expected:

Import the intermediate CA bundle k3s-server-ca.
I can not fetch the k3-server-ca certificate after importing it with c.secrtes.pki.read_certificate(serial="MY_SERIAL", mount_point="k3s-ca") since I do not see any serial.

In the initial setup, I also could see the certificate created on the k3s-ca certificate list in the vault web UI.
I now can not see that anymore in the imported version of this scenario.


vault secrets enable -path=k3s-server-ca pki


vault secrets tune -max-lease-ttl=43800h k3s-server-ca


vault write -format=json k3s-server-ca/config/ca pem_bundle=@k3s-server-ca-bundle > k3s-ca-server-bundle-respone.json


vault write k3s-server-ca/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki_int/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki_int/crl"

Either I am missing something or this is not possible.

Vault is actually functioning as I would expect here.

Each of the three PKI secrets engine instances you create, are completely independent.

It’s normal that the certificate that you import into one of them isn’t visible through another.

The only reason it was visible in the initial issuance case, is because it was stored within the PKI instance that issued it, as well as where it was imported to.

I hope that clarifies somewhat?

So, when I create a new intermediate CA the certificate for that will be stored automatically and therefore can be managed by vault and also retrieved via API with the serial number.

But when I import the CA the certificate will not be imported and therefore not managed by vault. Meaning: I can not retrieve the certificate?

Is that what you are saying?

I don’t fully understand what you mean in your last message.

The Vault PKI engine stores a copy of the certificates it issues. Naturally this is lost if you delete the PKI engine and re-import only the CA certificate and key.

Why is this an issue? Why would you want to retrieve the CA certificate by serial via its root CA?