DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Strengthening Cybersecurity: The Role of Digital Certificates and PKI in Authentication
  • Minimus Expands Enterprise Security Platform with General Availability of Advanced Supply Chain Controls
  • Building Threat Intelligence Pipelines Using Python, APIs, and Elasticsearch
  • Identity in Action

Trending

  • Why Your AI Agent's Logs Aren't Earning Trust
  • Give Your AI Assistant Long-Term Memory With perag
  • Native SQL in Java Without JDBC Boilerplate — Meet Ujorm3
  • Prompt Injection Is Real, So I Built a Python Firewall for LLM Pipelines
  1. DZone
  2. Software Design and Architecture
  3. Security
  4. Our Path to Better Certificate Management With Vault and FreeIPA

Our Path to Better Certificate Management With Vault and FreeIPA

Learn how to turn HashiCorp Vault into a subordinate CA under FreeIPA to streamline and automate PKI in cloud-native environments.

By 
Nikolai Mishin user avatar
Nikolai Mishin
·
Oct. 14, 25 · Analysis
Likes (3)
Comment
Save
Tweet
Share
2.0K Views

Join the DZone community and get the full member experience.

Join For Free

Managing public key infrastructure (PKI) is challenging, especially in dynamic, cloud-native environments. In the “good old days,” you could create a virtual machine, place a certificate on it, and forget about it for a couple of years (or at least until the certificate expired). But as modern infrastructure has evolved, a more automated and scalable approach is needed.

In this article, we’ll explore how to configure HashiCorp Vault as a subordinate Certificate Authority (CA) under FreeIPA, how to request certificates, and build a certificate chain trusted by any host in your infrastructure.

Problem in the Wild

Many organizations rely on FreeIPA not only for identity but also for certificate issuance. FreeIPA is a well-established tool, and remains a solid choice in many infrastructures.

However, it wasn't originally designed to integrate with Kubernetes, and due to the ephemeral and dynamic nature of pods, the process of issuing certificates can be a difficult task. That's where HashiCorp Vault comes in.

We implement a two-tier certificate authority system, where FreeIPA CA is used as a root CA and Vault is configured as an intermediate CA, signed by FreeIPA. This architecture not only enables our transition to cloud-native infrastructure but also enhances security and maintainability.

The following is an architecture diagram:

Architecture diagram

  • A FreeIPA server is used as the Root CA of the environment.
  • A HashiCorp Vault instance. It can be run in the Kubernetes cluster, as well as in a virtual machine.
  • Applications hosted in the Kubernetes cluster.
  • Any Virtual Machines with applications that need to have their own certificates.

Prerequisites. In this article, we will assume that we have a fully working and configured HashiCorp Vault and FreeIPA, and that we have administrator access to both of them.

FreeIPA configuration

First of all, we need to configure the certificate profile. Let's use this template. I will highlight what we need to update after the listing:

Shell
 
