Secret stores

Read credentials from a secret manager instead of storing them in AnsibleForms

  1. How it works
  2. Adding a store
  3. Using a store
    1. In a credential
    2. Inline, without a credential row
    3. Key names
  4. In the config seed
  5. Upgrading from the VAULT_* variables

For administrators: connect a secret manager, and let credentials read their user and password from it.


How it works

A secret store is a connection to a secret manager, added under Connections > Secret stores. A credential can then name a store and a place in it (the secret reference). When a form, a playbook or an expression uses that credential:

  • user and password are read from the store, every time (within the store’s cache time);
  • host, port and database stay those of the credential row. When the row leaves one of them empty and the secret carries it, the value from the secret is used.

Forms keep referring to the credential by name, so moving a password into a store changes nothing in your forms.

Store type Status
HashiCorp Vault (KV v1 and v2, token authentication) available
HashiCorp Vault dynamic credentials (<mount>/creds/<role>) experimental
CyberArk Central Credential Provider (CCP) experimental

Adding a store

From 7.3.0 only an admin can add, change or delete a store, as its token or key is sent to its address. Every store has these fields:

Field Meaning
Name How credentials refer to the store.
Type The secret manager.
Credential How and where the store logs in: a credential of Connections > Credentials, on the Credentials step, of type HashiCorp Vault (its token and address) or CyberArk. It holds the base URL, e.g. https://vault.example.com:8200, and the TLS settings: skip certificate verification, or a CA bundle in PEM for a private CA. It cannot read from a secret store itself.
Cache (seconds) How long a secret that was read is reused. 0 reads it on every use. Default 60.
Extra options (JSON) Options specific to the type, as a JSON object.

Up to 7.2.0 the address, the token and the TLS settings were fields of the store. The upgrade to 7.3.0 moves them into its credential, named <store>-token (or <store>-appid for CyberArk), see Addresses in credentials.

Test connection checks access without reading a secret. The Status page checks every store, and warns 7 days before a Vault token expires.

Each secret manager has a page of its own:

Page What it covers
HashiCorp Vault KV v1 and v2 with token authentication, and dynamic database credentials
CyberArk The Central Credential Provider (CCP) of CyberArk’s Application Access Manager

Using a store

In a credential

Under Connections > Credentials, on the credential’s Store step, choose Secret store, then pick the Secret store and fill in the Reference. The user and password are then read from the store, not typed in.

Inline, without a credential row

Anywhere a credential name is accepted, you can reference a store directly instead (fnCredentials, fnRestBasic, a form’s credentials:, a query’s dbConfig).

fn.fnCredentials('secret:vault:secret/myapp/prod')   // secret:<store>:<reference>
fn.fnCredentials('vault:secret/myapp/prod')          // the store named `vault`
- name: servers
  type: enum
  query: SELECT name FROM servers
  dbConfig: secret:vault:secret/cmdb/db

Without a credential row, a database secret needs at least a db_type and a host, for example:

{ "username": "forms", "password": "...", "host": "cmdb.example.com", "port": 3306,
  "db_type": "mysql", "database": "cmdb" }

Without db_type, the secret is a plain credential (user, password and any other keys), and a query on it assumes mysql.

Key names

A secret is mapped to a credential with these aliases. Other keys are passed through.

Credential field Accepted keys in the secret
user user, username, login
password password, token, api_key, apikey, secret
host host, address
port port
db_name db_name, database
db_type, secure same name (inline secrets only)

For a credential row, host, port and database name from the secret are used only where the row leaves them empty.

A secret with a different structure can be reshaped with a jq expression, passed as the third argument of fnCredentials:

// secret: { "creds": { "u": "admin", "p": "Netapp12" } }
fn.fnCredentials('vault:secret/weird', '', '.creds | { user: .u, password: .p }')

In the config seed

Stores can also be declared in the config seed, like the other admin objects:

secret_stores:
  items:
    - name: vault
      type: vault
      credential: vault-login        # its address and token

credentials:
  items:
    - name: vault-login
      credential_type: vault
      url: https://vault.example.com:8200
      password: ${SEED_VAULT_TOKEN}  # the Vault token
    - name: production-db
      secret_store: vault
      secret_ref: secret/forms/production-db
      host: db.example.com
      port: 3306
      db_type: mysql
      is_database: true

The store logs in with its credential: a vault one holds the Vault’s address in url and the token as its password, a cyberark one the AppID and the CCP address. Without a credential, the store’s own url and token are used. For details, see Config seed.


Upgrading from the VAULT_* variables

Before 7 the only store was a HashiCorp Vault configured with VAULT_* environment variables, and a credential pointed at it with Vault path.

  • The variables are imported once as a store named vault; remove them afterwards.
  • Credentials with a vault_path now use that store; vault_path goes away in 8.