Vault’s secret rotation isn’t just about cycling credentials; it’s a dynamic system that actively revokes and regenerates secrets on a schedule, ensuring that even if a secret is compromised, its lifespan is intentionally limited.
Let’s see this in action. Imagine you have a database that Vault is managing credentials for.
resource "vault_database_secret_backend" "postgres" {
path = "database/config/postgres"
backend_type = "postgresql"
connection_urls = ["postgres://username:password@host:port/dbname"]
username = "vault_admin"
password = "supersecretpassword"
}
resource "vault_mount" "postgres_secrets" {
path = "database/postgres"
type = "database"
description = "PostgreSQL database secrets"
}
resource "vault_database_secret_backend_role" "app_role" {
name = "app"
backend = vault_database_secret_backend.postgres.path
db_name = vault_database_secret_backend.postgres.path # Corresponds to the database connection config
creation_statements = [
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
"GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";"
]
renewal_username = "vault_admin"
renewal_password = "supersecretpassword"
default_ttl_seconds = 3600 # 1 hour
max_ttl_seconds = 7200 # 2 hours
}
In this configuration, Vault is set up to manage PostgreSQL credentials. The vault_database_secret_backend defines how Vault connects to the PostgreSQL instance itself (using a superuser account). The vault_mount makes this backend available under a specific path in Vault. The vault_database_secret_backend_role named "app_role" defines how Vault should generate credentials for applications. It specifies the SQL statements to create a new user ({{name}} and {{password}} are placeholders Vault fills in) and grants it specific permissions. Crucially, default_ttl_seconds and max_ttl_seconds dictate how long these generated credentials will be valid before Vault attempts to revoke them and issue new ones.
When an application requests a secret from secret/database/postgres/app, Vault doesn’t just hand over static credentials. It executes the creation_statements on the target database, creates a new role/user, and returns the generated username and password to the application. This new role has a VALID UNTIL timestamp set by Vault, corresponding to the default_ttl_seconds. Before this timestamp expires, Vault’s internal scheduler will attempt to renew the credential. If renewal fails (e.g., the database is unreachable, or the renewal_username/renewal_password are invalid), Vault will attempt to revoke the credential. If renewal succeeds, Vault updates the VALID UNTIL time for the existing role. If the application requests a new secret after the max_ttl_seconds has passed since the initial generation, Vault will revoke the old credential and create a brand new one, effectively cycling the credentials.
The most surprising part of Vault’s secret rotation, especially for database backends, is how it handles the lifecycle of the generated role itself. It’s not just about issuing a new password; Vault actively manages the database user’s existence. When Vault revokes a credential, it doesn’t just invalidate a token or a key. For database backends, this means Vault will execute a DROP ROLE or equivalent command to remove the database user it previously created. This ensures that no lingering database accounts exist beyond their intended operational lifespan, even if the application consuming them has long since stopped requesting new credentials.
The next step is understanding how to integrate this rotation into your CI/CD pipelines for automated deployments.