desc=This certificate profile is for enrolling Subordinate Certificate Authority certificates.
visible=true
enable=true
auth.instance_id=raCertAuth
classId=caEnrollImpl
enableBy=ipara
name=Subordinate CA Certificate Profile
input.list=i1,i2
input.i1.class_id=certReqInputImpl
input.i2.class_id=submitterInfoInputImpl
output.list=o1
output.o1.class_id=certOutputImpl
policyset.list=caSubCertSet
policyset.caSubCertSet.list=1,2,3,4,5,6,8,9,10
policyset.caSubCertSet.1.constraint.class_id=subjectNameConstraintImpl
policyset.caSubCertSet.1.constraint.name=Subject Name Constraint
policyset.caSubCertSet.1.constraint.params.pattern=.*CN=.+
policyset.caSubCertSet.1.constraint.params.accept=true
policyset.caSubCertSet.1.default.class_id=userSubjectNameDefaultImpl
policyset.caSubCertSet.1.default.name=Subject Name Default
policyset.caSubCertSet.1.default.params.name=
policyset.caSubCertSet.2.constraint.class_id=validityConstraintImpl
policyset.caSubCertSet.2.constraint.name=Validity Constraint
policyset.caSubCertSet.2.constraint.params.range=7305
policyset.caSubCertSet.2.constraint.params.notBeforeCheck=false
policyset.caSubCertSet.2.constraint.params.notAfterCheck=false
policyset.caSubCertSet.2.default.class_id=caValidityDefaultImpl
policyset.caSubCertSet.2.default.name=CA Certificate Validity Default
policyset.caSubCertSet.2.default.params.range=7305
policyset.caSubCertSet.2.default.params.startTime=0
policyset.caSubCertSet.3.constraint.class_id=keyConstraintImpl
policyset.caSubCertSet.3.constraint.name=Key Constraint
policyset.caSubCertSet.3.constraint.params.keyType=-
policyset.caSubCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096,nistp256,nistp384,nistp521
policyset.caSubCertSet.3.default.class_id=userKeyDefaultImpl
policyset.caSubCertSet.3.default.name=Key Default
policyset.caSubCertSet.4.constraint.class_id=noConstraintImpl
policyset.caSubCertSet.4.constraint.name=No Constraint
policyset.caSubCertSet.4.default.class_id=authorityKeyIdentifierExtDefaultImpl
policyset.caSubCertSet.4.default.name=Authority Key Identifier Default
policyset.caSubCertSet.5.constraint.class_id=basicConstraintsExtConstraintImpl
policyset.caSubCertSet.5.constraint.name=Basic Constraint Extension Constraint
policyset.caSubCertSet.5.constraint.params.basicConstraintsCritical=true
policyset.caSubCertSet.5.constraint.params.basicConstraintsIsCA=true
policyset.caSubCertSet.5.constraint.params.basicConstraintsMinPathLen=0
policyset.caSubCertSet.5.constraint.params.basicConstraintsMaxPathLen=0
policyset.caSubCertSet.5.default.class_id=basicConstraintsExtDefaultImpl
policyset.caSubCertSet.5.default.name=Basic Constraints Extension Default
policyset.caSubCertSet.5.default.params.basicConstraintsCritical=true
policyset.caSubCertSet.5.default.params.basicConstraintsIsCA=true
policyset.caSubCertSet.5.default.params.basicConstraintsPathLen=0
policyset.caSubCertSet.6.constraint.class_id=keyUsageExtConstraintImpl
policyset.caSubCertSet.6.constraint.name=Key Usage Extension Constraint
policyset.caSubCertSet.6.constraint.params.keyUsageCritical=true
policyset.caSubCertSet.6.constraint.params.keyUsageDigitalSignature=true
policyset.caSubCertSet.6.constraint.params.keyUsageNonRepudiation=true
policyset.caSubCertSet.6.constraint.params.keyUsageDataEncipherment=false
policyset.caSubCertSet.6.constraint.params.keyUsageKeyEncipherment=false
policyset.caSubCertSet.6.constraint.params.keyUsageKeyAgreement=false
policyset.caSubCertSet.6.constraint.params.keyUsageKeyCertSign=true
policyset.caSubCertSet.6.constraint.params.keyUsageCrlSign=true
policyset.caSubCertSet.6.constraint.params.keyUsageEncipherOnly=false
policyset.caSubCertSet.6.constraint.params.keyUsageDecipherOnly=false
policyset.caSubCertSet.6.default.class_id=keyUsageExtDefaultImpl
policyset.caSubCertSet.6.default.name=Key Usage Default
policyset.caSubCertSet.6.default.params.keyUsageCritical=true
policyset.caSubCertSet.6.default.params.keyUsageDigitalSignature=true
policyset.caSubCertSet.6.default.params.keyUsageNonRepudiation=true
policyset.caSubCertSet.6.default.params.keyUsageDataEncipherment=false
policyset.caSubCertSet.6.default.params.keyUsageKeyEncipherment=false
policyset.caSubCertSet.6.default.params.keyUsageKeyAgreement=false
policyset.caSubCertSet.6.default.params.keyUsageKeyCertSign=true
policyset.caSubCertSet.6.default.params.keyUsageCrlSign=true
policyset.caSubCertSet.6.default.params.keyUsageEncipherOnly=false
policyset.caSubCertSet.6.default.params.keyUsageDecipherOnly=false
policyset.caSubCertSet.8.constraint.class_id=noConstraintImpl
policyset.caSubCertSet.8.constraint.name=No Constraint
policyset.caSubCertSet.8.default.class_id=subjectKeyIdentifierExtDefaultImpl
policyset.caSubCertSet.8.default.name=Subject Key Identifier Extension Default
policyset.caSubCertSet.8.default.params.critical=false
policyset.caSubCertSet.9.constraint.class_id=signingAlgConstraintImpl
policyset.caSubCertSet.9.constraint.name=No Constraint
policyset.caSubCertSet.9.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withEC,SHA512withEC
policyset.caSubCertSet.9.default.class_id=signingAlgDefaultImpl
policyset.caSubCertSet.9.default.name=Signing Alg
policyset.caSubCertSet.9.default.params.signingAlg=-
policyset.caSubCertSet.9.constraint.class_id=noConstraintImpl
policyset.caSubCertSet.9.constraint.name=No Constraint
policyset.caSubCertSet.9.default.class_id=crlDistributionPointsExtDefaultImpl
policyset.caSubCertSet.9.default.name=CRL Distribution Points Extension Default
policyset.caSubCertSet.9.default.params.crlDistPointsCritical=false
policyset.caSubCertSet.9.default.params.crlDistPointsEnable_0=true
policyset.caSubCertSet.9.default.params.crlDistPointsIssuerName_0=CN=Certificate Authority,o=ipaca
policyset.caSubCertSet.9.default.params.crlDistPointsIssuerType_0=DirectoryName
policyset.caSubCertSet.9.default.params.crlDistPointsNum=1
policyset.caSubCertSet.9.default.params.crlDistPointsPointName_0=http://<your IPA's CRL revocation list>
policyset.caSubCertSet.9.default.params.crlDistPointsPointType_0=URIName
policyset.caSubCertSet.9.default.params.crlDistPointsReasons_0=
policyset.caSubCertSet.10.constraint.class_id=noConstraintImpl
policyset.caSubCertSet.10.constraint.name=No Constraint
policyset.caSubCertSet.10.default.class_id=authInfoAccessExtDefaultImpl
policyset.caSubCertSet.10.default.name=AIA Extension Default
policyset.caSubCertSet.10.default.params.authInfoAccessADEnable_0=true
policyset.caSubCertSet.10.default.params.authInfoAccessADLocationType_0=URIName
policyset.caSubCertSet.10.default.params.authInfoAccessADLocation_0=
policyset.caSubCertSet.10.default.params.authInfoAccessADMethod_0=1.3.6.1.5.5.7.48.1
policyset.caSubCertSet.10.default.params.authInfoAccessCritical=false
policyset.caSubCertSet.10.default.params.authInfoAccessNumADs=1
profileId=caSubCertAuth2

