Variables de seguridad

Tokens, cifrado, hosts REST salientes, enmascaramiento y los antiguos ajustes VAULT_*

  1. Ejemplo
  2. Llamadas REST y jq
  3. Variables

Cambia estas variables para definir tus propios secretos de token y de cifrado, y para limitar lo que los formularios pueden llamar y mostrar.


Ejemplo

Tus propios secretos, llamadas REST limitadas a una API y a una red interna, y una clave enmascarada más. En el .env del proyecto compose:

ACCESS_TOKEN_SECRET=change-me-to-a-long-random-string
ENCRYPTION_SECRET=change-me-to-32-random-characters
REST_ALLOWED_HOSTS=api.example.com,10.20.0.0/16
MASK_EXTRAVARS_REGEX=password|secret|token|apikey

Define ENCRYPTION_SECRET antes de guardar la primera credencial. Desde la 7.3.0, definirla o cambiarla vuelve a cifrar todos los secretos guardados en el siguiente arranque, con ENCRYPTION_SECRET_PREVIOUS indicando la clave antigua cuando era tuya, consulta Cambiar el secreto de cifrado.


Llamadas REST y jq

Desde la 7.3.0 las funciones auxiliares REST (fn.fnRestBasic, fn.fnRestAdvanced, fn.fnRestJwt, fn.fnRestJwtSecure) comprueban el certificado del endpoint al que llaman, como cualquier otra conexión; hasta la 7.2.0 no lo hacían nunca. Se les aplican tres ajustes:

Variable Predeterminado Efecto
REST_IGNORE_CERTS 0 1 omite la comprobación del certificado: solo para endpoints con un certificado autofirmado
REST_TIMEOUT_SECONDS 60 Cuánto espera una llamada REST una respuesta antes de fallar por tiempo de espera
JQ_TIMEOUT_SECONDS 30 Cuánto puede ejecutarse un filtro jq antes de detenerse; la salida de jq también está limitada a 64 MB

Las tres se aplican al guardarlas, sin reiniciar. Para un endpoint cuyo certificado está firmado por una CA privada, mantén la comprobación activada y dale esa CA a Node.js: define NODE_EXTRA_CA_CERTS con un archivo PEM que la contenga en el entorno de la aplicación y reinicia la aplicación.


Variables

Todas las variables de este grupo, con su tipo, su valor predeterminado y su descripción:

Variable Opciones/Valores predeterminados Comentarios
VAULT_ADDR
string

HashiCorp Vault address (deprecated)
a valid HashiCorp Vault URL

