Secret stores
Read credentials from a secret manager instead of storing them in AnsibleForms
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_pathnow use that store;vault_pathgoes away in 8.