visit
max_ttl
and redeploy/restart the application often enough(Mongooses cannot keep secrets - Image by from )
If you have a Spring Boot application or microservice, sooner or later (better sooner) you have to think about how to handle the secrets in your code in a secure manner. Fortunately, these days there are already really good tools available. The most famous and mature one is . As a Kotlin or Java developer building a Spring Boot application you are in a lucky situation, because it is really easy to get your Hashicorp Vault integration up and running by using . Vault’s concept of is probably THE killer feature and allows you for example to easily get unique, secure and short-lived database credentials which follow the .There are quite good examples how to get started with Spring, Vault and e.g. , , and . These examples work with surprisingly little effort. But as usually the also applies here. There a quite some challenges. For instance, , like it is done in most examples, is easy and fast but you should never do that in production. And this is just the most obvious operational task to tackle. Here is a (not complete) list contains of things to consider:This is a show stopper, because it will leave the Spring application in a broken state where no database communication is possible anymore. So, what can you do about this?I will show you 4 solutions on how to make the setup work. The first 3 are more generically applicable with some prerequisites to apply. The 4th solution is only working for a more specific but also not too uncommon setup and provides a smoother experience. I will describe its details in the next blog post.Let’s check our options.Spring Cloud Vault does not support getting new credentials and configuring your
with them when the maximum lease time has been reached. That is, ifDataSource
of the Database role in Vault is set tomax_ttl
that means that 24 hours after your application has started it can no longer authenticate with the database.24h
max_ttl
for the dynamic database credentialThe Spring Cloud Vault documentation talks about the
max_ttl
of the database role in Vault, which is the maximum lease time. This duration is configurable. So, if you regularly deploy or restart your application, you can just use a long enough max_ttl
.Let’s consider you have a two-week sprint and it is guaranteed that after each sprint your application is redeployed. Then you could just configure the
max_ttl
to be longer than 2 weeks, for instance 16 days. A good example how to setup Vault to generate dynamic credentials for PostgreSQL can be found on the . You just have to adapt the max_ttl
value to 16 days (384 hours) :vault write database/roles/readonly db_name=postgresql \
[email protected] \
default_ttl=1h max_ttl=384h
The system max TTL, which is 32 days but can be changed in Vault’s configuration file.—
(Time is running out - Image by from )
…it is also possible to increase it via the Vault configuration. The mentions that the
max_lease_ttl
parameter, which defaults to "768h"
can be used for that.An example vault config could look like:max_lease_ttl = "3000h" # (1)
storage "file" {
path = "/var/vault/vault-storage"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = 1 # (2)
}
(1) The system max TTL is set to 3000h here, the rest of the config is just for demonstration purpose
(2) You should never do that in production
Before you now just change the
max_lease_ttl
for all your Vault instances to a really big value, you should wait a minute. Hashicorp of course has a good reason to limit the maximum TTL and to not use a too big value by default: The longer a dynamic secret lives, the less dynamic it is in the end. So if you choose a too big time-to-live value, the risk of having really long living secrets and with that the risk of is increasing. That is the reason why I would not recommend changing this value if you can use one of the other options.I expect that you’ve already setup Spring and Vault to create dynamic database secrets (how to do that, see for example or ). Then you can autowire the
SecretLeaseContainer
, the database role which is configured as the property spring.cloud.vault.database.role
and the ConfigurableApplicationContext
to allow closing the ApplicationContext which eventually shuts down the Spring application:@Configuration
class VaultConfig(
private val leaseContainer: SecretLeaseContainer,
@Value("\${spring.cloud.vault.database.role}")
private val databaseRole: String,
private val applicationContext: ConfigurableApplicationContext
) {
In a
@PostConstruct
method you can then add the additional LeaseListenener
which does the shutdown:@PostConstruct
private fun configureShutdownWhenLeaseExpires() {
val vaultCredsPath = "database/creds/$databaseRole" // (1)
leaseContainer.addLeaseListener { event -> // (2)
if (event.path == vaultCredsPath) { // (3)
log.info { "Lease change for DB: ($event) : (${event.lease})" }
if (event.isLeaseExpired && event.mode == RENEW) { // (3)
log.error { "Database lease expired. Shutting down." }
applicationContext.close() // (4)
}
}
}
}
databaseRole
LeaseListenener
is a SAM interface, so just provide a lambda (see )event.path
, event.isLeaseExpired
and event.mode
are extension methods (see next code snippet)private val SecretLeaseEvent.path get() = source.path
private val SecretLeaseEvent.isLeaseExpired get() = this is SecretLeaseExpiredEvent
private val SecretLeaseEvent.mode get() = source.mode
(Cliffhanger … to be continued - Image by from )
There is also the possibility to implement the database credential renewal in the application itself. Spring itself does not provide a generic way to do this.Originally published at on January 28, 2020