Understand static and dynamic secrets
Vault will store any secret in a secure manner. The secrets may be SSL certificates and keys for your organization's domain, credentials to connect to a corporate database server, etc. Storing such sensitive information in plaintext is not desirable or secure so Vault secure stores and provides retrieval mechanisms.
Unless the someone in the organization manually changes the secret, they are not updated. The kv secrets engine seen in the CLI, HTTP API and UI introductory tutorials is an example of these static secrets. The KV secrets engine is the most commonly used engine for static secrets.
In addition to offering static secrets through the kv secrets engine, Vault can generate dynamic secrets. Dynamic secrets do not exist until read, so the risk of being stolen is greatly reduced. Because Vault has built-in revocation mechanisms, Vault revokes dynamic secrets after use thereby minimizing the amount of time the secret existed.
Scenario
HashiCups has a requirement for development teams to securely connect their web services and applications when accessing a PostgreSQL database. If each team has to manually manage secrets for web services and applications, there are two potential problems. Ether services would share secrets or it will be a challenge managing secrets. Sharing secrets is always a risk, as a compromised secret has access to multiple applications and services. Managing many secrets is a challenge of it's own. Instead, they can use the Vault dynamic secrets to create secrets when accessed and to invalidate the secrets after use.
It is Oliver's job to set up dynamic secrets for the development teams. They will create a database secrets engine, connect it to a PostgreSQL instance, configure it and test Vault's dynamic secret generation.
Prerequisites
To complete this tutorial, you need the following:
- Vault binary installed in your system PATH.
- Docker installed.
- This scenario requires at least 4GB of memory allocated to Docker.
Set up the lab
Open a terminal and start a Vault dev server with the literal string
root
as the root token value, and enable TLS.$ vault server -dev -dev-root-token-id root -dev-tls
The dev server listens on the loopback interface at 127.0.0.1 on TCP port 8200 with TLS enabled. At runtime, the dev server also automatically unseals, and prints the unseal key and initial root token values to the standard output.
Root tokens
The dev mode server starts with an initial root token value set.
Root token use should be guarded in production environments because it provides full access to the Vault server.
You can supply the root token value to start Vault in dev mode for convenience and to keep the steps here focused on the learning goals of this tutorial.
In a new terminal, export the
VAULT_ADDR
andVAULT_CACERT
environment variables using the commands suggested in your Vault dev server output.Copy each command (without the
$
) from the server output, and paste it into the new terminal session.Example:
export VAULT_ADDR='https://127.0.0.1:8200'
Example:
$ export VAULT_CACERT='/var/folders/qr/zgztx0sj6n1dxy86sl36ntnw0000gn/T/vault-tls3037226588/vault-ca.pem'
Remember to use your dev server's values, not the examples shown here.
Export an environment variable for the
vault
CLI to authenticate with the Vault server.$ export VAULT_TOKEN=root
The Vault server is ready.
Secrets engine commands
There are some basic commands that are useful to know when dealing with secrets engines.
Vault provides contexual help for the
secret
subcommand.$ vault secret --helpUsage: vault <command> [args] Common commands: read Read data and retrieves secrets write Write data, configuration, and secrets delete Delete secrets and configuration list List data or secrets login Authenticate locally agent Start a Vault agent server Start a Vault server status Print seal and HA status unwrap Unwrap a wrapped secret...
List the secrets engines enabled by default.
$ vault secrets listPath Type Accessor Description---- ---- -------- -----------cubbyhole/ cubbyhole cubbyhole_8cfa3d98 per-token private secret storageidentity/ identity identity_47a3d75e identity storesecret/ kv kv_bdb0b0c1 key/value secret storagesys/ system system_04e9fd4e system endpoints used for control, policy and debugging
To create a new secrets engine use the
enable
command.$ vault secrets enable -path=kvv2 kv-v2Success! Enabled the kv-v2 secrets engine at: kvv2/
The particulars of what to do with each secrets engine will vary, and refer to the documentation for that secrets engine.
Disabling a secrets engine is similar.
$ vault secrets disable kv-v2Success! Disabled the secrets engine (if it existed) at: kv-v2/
Dynamic secrets
The database secrets engine supports a variety of databases through a plugin interface. To use a PostgreSQL database with the secrets engine requires further configuration with the postgresql-database-plugin plugin and connection information. Vault will connect to a PostgreSQL database and then use the HTTP API to create dynamic credentials.
In this section create a database secrets engine, configure it and review the
path-help
command.
Start PostgreSQL
(Persona: developer)
The tutorial requires a PostgreSQL database. Docker provides a PostgreSQL server image that satisfies this requirement.
Back in the first terminal, pull a PostgreSQL server image with
docker
.$ docker pull postgres:latest
Create a PostgreSQL database with a root user named
root
with the passwordrootpassword
.$ docker run \ --detach \ --name learn-postgres \ -e POSTGRES_USER=root \ -e POSTGRES_PASSWORD=rootpassword \ -p 5432:5432 \ --rm \ postgres
Verify that the PostgreSQL container is running.
$ docker ps -f name=learn-postgres --format "table {{.Names}}\t{{.Status}}"NAMES STATUSlearn-postgres Up 5 seconds
The credentials generated by the Vault role requires a role named
ro
that has the capability to read all tables.Create a role named
ro
.$ docker exec -i \ learn-postgres \ psql -U root -c "CREATE ROLE \"ro\" NOINHERIT;"
Grant the ability to read all tables to the role named
ro
.$ docker exec -i \ learn-postgres \ psql -U root -c "GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"ro\";"
The database and the role with the appropriate permissions is available.
Configure Vault
(Persona: operations)
Create a database secrets engine.
$ vault secrets enable database
Set an environment variable for the PostreSQL address.
$ export POSTGRES_URL="127.0.0.1:5432"
Configure the database secrets engine with the connection credentials for the PostgreSQL database.
$ vault write database/config/postgresql \ plugin_name=postgresql-database-plugin \ connection_url="postgresql://{{username}}:{{password}}@$POSTGRES_URL/postgres?sslmode=disable" \ allowed_roles=readonly \ username="root" \ password="rootpassword"
Example output:
Success! Data written to: database/config/postgresql
Using the path after the
secret write
, try thepath-help
command.$ vault path-help database/config/postgresqlRequest: config/postgresqlMatching Route: ^config/(?P<name>\w(([\w-.]+)?\w)?)$ Configure connection details to a database plugin.
Run a
vault read
command with the config path and compare the settings.$ vault read database/config/postgresqlKey Value--- -----allowed_roles [readonly]connection_details map[connection_url:postgresql://{{username}}:{{password}}@127.0.0.1:5432/postgres?sslmode=disable username:root]password_policy n/aplugin_name postgresql-database-pluginplugin_version n/aroot_credentials_rotate_statements []
Create a role
In the dynamic secrets section, you configured the PostgreSQL secrets engine with the allowed role named readonly
. A role is a logical name within Vault that maps to database credentials. The SQL statements express the credentials and assigned to the Vault role.
Note
Important: when you define the role in a production deployment, you must create user creation_statements, revocation_statements, renew_statements, and rotation_statements which are valid for the database you've configured. If you do not specify statements appropriate to creating, revoking, or rotating users, Vault inserts generic statements which can be unsuitable for your deployment.
Create an API request payload containing the database role definition.
$ tee readonly.sql <<EOFCREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}' INHERIT;GRANT ro TO "{{name}}";EOF
The SQL statement contains the templatized fields {{name}}, {{password}}, and {{expiration}}. Vault provides these values when creating the credentials. This creates a new PostgreSQL user and then grants that user the permissions defined in the PostgreSQL role named
ro
. When PostgreSQL starts it creates the PostgreSQL role.Create the role named
readonly
that creates credentials with thecreation_statements
defined in the payload.$ vault write database/roles/readonly \ db_name=postgresql \ creation_statements=@readonly.sql \ default_ttl=1h \ max_ttl=24h
The role generates database credentials with a default TTL of 1 hour and max TTL of 24 hours.
What format do you use when referencing templated files in a Vault configuration?
Wrap the reference in double curly brackets {{ template }}
.
Request PostgreSQL credentials
When a web service or application needs dynamic credentials, it can read them from Vault given the path created above.
The applications that require the database credentials read them from the secret engine's read only role. Read credentials from the
readonly
database role.$ vault read database/creds/readonlyKey Value--- -----lease_id database/creds/readonly/JBOEcGcnucqEGuTxkAVQed2Jlease_duration 1hlease_renewable truepassword pk5eOK9UcBBs-LmY48B1username v-token-readonly-YKqckMeO0qrZCjcQv25H-1722269563
Take note of this
username
for comparison later.
Validation
To verify that Vault created the user for PostgreSQL, connect to the database and list all database users.
$ docker exec -i \ learn-postgres \ psql -U root -c "SELECT usename, valuntil FROM pg_user;"
The output displays a table of all the database credentials generated. The Vault generated credentials appear in this list.
usename | valuntil--------------------------------------------------+------------------------root |v-token-readonly-YKqckMeO0qrZCjcQv25H-1722269563 | 2024-07-29 17:12:48+00
Check this user against the user noted earlier.
Clean up
Unset the
VAULT_TOKEN
,VAULT_ADDR
and thePOSTGRES_URL
environment variable.$ unset VAULT_TOKEN && unset VAULT_ADDR && unset VAULT_CACERT && unset POSTGRES_URL
Stop the PostgreSQL container.
$ docker stop $(docker ps -f name=learn-postgres -q)
Use
CTRL+C
to stop the server process in the terminal window where you started the server, or use this command to kill the server process from any local terminal session:$ pkill vault
Summary
Vault can store static secrets such as usernames and passwords, API keys, PKI certificates, and many other types of secrets.
Dynamic secrets allow you to created just-in-time dynamic credentials for popular cloud providers, database platforms, and container orchestration tools. These dynamic credentials are not available until requested, and revoked when it reaches the defined TTL. This greatly reduces the opportunity of exposure for these credentials because they are only valid for a short period of time.