Vault
Username templating
Challenge
Some of Vault's secrets engines generate usernames and passwords for an external system to provide short-lived credentials to secure the target system, such as databases.
The User Configurable Password Generation for Secret Engines tutorial demonstrated the use of the password policy to set a rule for the password that an organization must adhere to (e.g. the character length of the password).
Similarly, an organization may have a username convention or standard that differs from what Vault generates by default.
Solution
Username templating lets you customize how usernames are generated for the external systems when using Vault's secrets engines. It defines the format with static text, secrets engine metadata, system information, and randomized values.
Secrets engines with support for username templating:
Requires Vault 1.7 or later | Requires Vault 1.8 or later |
---|---|
Cassandra | Elasticsearch |
Couchbase | InfluxDB |
MongoDB | MongoDB Atlas |
MSSQL | RedShift |
MySQL/MariaDB | SnowflakeDB |
LDAP | RabbitMQ |
PostgreSQL |
Prerequisites
This lab was tested on macOS using an x86_64 based processor. If you are running macOS on an Apple silicon-based processor, use a x86_64 based Linux virtual machine in your preferred cloud provider.
To perform the tasks described in this tutorial, you need to have:
A Vault environment of version 1.7 or later. Refer to the Getting Started tutorial to install Vault. Make sure that your Vault server has been initialized and unsealed.
ngrok installed and configured with auth token (HCP Vault Dedicated only)
Lab setup
Start Postgres
The tutorial requires a Postgres database. Docker provides a Postgres server image that satisfies this requirement.
Open a new terminal and pull a Postgres server image with
docker
.$ docker pull postgres:latest
Create a Postgres database with a root user named
root
with the passwordrootpassword
.$ docker run \ --name postgres \ --env POSTGRES_USER=root \ --env POSTGRES_PASSWORD=rootpassword \ --detach \ --publish 5432:5432 \ postgres
The database is available.
In a another terminal, connect to the Postgres database via the CLI within the
postgres
container.$ docker exec -it postgres psql
Your system prompt is replaced with a new prompt
root=#
. Commands issued at this prompt are executed against the Postgres database running within the container.Create a role named
ro
.$ CREATE ROLE ro NOINHERIT; CREATE ROLE
Grant the ability to read all tables to the role named
ro
.$ GRANT SELECT ON ALL TABLES IN SCHEMA public TO "ro"; GRANT
The role is created and assigned the appropriate permissions.
Disconnect from the Postgres database.
$ \q
Start Vault
In a new terminal start a Vault dev server with
root
as the root token.$ vault server -dev -dev-root-token-id root
The Vault dev server defaults to running at
127.0.0.1:8200
. The server is initialized and unsealed.Insecure operation
Do not run a Vault dev server in production. This approach starts a Vault server with an in-memory database and runs in an insecure way.
In a new terminal export an environment variable for the
vault
CLI to address the Vault server.$ export VAULT_ADDR=http://127.0.0.1:8200
Export an environment variable for the
vault
CLI to authenticate with the Vault server.$ export VAULT_TOKEN=root
The Vault server is ready.
Note
For these tasks, you can use Vault's root token. However, it is recommended that root tokens are only used for enough initial setup or in emergencies. As a best practice, use an authentication method or token that meets the policy requirements.
Set an environment variable for the PostreSQL address.
$ export POSTGRES_URL=127.0.0.1:5432
You are ready to proceed with the lab.
Enable and configure the database secrets engine
The database secrets engine generates database credentials dynamically based on configured roles.
Enable the database secrets engine at the
database/
path.$ vault secrets enable database
Configure the database secrets engine with the connection credentials for the Postgres database.
$ vault write database/config/postgresql \ plugin_name=postgresql-database-plugin \ connection_url="postgresql://{{username}}:{{password}}@localhost:5432/postgres?sslmode=disable" \ allowed_roles=readonly \ username="root" \ password="rootpassword"
Define the SQL used to create credentials.
$ tee readonly.sql <<EOF CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}' INHERIT; GRANT ro TO "{{name}}"; EOF
Create the role named
readonly
that creates credentials with thereadonly.sql
.$ vault write database/roles/readonly \ db_name=postgresql \ creation_statements=@readonly.sql \ default_ttl=1h \ max_ttl=24h
The databaes secrets engine is configured and the role is defined with the default username template.
Note
Learn more about managing database credentials in Dynamic Secrets: Database Secrets Engine tutorial.
Request credentials with default username
The applications that require the database credentials read them from the secret engine's readonly role. The database secrets engines generate usernames that adhere to a default pattern.
Read credentials from the
readonly
database role.$ vault read database/creds/readonly Key Value --- ----- lease_id database/creds/readonly/ZxoKlbklsliYA4hZs7umoPIz lease_duration 1h lease_renewable true password 9MSegMz7N1Fr69ZTyb#D username v-token-readonly-wGLPkpDyc6AgqBfMZTD3-1604195404
The generated username uses the default pattern expressed as a Go template:
{{printf "v-%s-%s-%s-%s" (.DisplayName | truncate 8) (.RoleName | truncate 8) (random 20) (unix_time) | truncate 63 }}
The printf "v-%s-%s-%s-%s"
function accepts text, or string, as a parameter.
This string may contain text (v-
and -
) and variables (%s
). These
variables represent functions or values that return a string. These functions or
values immediately follow this string.
The (.DisplayName | truncate 8)
renders the .DisplayName
attribute of the
authenticated token. The result is then piped to the truncate
function and
the string is truncated, or shortened to 8
characters.
The (.RoleName | truncate 8)
renders the name of the requested database
secrets engine role, .RoleName
, truncated again to 8 characters.
The (random 20)
renders a randomized sequence of 20
lowercase letters,
uppercase letters, and numbers.
The unix_time
renders the current unix timestamp (number of seconds since Jan
1, 1970).
The resulting string that printf "v-%s-%s-%s-%s"
produces is piped to the
truncate
function and truncated, or shortened to 63
characters.
Refer to the Username Templating documentation to learn more functions that can be applied.
Set the username template
A customized username template may be provided to meet the needs of your organization.
Ensure that custom username templates include enough randomness to prevent the same username being generated multiple times.
The generated username uses the default pattern expressed as a Go template:
myorg-{{.RoleName}}-{{unix_time}}-{{random 8}}
This username template is prefixed with myorg-
, uses the name of role,
readonly
, the unix timestamp in seconds, and a random sequence of 8
characters. These functions and values are displayed inline and escaped with the
{{ }}
sequence.
Configure the database secrets engine with a new username template.
$ vault write database/config/postgresql \ username_template="myorg-{{.RoleName}}-{{unix_time}}-{{random 8}}"
Read credentials from the
readonly
database role.$ vault read database/creds/readonly Key Value --- ----- lease_id database/creds/readonly/NOCGtSbz7g4FFjcztX6Bqh3S lease_duration 1h lease_renewable true password -h3B-JteYjgOPYIC6dGQ username myorg-readonly-1616447348-af9eHMWD
Next Step
You defined a customized username template for the database secrets engine. Learn more about templating by reading the Username Templating documentation.
You generated credentials for the database secrets engine. Learn more about managing these credentials in the Dynamic Secrets: Database Secrets Engine tutorial.
Username templating is supported by other secrets engines. Learn more about defining a username template in the Database Secrets Engine with MongoDB tutorial.