Deprecated since 7, removed in 8. The first 7.x start that finds these variables imports them once as the secret store vault; after that they are ignored. Manage the Vault under Connections > Secret stores. Base URL of a HashiCorp Vault server (e.g. https://vault.example.com:8200). When set together with VAULT_TOKEN, AnsibleForms can resolve credentials from Vault. A credential row in the local database can specify a vault_path; when present, the user/password are fetched from Vault at runtime instead of being read from the local encrypted columns.

VAULT_TOKEN
string Secreto

HashiCorp Vault token (deprecated)
a valid Vault token

Deprecated since 7, removed in 8. The first 7.x start that finds these variables imports them once as the secret store vault; after that they are ignored. Manage the Vault under Connections > Secret stores. Authentication token used by AnsibleForms to read secrets from Vault. Required when VAULT_ADDR is set. Use a token with read-only access to the relevant secret paths.

VAULT_NAMESPACE
string

HashiCorp Vault namespace (deprecated)

Deprecated since 7, removed in 8. The first 7.x start that finds these variables imports them once as the secret store vault; after that they are ignored. Manage the Vault under Connections > Secret stores. Optional Vault Enterprise namespace. Sent as the X-Vault-Namespace header on every request.

VAULT_KV_VERSION
number
Valor predeterminado:
2

Vault KV secret engine version (deprecated)
1, 2

Deprecated since 7, removed in 8. The first 7.x start that finds these variables imports them once as the secret store vault; after that they are ignored. Manage the Vault under Connections > Secret stores. Version of the Vault KV secret engine. Defaults to 2 (recommended). For KV v2, the client automatically inserts the /data/ segment in the API path if it is missing.

VAULT_DEFAULT_MOUNT
string
Valor predeterminado:
secret

Default Vault KV mount (deprecated)

Deprecated since 7, removed in 8. The first 7.x start that finds these variables imports them once as the secret store vault; after that they are ignored. Manage the Vault under Connections > Secret stores. Default KV mount used when a credential’s vault_path is provided as a bare key (no slash). For example, with the default secret mount, vault_path = "myapp" resolves to secret/data/myapp (KV v2).

VAULT_CACHE_TTL_MS
number
Valor predeterminado:
60000

Vault read cache TTL (ms) (deprecated)

Deprecated since 7, removed in 8. The first 7.x start that finds these variables imports them once as the secret store vault; after that they are ignored. Manage the Vault under Connections > Secret stores. Time-to-live in milliseconds of the in-memory cache used by AnsibleForms when reading secrets from Vault. Lower this if you rotate secrets aggressively; raise it to reduce load on Vault.

VAULT_SKIP_VERIFY
string
Valor predeterminado:
false

Skip Vault TLS verification (deprecated)
true, false

Deprecated since 7, removed in 8. The first 7.x start that finds these variables imports them once as the secret store vault; after that they are ignored. Manage the Vault under Connections > Secret stores. Disable TLS certificate verification when calling Vault. For development/testing only — never enable this in production.

ENTRA_AUTHORITY_URI
string
added in version 7.3.0
Valor predeterminado:
https://login.microsoftonline.com

Microsoft Entra ID sign-in authority
a valid base URI without slash on the end

The Microsoft identity platform the server sends a Microsoft Entra ID sign-in to, with the tenant id of the provider after it (<authority>/<tenant>/v2.0). Change it for a national cloud, e.g. https://login.microsoftonline.us (US Government) or https://login.chinacloudapi.cn (China), together with ENTRA_GRAPH_URI. Read when the Microsoft Entra ID provider is enabled or saved. Only HTTPS is accepted, except with NODE_ENV=development, for a local test provider. Replaces AZURE_AUTHORITY_URI, still read when this one is not set.

ENTRA_GRAPH_URI
string
added in version 7.3.0
Valor predeterminado:
https://graph.microsoft.com

Microsoft Graph API base URI
a valid base URI without slash on the end

The Microsoft Graph endpoint the server calls at a Microsoft Entra ID login to read the user’s group membership (/v1.0/me/transitiveMemberOf). Since 6.5.3 that call is made by the AnsibleForms server, not by the browser, so the server needs a route to it - behind an egress proxy set HTTPS_PROXY and NODE_USE_ENV_PROXY=1. Change it for a national cloud, e.g. https://graph.microsoft.us. Replaces AZURE_GRAPH_URI, still read when this one is not set.

AZURE_AUTHORITY_URI
string
added in version 7.0.4
Valor predeterminado:
https://login.microsoftonline.com

Microsoft Entra ID sign-in authority (deprecated)
a valid base URI without slash on the end

Deprecated: use ENTRA_AUTHORITY_URI ; removed in 8.0. Still read when ENTRA_AUTHORITY_URI is not set, with a warning in the log at start ; when both are set, ENTRA_AUTHORITY_URI wins.

AZURE_GRAPH_URI
string
added in version 4.0.3
Valor predeterminado:
https://graph.microsoft.com

Azure Graph API base URI (deprecated)
a valid base URI without slash on the end

Deprecated: use ENTRA_GRAPH_URI ; removed in 8.0. Still read when ENTRA_GRAPH_URI is not set, with a warning in the log at start ; when both are set, ENTRA_GRAPH_URI wins.

REST_ALLOWED_HOSTS
string
added in version 6.0.0
Valor predeterminado:

Allowed hosts for REST expressions

Comma-separated list of hostnames and/or CIDR ranges that REST helper functions (fn.fnRestBasic, fn.fnRestAdvanced, fn.fnRestJwt, fn.fnRestJwtSecure) are allowed to call.

When set, only these destinations are allowed; everything else is blocked. When unset, no allow-list is applied (default behaviour, all hosts allowed).

Hostnames are matched case-insensitively against the URL host; CIDRs are matched against the IPs that the host resolves to (so an entry like 10.0.0.0/8 blocks all private targets in that range regardless of which DNS name was used).

Example: REST_ALLOWED_HOSTS=api.example.com,partner.api.com,10.20.0.0/16

Caveat: this only protects the AnsibleForms Node.js process. Ansible playbooks run on the host directly and bypass this guard.

REST_DENIED_HOSTS
string
added in version 6.0.0
Valor predeterminado:

Blocked hosts for REST expressions

Comma-separated list of hostnames and/or CIDR ranges that REST helper functions (fn.fnRestBasic, fn.fnRestAdvanced, fn.fnRestJwt, fn.fnRestJwtSecure) must never call.

Wins over REST_ALLOWED_HOSTS. Useful to lock down dangerous targets like cloud metadata services or your internal admin UIs: REST_DENIED_HOSTS=169.254.169.254,127.0.0.0/8,internal-secret.example

Hostnames are matched case-insensitively against the URL host; CIDRs are matched against the IPs that the host resolves to.

Caveat: this only protects the AnsibleForms Node.js process. Ansible playbooks run on the host directly and bypass this guard.

REST_TIMEOUT_SECONDS
number
added in version 7.3.0
Valor predeterminado:
60

REST call timeout (seconds)

How long the REST helper functions (fn.fnRestBasic, fn.fnRestAdvanced, fn.fnRestJwt, fn.fnRestJwtSecure) wait for the remote endpoint before the call fails with a timeout error.

Without a limit, an endpoint that never answers kept the request and the form field waiting forever. A value that is not a number above 0 falls back to the default. Takes effect when saved.

REST_IGNORE_CERTS
boolean
added in version 7.3.0
Valor predeterminado:
0

Ignore REST certificates
0, 1

Whether the REST helper functions (fn.fnRestBasic, fn.fnRestAdvanced, fn.fnRestJwt, fn.fnRestJwtSecure) skip the TLS certificate check of the endpoint they call.

Until 7.3.0 they never checked it, so a man in the middle could read their tokens and answers. They now check it like every other connection: trust a private CA with NODE_EXTRA_CA_CERTS, or set this to 1 only for endpoints with a self-signed certificate. Takes effect when saved.

JQ_TIMEOUT_SECONDS
number
added in version 7.3.0
Valor predeterminado:
30

jq timeout (seconds)

How long a jq filter may run before the jq process is killed: fn.fnJq, the jq expression of the REST, file and credential functions, and the jq expression of database queries.

A runaway filter (for example a recursive definition that never ends) used to keep a jq process spinning forever. jq output is also capped at 64 MB. A value that is not a number above 0 falls back to the default. Takes effect when saved.

EXPRESSION_SANITIZER
string
added in version 6.3.0
Opciones:
  • off
  • paranoid
  • strict (predeterminado)
  • legacy

Server expression sanitizer
off, paranoid, strict

Server expressions (fields of type expression without runLocal) are evaluated on the server by any authenticated user. The sanitizer refuses anything that is not a call to an fn. or fnc. function.

  • off: every server expression is refused, use runLocal instead.
  • paranoid: only direct fn./fnc. calls. fn.fnTime().format('YYYY') is refused.
  • strict (default): fn./fnc. calls, and methods on their result.
  • legacy: the rules of 6.2.1 and earlier, where anything is allowed once the expression starts with fn.. This allows remote code execution for every authenticated user. It exists only as a temporary escape hatch after an upgrade, can only be set in the real environment (docker-compose, kubernetes), shows as a warning on the status page, and logs every expression the strict rules would refuse.

An unknown value falls back to strict.

API_TOKEN_MAX_DAYS
number
added in version 7.0.0
Valor predeterminado:
90

API token maximum life (days)
a positive integer

A role with the allowApiTokens option may log in with ?expiryDays=<n> and get an access token that lives n days, for scripts and integrations. It lives this many days at most: a longer request is cut to it. A role with extendedTokenExpiration may also ask ?expiryDays=never for a token that never expires. Like every token, it ends sooner when its user changes their password (or an admin sets it), when it is revoked, or when its session logs out.

Profile > API token makes one for any account. A local or LDAP user confirms their password ; a Microsoft Entra ID or OIDC user has none here, so the token is made from their session, which must have signed in at most 10 minutes before (the page sends them through the sign-in again otherwise). An API token cannot make another. Each token is listed on the profile and on the user’s page in Settings > Users (made, expires, last used), where the user or an admin revokes it. Blocking a user ends their tokens too. An SSO user’s token carries the roles of their last sign-in: a group change in the identity provider reaches it only when they sign in again and make a new one.

PASSWORD_MIN_LENGTH
number
added in version 7.0.0
Valor predeterminado:
8

Minimum password length
0 or a positive integer

A local password set through the application - a new user, an admin setting it, a user changing their own - needs at least this many characters, and is neither the username nor the public default password. Length rather than composition rules: a long passphrase is strong and easy to remember. ADMIN_PASSWORD at the first start is not checked. Applied without a restart.

CONTENT_SECURITY_POLICY
number
added in version 7.0.0
Valor predeterminado:
1

Enforce the Content-Security-Policy
0, 1

The web app is served with a Content-Security-Policy: scripts from the app itself only, so a script injected into a page does not run, and no other site may frame it. It allows what the app needs: local form expressions (evaluated in the browser), inline styles, images from any url (form and category images), and the code editor’s workers. With 0 it is sent as Content-Security-Policy-Report-Only: the browser’s console says what it would block, and nothing is blocked - for a setup that cannot work with it yet, a reverse proxy that injects its own script for example.

AUTH_RATE_LIMIT
number
added in version 7.0.0
Valor predeterminado:
120

Sign-in requests a minute from one address
0 or a positive integer

A ceiling on the authentication endpoints - the login, the logout, the token refresh and the single sign-on callbacks - per client address and per app node: above it they answer 429 before any password or token is looked at. Generous, so that a person or a script never reaches it ; LOGIN_MAX_FAILURES is what stops a password from being guessed. Behind a reverse proxy, set TRUST_PROXY so the address is the client’s. Applied without a restart.

LOGIN_MAX_FAILURES
number
added in version 7.0.0
Valor predeterminado:
5

Failed sign-ins before an account locks
0 or a positive integer

After this many failed logins in a row, an account (local or LDAP) is locked for LOGIN_LOCKOUT_MINUTES: every attempt is refused with status 429 before any password is checked, whether the account exists or not. A successful login clears the count, and so does an admin setting the user’s password. Counted in the database, so every app node sees the same attempts. 0 turns the lockout off. Applied without a restart.

LOGIN_MAX_FAILURES_PER_IP
number
added in version 7.0.0
Valor predeterminado:
30

Failed sign-ins from one address before it is held back
0 or a positive integer

One address trying many accounts: after this many failed logins within LOGIN_LOCKOUT_MINUTES, every login from it is refused with status 429 until that window ends. Behind a reverse proxy, set TRUST_PROXY so the address is the client’s and not the proxy’s. 0 turns it off. Applied without a restart.

LOGIN_LOCKOUT_MINUTES
number
added in version 7.0.0
Valor predeterminado:
15

Account lockout (minutes)
0 or a positive integer

How long an account stays locked after LOGIN_MAX_FAILURES failed logins, and the window in which the failures of an account and of an address are counted. 0 turns the lockout and the per-address limit off. Applied without a restart.

ACCESS_TOKEN_SECRET
string Secreto
Valor predeterminado:
*** NOT REVEALED ***

Access token secret
a hard secret string

AnsibleForm uses Basic Authentication as authentication mechanism.
Once authenticated, the client uses a JWT (Json Web Token) for authorization (Bearer authorization header). This token is stored on the client side (i.e. browser cookie) and is signed with a secret key.
To keep the communication between client and server safe, we strongly recommend you to set this secret.

Not set: each start generates a random secret, so a restart ends every session and everyone logs in again. An app node (AF_ROLE=app) refuses to start without it: set the same value on every app node.

ACCESS_TOKEN_EXPIRATION
string
Valor predeterminado:
30m

Access token expiration
a valid time indication

A JWT (Json Web Token) is only valid for a certain amount of time.
If someone would be able to intercept a communication packet and see the token, it would only be valid for a short time.
After this short time, the client must refresh his access token using his refresh token.

ACCESS_TOKEN_REFRESH_EXPIRATION
string
Valor predeterminado:
24h

Refresh token expiration
a valid time indication

Once the access token is expired, and the client tries to connect to the server, the client hits a 401 error (unauthorized). The client application captures this error, and calls the refresh API with its refresh token. If the refresh token is valid, the client gets a new set of access and refresh tokens and retries the last unauthorized api call with the renew access token.
With this mechanism, the client can keep the user-connection open for a long time and avoid a sudden logout during a save action.
If the client has not connected back to the server during the expiration time of the refresh token, the client is logged of and authentication is required again.

ACCESS_TOKEN_ISSUER
string
added in version 5.0.8
Valor predeterminado:
ansibleforms

Access token issuer
a valid issuer string

The issuer of the access token is the name of the application that issues the token.
This is used to verify the token on the server side. Also reverse proxies can be picky about the issuer being present.

ENCRYPTION_SECRET
string Secreto
Valor predeterminado:
*** NOT REVEALED ***

Encryption secret
a strong encryption string

AnsibleForms encrypts the passwords and tokens it stores in the database with this secret. Set your own : without it they are encrypted with a default key that is public in the source code. The secret must be 32 characters long ; a shorter one is extended and a longer one cut. Setting it, or changing it, re-encrypts every stored secret with it on the next start, in one transaction, so none is lost : from the default key the first time, from ENCRYPTION_SECRET_PREVIOUS after that. Set the same value on every app node, the worker and every RTE that registers itself.

ENCRYPTION_SECRET_PREVIOUS
string Secreto
added in version 7.3.0
Valor predeterminado:

Previous encryption secret
the previous ENCRYPTION_SECRET

When ENCRYPTION_SECRET changes from a key of your own to another, set this to the old one for one start : every stored secret is re-encrypted from it to the new ENCRYPTION_SECRET, then it is not used and can be removed. Not needed when ENCRYPTION_SECRET is set for the first time (the stored secrets are then re-encrypted from the default key). When the stored secrets were encrypted with a key that is neither, nothing is changed and the worker refuses to start, saying in the log which variable to set.

REGEX_FILTER_JOB_OUTPUT
string
Valor predeterminado:
\[low\]

Job output filter (regex)
an escaped regular expression

Sometime, the job output is flooded with meaningless output, such as ‘Gathering Facts’, or ‘Includes’. You filter out these by add a piece of string in the taskname, i.e. [low] and then used regex \\[low\\] to hide this information.
The output has a button Apply filter for the filtering to take effect.

MASK_EXTRAVARS_REGEX
string
Valor predeterminado:
password|secret|token

Extravars to mask (regex)
an escaped regular expression

When you have sensitive extravars, such as passwords, tokens, keys, credentials, you do not want these to be shown in clear text in the extravars section of a job.
By setting this regex, any extravar key matching this regex will have its value masked with ********. Note the regex will be case insensitive.

Since 6.4.1 the value of every password field of the form is masked as well, whatever its key or model path - in list rows and yaml subforms too. The regex is for the other secrets, the ones that are not password fields.

EXTRAVARS_USER_FIELDS
string
Valor predeterminado:

User fields in the extravars
empty, all, none, or a comma separated list of keys

Every job carries the launching user as ansibleforms_user in its extravars. With an LDAP or Microsoft Entra ID login that is the user’s full group membership plus every resolved role option, which for a directory user in a hundred groups is a hundred lines of extravars the playbook never reads - stored in the AWX job and in jobs.extravars, readable by anyone with access to the job.

Leave it empty (the default) and the whole object is sent, exactly as in every release before this one. Set it to a comma separated list of top level keys, for example username,email,type, and only those are sent. Set it to none and ansibleforms_user is not sent at all. all is the default said out loud, useful for a form that must override a trimmed global setting.

A single form can override this with its userExtravars property. The frontend __user__ object comes from the login token and is not affected, and neither are role checks or the form’s own roles list.

Note: the FAQ suggests asserting on ansibleforms_user.groups inside a playbook as a defence in depth check. If you do that, keep groups in the list.