Hashicorp Vault as a Secret Manager

Hashicorp Vault is a widely used secrets and encryption management system. In the case of TDK, secrets refer to database connection credentials, such as usernames and passwords.

Without Vault

Without secret managers, such as Vault, we have to use usernames and passwords in their raw and unsafe format:

tdk \
    --input-url ...
    --input-username USER_FOO \
    --input-password fxORRC-IYcavW5CoYX9Z \
    --output-url ...
    --output-username USER_BAR \
    --output-password -Z3aS2JUnZUjeyCd4bGu \
    ...

Using Hashicorp Vault

But using Hashicorp Vault, we can store and manage our database credentials in a special secure storage.

To enable Vault as a secret manager for TDK, you only need to add some properties, such as URI and authentication parameters, to the Application properties file:

secrets.vault.secret.manager.enabled = true
vault.uri=http://localhost:9200
vault.token=1234

Please refer to the documentation for information on other Vault authentication methods that are supported.

Types of Vault database secrets

TDK supports at least 3 methods for storing and managing database secrets. Let’s consider each of them.

1. Simple secrets

In the simplest case, we can store usernames and passwords like separate, unrelated Vault secrets without validation on the database side, rotation, TTL options, and so on.

Add simple secrets with the database username and password in the Vault key-value storage:

vault kv put secret/postgres-source-simple-user username=simple_pg_user password=SecrETPassw0rD!
vault kv put secret/postgres-target-simple-user username=simple_pg_user password=SecrETPassw0rD!
Prepare TDK inventory file inventory_simple_secret.yaml
data_sources:
  default_source:
    jdbc_url: jdbc:postgresql://localhost:6000/postgres
    user:
      type: vault
      storage: secret
      path: postgres-source-simple-user
      versioned: true
      secret: username
    password:
      type: vault
      storage: secret
      path: postgres-source-simple-user
      versioned: true
      secret: password
  default_target:
    jdbc_url: jdbc:postgresql://localhost:6001/postgres
    user:
      type: vault
      storage: secret
      path: postgres-target-simple-user
      versioned: true
      secret: username
    password:
      type: vault
      storage: secret
      path: postgres-target-simple-user
      versioned: true
      secret: password

Run TDK using the inventory_simple_secret.yaml file:

tdk \
    --inventory-file inventory_simple_secret.yaml \
    --config-file config.tdk.yaml

2. Static database roles

Vault offers more than just ordinary secrets. It also provides static roles for storing and managing database-specific users and connections.

Enable Vault database plugin:

vault secrets enable database

Create a Vault connection for the source database:

vault write database/config/postgres_source \
    plugin_name=postgresql-database-plugin \
    connection_url="postgresql://{{username}}:{{password}}@input_db:5432/postgres" \
    allowed_roles="postgres-source-static-role,postgres-source-dynamic-role" \
    username="postgres" \
    password="postgres"
Create a Vault connection for the target database in the same manner
vault write database/config/postgres_target \
    plugin_name=postgresql-database-plugin \
    connection_url="postgresql://{{username}}:{{password}}@output_db:5432/postgres" \
    allowed_roles="postgres-target-static-role,postgres-target-dynamic-role" \
    username="postgres" \
    password="postgres"

Create a Vault static role for the source database:

vault write database/static-roles/postgres-source-static-role \
    db_name=postgres_source \
    username="static_vault_pg_user" \
    rotation_period=86400
Create a Vault static role for the target database in the same manner
vault write database/static-roles/postgres-target-static-role \
    db_name=postgres_target \
    username="static_vault_pg_user" \
    rotation_period=86400
Prepare TDK inventory file inventory_static_users.yaml
data_sources:
  default_source:
    jdbc_url: jdbc:postgresql://localhost:6000/postgres
    user:
      type: vault
      storage: database
      path: static-creds/postgres-source-static-role
      secret: username
    password:
      type: vault
      storage: database
      path: static-creds/postgres-source-static-role
      secret: password
  default_target:
    jdbc_url: jdbc:postgresql://localhost:6001/postgres
    user:
      type: vault
      storage: database
      path: static-creds/postgres-target-static-role
      secret: username
    password:
      type: vault
      storage: database
      path: static-creds/postgres-target-static-role
      secret: password

Run TDK using the inventory_static_users.yaml file:

tdk \
    --inventory-file inventory_static_users.yaml \
    --config-file config.tdk.yaml

3. Dynamic database roles

In addition to static roles, Vault also provides dynamic roles. In this case, Vault creates database users with random passwords for each request.

Create a Vault dynamic role for the source database:

vault write database/roles/postgres-source-dynamic-role \
    db_name="postgres_source" \
    creation_statements=" \
      create role \"{{name}}\" with login password '{{password}}' valid until '{{expiration}}'; \
      grant connect on database postgres to \"{{name}}\"; \
      grant usage on schema foo TO \"{{name}}\"; \
      grant select on all tables in schema \"foo\" to \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"
Create a Vault dynamic role for the target database in the same manner
vault write database/roles/postgres-target-dynamic-role \
    db_name="postgres_target" \
    creation_statements=" \
      create role \"{{name}}\" with login password '{{password}}' valid until '{{expiration}}'; \
      grant create on database postgres to \"{{name}}\"; \
      grant usage, create on schema foo TO \"{{name}}\"; \
      grant truncate, select, insert on all tables in schema \"foo\" to \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"
Prepare TDK inventory file inventory_dynamic_users.yaml
data_sources:
  default_source:
    jdbc_url: jdbc:postgresql://localhost:6000/postgres
    user:
      type: vault
      storage: database
      path: creds/postgres-source-dynamic-role
      secret: username
    password:
      type: vault
      storage: database
      path: creds/postgres-source-dynamic-role
      secret: password
  default_target:
    jdbc_url: jdbc:postgresql://localhost:6001/postgres
    user:
      type: vault
      storage: database
      path: creds/postgres-target-dynamic-role
      secret: username
    password:
      type: vault
      storage: database
      path: creds/postgres-target-dynamic-role
      secret: password

Run TDK using the inventory_dynamic_users.yaml file:

tdk \
    --inventory-file inventory_dynamic_users.yaml \
    --config-file config.tdk.yaml