Skip to main content

Reload SSL certificates from HashiCorp Vault for Spring Boot

Update applications with new certificates from Vault’s PKI secrets engine using SSL hot reload in Spring Boot.

Spring Boot includes an embedded web server with the ability to configure SSL certificates to secure connections. In this post, learn how to use Vault Agent to generate certificates for a file and configure SSL hot reload in Spring Boot to automatically update web servers with new certificates. This approach automates the handling of expiring certificates without restarting the application while keeping the application code agnostic of Vault (i.e. you don’t need to add code to your application to connect to Vault). Review a complete example of using SSL hot reload with Vault on GitHub.

»Set up PKI secrets engine

Vault has the ability to generate dynamic X.509 certificates using the PKI secrets engine. You can use Vault as a root certificate authority (CA) or bring your own offline root CA. Refer to your Vault operations team to determine how to best enable the PKI secrets engine.

If you have the ability to configure secrets engines on your Vault server, set up the PKI secrets engine with a root CA. The example configuration here sets up a root certificate that expires in one day and configures a Vault role named payments-app for the web server using the certificate.

vault secrets enable pki
vault secrets tune -max-lease-ttl="24h" pki
vault write -field=certificate pki/root/generate/internal \
    common_name="${COMMON_NAME}" \
    issuer_name="root-2024" \
    ttl="12h" > certs/root_2024_ca.crt
 
vault write pki/config/urls \
    issuing_certificates="${VAULT_ADDR}/v1/pki/ca" \
    crl_distribution_points="${VAULT_ADDR}/v1/pki/crl"
 
vault write pki/roles/payments-app allow_any_name=true

Generate an intermediate CA for the web server that uses the certificate. The intermediate CA will issue certificates that expire every six hours.

vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=12h pki_int
vault pki issue \
     --issuer_name=example-intermediate \
     /pki/issuer/$(vault read -field=default pki/config/issuers) \
     /pki_int/ \
     common_name="${COMMON_NAME} Intermediate Authority" \
     key_type="rsa" \
     key_bits="4096" \
     max_depth_len=1 \
     ttl="12h"
 
vault write pki_int/roles/payments-app \
    issuer_ref="$(vault read -field=default pki_int/config/issuers)" \
    allow_any_name=true \
    max_ttl="6h"

Before configuring the application, make sure its Vault role has a policy to use the PKI secrets engine. For example, the payments-app role should have access to issue the intermediate certificates at pki_int/issue/payments-app using the update capability.

path "pki_int/issue/payments-app" {
 capabilities = ["update"]
}

For additional configurations of the PKI secrets engine, refer to our developer documentation.

»Deploy Vault Agent

Spring Boot’s SSL hot reload capability can reference SSL certificates from a file. How do you get the certificates from Vault and write them to a file for the application to reference? Vault Agent automatically authenticates to Vault and writes secrets to a file based on a template. Run Vault Agent as a separate process on the same machine as your application, and it will automatically handle authentication and secret retrieval.

The template for certificates generated by Vault references the API path to issue the certificate and any other attributes required by the PKI secrets engine configuration, such as common name. The Vault Agent template writes the certificate’s CA, private key, and public key to separate files. Vault Agent needs the template file in order to check for differences in the certificate and get a new one when it expires. This example requests a certificate that expires every five minutes.

{{- with pkiCert "pki_int/issue/payments-app" "common_name=payments.${COMMON_NAME}" "alt_names=localhost" "ttl=5m" -}}
{{ .Cert }}{{ .CA }}{{ .Key }}
{{ .Key | trimSpace | writeToFile "/vault-agent/config/certs/payments.key" "" "" "0400" }}
{{ .CA | trimSpace | writeToFile "/vault-agent/config/certs/ca.pem" "" "" "0644" }}
{{ .Cert | trimSpace | writeToFile "/vault-agent/config/certs/payments.crt" "" "" "0644" }}
{{- end -}}

Reference the template file in Vault Agent’s configuration and set a destination to write out all certificate information for Vault Agent to track. The example below automatically authenticates to Vault using the approle auth method. The auto_auth configuration will change depending on your auth method and where you run Vault Agent and your application.

pid_file = "/vault-agent/pidfile"
 
// Define Vault Agent's connection to Vault server.
// This example uses docker-compose with the Vault server at http://vault:8200.
vault {
 address = "http://vault:8200"
}
 
auto_auth {
 // This example uses AppRole authentication. When you set up Vault,
 // the scripts wrote the role-id and secret-id to a file. You can think of
 // AppRole authentication method as a username/password combination
 // for automation.
 method {
   type = "approle"
   config = {
     role_id_file_path                   = "/vault-agent/role-id"
     secret_id_file_path                 = "/vault-agent/secret-id"
     remove_secret_id_file_after_reading = false
   }
 }
}
 
template {
 source       = "/vault/templates/cert.tpl"
 destination  = "/vault-agent/config/all-certs"
}

When you run Vault Agent with vault agent -config=/vault/config.hcl, you can find the CA, private key, and public key written to /vault-agent/config/certs.

$ ls vault-agent/config/certs 
ca.pem       payments.crt payments.key

The Spring Boot application references the certificates from the vault-agent/config/certs directory.

»Configure application

The application needs to reference the certificates from a file using Spring’s application properties. Configure application.properties with spring.ssl.bundle.pem, which references the directory with the certificates created by Vault Agent. The properties should set reload-on-update to true so the application hot reloads when Vault Agent updates the certificates.