Here, we need to update this line with the correct address of your FreeIPA:

Shell
 
policyset.caSubCertSet.9.default.params.crlDistPointsPointName_0=http://ldap.example.com/ipa/crl/MasterCRL.bin


The line:

Shell
 
policyset.caSubCertSet.1.constraint.params.pattern=.*CN=.+


imposes the broadest possible rule on CS. In this line, it means that we need to have a “CN=” string somewhere.

These parameters (path len =0) mean that our intermediate CA can issue certificates, but these certificates must be end-entity certificates, which is exactly what we want to achieve here.


Shell
 
policyset.caSubCertSet.5.constraint.params.basicConstraintsIsCA=true
policyset.caSubCertSet.5.constraint.params.basicConstraintsMinPathLen=0
policyset.caSubCertSet.5.constraint.params.basicConstraintsMaxPathLen=0
policyset.caSubCertSet.5.default.params.basicConstraintsIsCA=true
policyset.caSubCertSet.5.default.params.basicConstraintsPathLen=0


After updating, save this template to the file caSubCertAuth2.cfg.

And we can import it to the IPA (don’t forget to log in to the IPA via kinit your_username):

Shell
 
ipa certprofile-import caSubCertAuth2 --store=true --file=caSubCACert2.cfg


Now we need to create a Certificate ACL. For that, we need to log in to the FreeIPA UI, navigate to the authentication, certificates, and CA ACLs:


This can also be done via IPA CLI, with commands “ipa caacl-add” and “ipa caacl-add-profile.”

For signing CSR, we need to have the principal (HashiCorp Vault) already registered in FreeIPA. It can be done via the host-add command:

Shell
 
ipa host-add --force vault.example.com


For now, we are ready to sign the CSR from the Vault.

How to Configure an Intermediate CA in the Vault

The first step is mounting a PKI backend:

Shell
 
$ vault secrets enable -path=pki_int pki


Next, we need to set the default maximum time-to-live (TTL) for the issued certificates:

Shell
 
$ vault secrets tune -max-lease-ttl=43800h pki_int


TTL should be equal to or less than the root certificate authority.

Now we can generate an intermediate certificate signing request (CSR):

Shell
 
$ vault write -format=json pki_int/intermediate/generate/internal common_name="example.com Intermediate Authority" ttl=43800h | jq -r '.data.csr' > request.csr


We need to copy this CSR to the IPA host and sign it with profile-id created in the previous steps:

Shell
 
$ ipa cert-request ./request.csr --principal host/[email protected] --profile-id caSubCertAuth2


This command will issue the certificate.

Now, configure the intermediate certificate authority's signing certificate to use the root-signed certificate:

Shell
 
$ vault write pki_int/intermediate/set-signed certificate=@signed_certificate.pem


Configure CRL location:

Shell
 
$ vault write pki_int/config/urls issuing_certificates="$VAULT_ADDR/v1/pki/ca" crl_distribution_points="$VAULT_ADDR/v1/pki/crl"


And last but not least, we need to configure a role: 

Shell
 
$ vault write pki_int/roles/example.com \
    allowed_domains=example.com \
    allow_subdomains=true max_ttl=72h


Check out the PKI HTTP API reference for other attributes that you can configure.    

After that, we can request certificates from the Vault like this:

Shell
 
$ vault write pki_int/issue/example.com common_name=consul02.dev.any.infra.example.com


In the output, we will have:

Shell
 
Key             	Value
---             	-----
ca_chain        	[-----BEGIN CERTIFICATE-----
<truncated>
-----END CERTIFICATE-----]
certificate     	-----BEGIN CERTIFICATE-----
<truncated>
-----END CERTIFICATE-----
expiration          2033992954
issuing_ca      	-----BEGIN CERTIFICATE-----
<truncated>
-----END CERTIFICATE-----
private_key     	-----BEGIN RSA PRIVATE KEY-----
<truncated>
-----END RSA PRIVATE KEY-----
private_key_type	rsa
serial_number       38:0b:df:ec:be:ad:ca:5f:4e:a4:cd:0a:19:c5:12:29:20:f1:35:a


To create a proper certificate chain, you need to create a file containing the certificate and the certificate chain, let's call it consul02.dev.any.infra.example.com.crt.

The first one — you need to copy the issued certificate itself from the certificate field, and the second one should be certificates from the ca_chain.

After that, your certificate will be marked as valid.

Conclusion

In this article, we explored how to integrate HashiCorp Vault with FreeIPA for certificate issuance. Establishing trust between the two systems allows us to maintain a unified trust framework across our infrastructure. By using Vault as an intermediate CA, we not only simplify certificate management but also gain integration capabilities with a wide range of applications — functionality that FreeIPA alone does not natively support.

Certificate authority FreeIPA security

Opinions expressed by DZone contributors are their own.

Related

  • Strengthening Cybersecurity: The Role of Digital Certificates and PKI in Authentication
  • Minimus Expands Enterprise Security Platform with General Availability of Advanced Supply Chain Controls
  • Building Threat Intelligence Pipelines Using Python, APIs, and Elasticsearch
  • Identity in Action

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook