Virtual Event
Join us for the next HashiConf Digital October 12-15, 2020 Register for Free

Secrets Management

Dynamic Secrets: Database Secrets Engine

Vault can generate secrets on-demand for some systems. For example, when an app needs to access an Amazon S3 bucket, it asks Vault for AWS credentials. Vault will generate an AWS credential granting permissions to access the S3 bucket. In addition, Vault will automatically revoke this credential after the TTL is expired.


Data protection is a top priority, and database credential rotation is a critical part of any data protection initiative. Each role has a different set of permissions granted to access the database. When a system is attacked by hackers, continuous credential rotation becomes necessary and needs to be automated.


Applications ask Vault for database credentials rather than setting them as environment variables. The administrator specifies the TTL of the database credentials to enforce its validity so that they are automatically revoked when they are no longer used.

Dynamic Secret Workflow

Each app instance can get unique credentials that they don't have to share. By making those credentials short-lived, you reduce the chance that they might be compromised. If an app was compromised, the credentials used by the app can be revoked rather than changing more global sets of credentials.


The end-to-end scenario described in this guide involves two personas:

  • admin with privileged permissions to configure secrets engines
  • apps read the secrets from Vault


To perform the tasks described in this guide, you need to have:

»Policy requirements

To perform all tasks demonstrated in this guide, your policy must include the following permissions:

# Mount secrets engines
path "sys/mounts/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]

# Configure the database secrets engine and create roles
path "database/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]

# Write ACL policies
path "sys/policies/acl/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]