spring.ssl.bundle.pem.demo.reload-on-update=true
spring.ssl.bundle.pem.demo.keystore.certificate=/vault-agent/config/certs/payments.crt
spring.ssl.bundle.pem.demo.keystore.private-key=/vault-agent/config/certs/payments.key
 
server.ssl.bundle=demo

Start the application. The logs show that the application uses the certificates from the /vault-agent/config/certs directory.

2024-08-26T15:27:07.187Z  INFO 8 --- [payments-app] [main] 
c.h.paymentsapp.PaymentsApplication  : 
Starting PaymentsApplication v0.0.1-SNAPSHOT 
using Java 22.0.2 with PID 8 (/app/app.jar started by root in /)
 
...
 
2024-08-26T15:27:10.519Z  INFO 8 --- [payments-app] [main] 
o.a.t.util.net.NioEndpoint.certificate   : 
Connector [https-jsse-nio-8081], TLS virtual host [_default_], certificate type [UNDEFINED] 
configured from keystore [/root/.keystore] using alias [tomcat] with trust store [null]
2024-08-26T15:27:10.531Z  INFO 8 --- [payments-app] [main] o.s.b.w.embedded.tomcat.TomcatWebServer  : 
Tomcat started on port 8081 (https) with context path ''
 
...
 
2024-08-26T15:27:10.570Z  INFO 8 --- [payments-app] [main] 
c.h.paymentsapp.PaymentsApplication  : 
Started PaymentsApplication in 4.002 seconds (process running for 4.545)

Access the application over HTTPS. The curl command needs to reference the files containing the certificate authority, certificate, and private key written by Vault Agent in order to verify the connection to the application.

$ curl --cacert vault-agent/config/certs/ca.pem --cert vault-agent/config/certs/payments.crt --key vault-agent/config/certs/payments.key https://localhost:8081/payments
 
[{"billing_address":"8 Eastern Himalayas Drive","created_at":"2024-08-26T00:00:00Z","id":"2310d6be-0e80-11ed-861d-0242ac120002","name":"Red Panda","status":"paid"}]

Use openssl to verify the certificate. The certificate includes the expiration date five minutes after it was generated.

$ openssl s_client -showcerts -connect localhost:8081 </dev/null
 
Connecting to ::1
CONNECTED(00000003)
 
## omitted
 
---
Certificate chain
 0 s:CN=payments.${COMMON_NAME}
   i:CN=${COMMON_NAME} Intermediate Authority
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Aug 26 15:43:20 2024 GMT; NotAfter: Aug 26 15:48:50 2024 GMT
-----BEGIN CERTIFICATE-----
MIIEmDCCAoCgAwIBAgIUEpiJgaQX8eNokLqN5I/cwxRV1PkwDQYJKoZIhvcNAQEL
 
## omitted
 
---
Server certificate
subject=CN=payments.${COMMON_NAME}
issuer=CN=${COMMON_NAME} Intermediate Authority
---
 
## omitted

When the certificate nears expiration, Vault Agent requests a new certificate and writes it to the files. The Spring application automatically reloads itself with the new certificate.

2024-08-26T15:47:03.532Z  INFO 8 --- [payments-app] [-bundle-watcher] 
o.a.t.util.net.NioEndpoint.certificate   : Connector [https-jsse-nio-8081], 
TLS virtual host [_default_], certificate type [UNDEFINED] 
configured from keystore [/root/.keystore] using alias [tomcat] 
with trust store [null]

When you review the certificate information, you will find a new certificate with an updated expiration.

$ openssl s_client -showcerts -connect localhost:8081 </dev/null
 
Connecting to ::1
CONNECTED(00000003)
 
## omitted
 
---
Certificate chain
 0 s:CN=payments.${COMMON_NAME}
   i:CN=${COMMON_NAME} Intermediate Authority
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Aug 26 15:47:52 2024 GMT; NotAfter: Aug 26 15:53:22 2024 GMT
-----BEGIN CERTIFICATE-----
MIIElzCCAn+gAwIBAgITa0OvvyCQ2qpKa7RCV24wI9dVLzANBgkqhkiG9w0BAQsF
 
## omitted
 
---
Server certificate
subject=CN=payments.${COMMON_NAME}
issuer=CN=${COMMON_NAME} Intermediate Authority
 
## omitted

The application now automatically loads a new certificate to its web server without the need to manually restart the application or introduce application downtime.

»Learn more

By combining SSL hot reload in Spring applications with certificates generated by Vault Agent, your application automatically handles certificate expiration without introducing downtime. This approach accounts for the dynamic behavior of certificates and avoids the need for manual configuration and restart. Rather than directly connecting the application to the Vault API, Vault Agent decouples your application’s dependency on Vault by handling the authentication and retrieval of certificates. The application can read the certificates from a certificate file created by Vault Agent. This helps minimize the overall application refactoring effort.

Find a more complete example of hot reloading Spring applications for secrets and certificates generated by Vault on GitHub. Additional configuration for SSL hot reloading in Spring Boot can be found in the Spring documentation. For more on how to use HashiCorp Vault with Spring applications, check out our tutorials on reloading secrets with the Spring Cloud Vault library or encrypting Spring application data with transit secrets engine.


Sign up for the latest HashiCorp news

By submitting this form, you acknowledge and agree that HashiCorp will process your personal information in accordance with the Privacy Policy.