This is second part where new intermediate CA will be created for issuing server and client certificates (in this case, for OpenVPN server).

If you followed first part of this saga, you should have functional Vault server. Before continuing, be sure that it’s unsealed and you are logged in.

Now, first thing we need to do now is mount pki secrets engine to new path, that will manage our main OpenVPN CA, and tune it’s max lease time.

vault secrets enable -path=pki_CA_ovpn pki
vault secrets tune -max-lease-ttl=43800h pki_CA_ovpn

Now, from this secrets engine we need to generate CSR (certificate signing request), that our root certificate will sign it, and then we import that signed certificate into pki_CA_ovpn.

vault write -format=json pki_CA_ovpn/intermediate/generate/internal \
common_name="OpenVPN Main Intermediate Authority" \
ttl=43800h key_type=ec key_bits=384 | \
jq -r '.data.csr' > pki_intermediate.csr

pki_intermediate.csr file holds our intermediate certificate CSR. We now need to sign it.

vault write -format=json pki_root/root/sign-intermediate csr=@pki_intermediate.csr \
format=pem_bundle ttl="43800h" | \
jq -r '.data.certificate' > intermediate.cert.pem

If there is no error, we have our certificate signed! Next, just import it back to vault.

vault write pki_CA_ovpn/intermediate/set-signed certificate=@intermediate.cert.pem

And that’s it! Now, we have intermediate CA certificate that can sign certificates.

In my use case, each server will have it’s certificates (server one and clients) generated from it’s own pki secrets engine back-end, and for simple reason; I want to have client certificates that are tied to specific server. Because, if we create two roles in pki_CA_ovpn for generating server and client certificates, every client certificate would work on any server certificate generated from that CA… And, when revoking server certificate, you would need to revoke every client certificate separately AFAIK… But, if each server has it’s own pki back-end and is signing it’s client certificates, that certificates will only work for that server, and when we revoke server certificate (and it’s intermediate CA), all client certificates then become invalid and unusable.

So, we need to repeat almost all steps from CA back-end, with small modifications…

vault secrets enable -path=pki_example_com pki
vault secrets tune -max-lease-ttl=21900h pki_example_com

Next generate certificates CSR.

vault write -format=json pki_example_com/intermediate/generate/internal \
common_name="example.com Intermediate Authority" \
ttl=43800h key_type=ec key_bits=384 | \
jq -r '.data.csr' > pki_example_com.csr

pki_example_com.csr file holds our new intermediate certificate CSR. We now need to sign it, and we sign it with main OpenVPN intermediate CA

vault write -format=json pki_CA_ovpn/root/sign-intermediate \
csr=@pki_example_com.csr \
format=pem_bundle ttl="43800h" | \
jq -r '.data.certificate' > pki_example_com.cert.pem

Now, import it back.

vault write pki_example_com/intermediate/set-signed certificate=@pki_example_com.cert.pem

And that’s it! Now, we have another intermediate CA certificate that can sign certificates, but this one will be used for single domain.

Next step is to create role which will be used for signing/issuing server certificates

vault write pki_example_com/roles/server \
    allowed_domains=example.com \
    allow_subdomains=true \
    max_ttl=43800h \
    allow_ip_sans=true allowed_uri_sans="*.example.com" \
    key_type=ec key_bits=384 \
    client_flag=false server_flag=true \
    code_signing_flag=false email_protection_flag=false

And role for client certificates

vault write pki_example_com/roles/client \
    max_ttl=43800h \
    key_type=ec key_bits=384 \
    allow_any_name=true enforce_hostnames=false
    client_flag=true server_flag=false \
    code_signing_flag=false email_protection_flag=false

Now, generate and get server certificate.

vault write pki_example_com/issue/server \
common_name="test.example.com" \
ttl="2400h"

Also, for client certificate.

vault write pki_example_com/issue/client common_name="user@example.com"

And from both outputs save certificate key and value of private_key key.

And that should be it… I’ll test it more next few days, and make updates where/if needed…