# Manage tokens for verification
path "auth/token/create" {
  capabilities = [ "create", "read", "update", "delete", "list", "sudo" ]

If you are not familiar with policies, complete the policies guide.

»Scenario Introduction

In this guide, you are going to configure the PostgreSQL secrets engine, and create a read-only database role. The Vault-generated PostgreSQL credentials will only have read permission.

  1. Enable the database secrets engine
  2. Configure PostgreSQL secrets engine
  3. Create a role
  4. Request PostgreSQL credentials
  5. Validation

Step 1 through 3 need to be performed by an admin user. Step 4 describes the commands that an app runs to get a database credentials from Vault.

»Step 1: Enable the database secrets engine

(Persona: admin)

First step is to enable the database secrets engine at a desired path.

Execute the following command to enable the database secrets engine at database/ path.

$ vault secrets enable database

If you want to enable the secrets engine at a different path, use the -path parameter to specify your desired path. Otherwise, the secrets engine gets enabled at a path named after its type. Since the type is database in this example, its path becomes database in absence of -path parameter.

»Step 2: Configure PostgreSQL secrets engine

(Persona: admin)

The PostgreSQL secrets engine needs to be configured with valid credentials. It is very common to give Vault the superuser credentials and let Vault manage the auditing and lifecycle credentials; it's much better than having one person manage the credentials.

For the purpose of this tutorial, let's run PostgreSQL Docker image in a container.

Execute the following command to start a postgres instance which listens to port 5432, and the superuser (root) password is set to rootpassword.

$ docker run --name postgres -e POSTGRES_USER=root \
         -e POSTGRES_PASSWORD=rootpassword \
         -d -p 5432:5432 postgres

Verify that the postgres container is running.

$ docker ps

CONTAINER ID        IMAGE            ...         PORTS                    NAMES
befcf913da91        postgres         ...>5432/tcp   postgres

The following command configures the database secrets engine using postgresql-database-plugin. The allowed role is readonly which you will create in Step 3.


$ vault write database/config/postgresql \
    plugin_name=postgresql-database-plugin \
    allowed_roles=readonly \

»Step 3: Create a role

(Persona: admin)

In Step 2, you configured the PostgreSQL secrets engine by passing readonly role as an allowed member. The next step is to define the readonly role. A role is a logical name that maps to a policy used to generate credentials.

Example: readonly.sql

CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';

The values within the {{<value>}} will be filled in by Vault. Notice that VALID_UNTIL clause. This tells PostgreSQL to revoke the credentials even if Vault is offline or unable to communicate with it.


$ vault write database/roles/readonly db_name=postgresql \
        creation_statements=@readonly.sql \
        default_ttl=1h max_ttl=24h

The above command creates a role named readonly with default TTL of 1 hour, and max TTL of the credential is set to 24 hours. The readonly.sql statement is passed as the role creation statement.

»Step 4: Request PostgreSQL credentials

(Persona: apps)

Now, you are switching to apps persona. To get a new set of PostgreSQL credentials (leases), the client app needs to be able to read from the readonly role endpoint. Therefore the app's token must have a policy granting the read permission.


# Get credentials from the database secrets engine
path "database/creds/readonly" {
  capabilities = [ "read" ]

First create an apps policy, and generate a token so that you can authenticate as an apps persona.


Create apps policy.

$ vault policy write apps apps-policy.hcl
Policy 'apps' written.

Create a new token with the apps policy attached.

$ vault token create -policy="apps"

Key                  Value
---                  -----
token                s.NN5Izfj9ok3VuZiaP9N9QJ1V
token_accessor       l1QxSKs80HfrzR1gfy5zm7ay
token_duration       768h
token_renewable      true
token_policies       ["apps" "default"]
identity_policies    []
policies             ["apps" "default"]

Use the returned token to perform the remaining.

Invoke the vault command with apps token you just generated.

$ VAULT_TOKEN=s.NN5Izfj9ok3VuZiaP9N9QJ1V vault read database/creds/readonly

Key                Value
---                -----
lease_id           database/creds/readonly/fyF5xDomnKeCHNZNQgStwBKD
lease_duration     1h
lease_renewable    true
password           A1a-ckirtymYaXACpIHn
username           v-token-readonly-6iRIcGv8tLpu816oblPY-1556567086

Re-run the command and notice that Vault returns a different set of credentials each time. This means that each app instance can acquire a unique set of DB credentials.


  1. Generate a new set of credentials.

    $ vault read database/creds/readonly
    Key                Value
    ---                -----
    lease_id           database/creds/readonly/IQKUMCTg3M5QTRZ0abmLKjTX
    lease_duration     1h
    lease_renewable    true
    password           A1a-T7Ezuy261IBew8H9
    username           v-token-readonly-47vOtpF7pZq79Xajx7yq-1556567237

    The generated username is v-token-readonly-47vOtpF7pZq79Xajx7yq-1556567237.

  2. Connect to the postgres Docker container.

    $ docker exec -it postgres bash
  3. Start the PostgreSQL client CLI.

    /# psql -U root
  4. Execute the \du command.

    root=# \du
                                                        List of roles
                      Role name                     |                         Attributes                         | Member of
    postgres                                         | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
    v-token-readonly-47vOtpF7pZq79Xajx7yq-1556567237 | Password valid until 2018-01-11 00:37:14+00                | {}

    The \du command lists all users. You should be able to verify that the username generated by Vault exists.

  5. Quit the Postgres client session with the \q command. Exit the Docker container shell with the exit command

    »Manage the leases

    The lease is valid for the given duration (TTL). Once the lease is expired, Vault can automatically revoke the data, and the consumer of the secret can no longer be certain that it is valid.

  1. To list the existing lease IDs, execute the following command.

    $ vault list sys/leases/lookup/database/creds/readonly
  2. If needed, you can renew the lease for the database credential by passing its lease_id.

    $ vault lease renew database/creds/readonly/IQKUMCTg3M5QTRZ0abmLKjTX
    Key                Value
    ---                -----
    lease_id           database/creds/readonly/IQKUMCTg3M5QTRZ0abmLKjTX
    lease_duration     1h
    lease_renewable    true
  3. If the credentials are no longer used, you can revoke the lease without waiting for its expiration.

    $ vault lease revoke database/creds/readonly/IQKUMCTg3M5QTRZ0abmLKjTX
  4. If you run the command with -prefix flag, it revokes all secrets under database/creds/readonly.

    $ vault lease revoke -prefix=database/creds/readonly

»Next Step

There are some tools available to help integrate your applications with Vault's database secrets engine. Using those tools, the existing applications may require minimum to no code change to work with Vault.

Refer to the following guides:

»Help and Reference