QueryProxy
Documentation menu

External secret references

Keep target database passwords in HashiCorp Vault, AWS Secrets Manager or Azure Key Vault. QueryProxy resolves the reference when a connection opens.

By default a connection’s password is stored in QueryProxy, encrypted with the application key. If your policy says database passwords live in a secret manager, give the connection a reference instead of the value. QueryProxy reads the secret when it opens the connection, hands it to the database driver and forgets it. Only the reference is stored.

Three kinds of reference are supported:

Store Reference
HashiCorp Vault vault://<mount>/<path>#<key>
AWS Secrets Manager aws-sm://<secret-id>[#<json-key>]
Azure Key Vault azure-kv://<vault-name>/<secret-name>[/<version>]

For Vault, #<key> names the field inside the secret. For AWS, <secret-id> is the secret’s name or ARN, and #<json-key> picks one key when the secret is a JSON object; without it the whole secret string is the password. For Azure, the version is optional and defaults to the current one.

vault://secret/teams/payments/orders-db#password
aws-sm://prod/payments/orders-db#password
azure-kv://payments-vault/orders-db-password

The password of a connection and its separate read password can each be a reference. A plain password that happens to start with vault://, aws-sm:// or azure-kv:// is now treated as a reference.

Which team may use which references

References are bounded per team, and a team starts with none. Without that bound, anyone who may create a connection could point its host at a server of their own and have QueryProxy send it any secret that the Vault token can read.

A system administrator sets, for each team, the prefixes its connections may reference. Either use Admin → Teams (the Secret paths panel), or the command line:

php artisan queryproxy:team:secret-prefixes payments vault://secret/teams/payments
php artisan queryproxy:team:secret-prefixes payments            # show the list
php artisan queryproxy:team:secret-prefixes payments --clear    # allow none

The command takes the team’s id or slug. The prefixes you pass are the complete new list. Rules:

  • A reference is allowed when it lies under a prefix, compared on whole path segments: vault://secret/teams/pay does not cover vault://secret/teams/payments.
  • A prefix has no wildcard, no #key, no .. or %, and at most 512 characters. A trailing / is dropped.
  • An AWS secret written as an ARN and the same secret written by name are two different addresses; list the form you use. Neither covers the other.
  • A change is written to the audit log as team.secret_prefixes_changed, with the list before and after. The panel warns when a prefix overlaps another team’s.
  • A team without prefixes cannot save a connection with a reference, and the connection form disables the external sources for it. Moving a connection to another team checks its reference again.
  • Checks run when the connection is saved and again when the secret is resolved, before any cache is read, so another team’s cached value is never handed out.

In the connection form

Open Connections → New connection (or edit one). Credential source offers Stored password, HashiCorp Vault, AWS Secrets Manager and Azure Key Vault. For an external source the password boxes turn into a reference field that shows the expected format and a warning if what you typed does not look right. The warning is only a hint: the server decides. The team’s allowed paths are listed under the field.

The connection list has a Credentials column: the source and, for a reference, the reference itself, never a password. A reference that now falls outside the team’s prefixes is marked Outside team paths. Editing a connection shows its reference; a stored password is never shown.

Test resolves the reference for real. If anything fails, it reports a fixed message that names a reason and never carries the secret.

When resolution fails

Resolution fails closed: the request is marked failed with a generic message, and no connection is attempted. The detail goes to the application log (scheme, host or path, reason, HTTP status) and to the audit log as secret.resolve_failed (the scheme and path, without #key, and the reason). The value and the tokens used to fetch it are never written anywhere. The reasons are invalid_reference, not_allowed, not_configured, auth_failed, access_denied, not_found, key_missing, malformed_response and unavailable.

Values are cached in the process memory of the web and worker processes, not in the application cache. QUERYPROXY_SECRET_CACHE_TTL sets the lifetime (default 300 seconds, 0 turns the cache off). After you rotate a password in the store, connections pick it up within that time. Each process has its own cache.

php artisan queryproxy:doctor has a secret_stores check. It makes no network call: it fails when a store that a saved reference uses is not configured, or when a saved reference is malformed or outside its team’s prefixes.

Settings

Variable Default Meaning
QUERYPROXY_SECRET_CACHE_TTL 300 Seconds a resolved value stays in process memory; 0 turns the cache off
QUERYPROXY_SECRET_TIMEOUT 5 Seconds a store may take to connect and to answer

HashiCorp Vault

Variable Default Meaning
QUERYPROXY_VAULT_ADDR none Address of Vault, such as https://vault.example.com:8200
QUERYPROXY_VAULT_NAMESPACE none Vault namespace, when you use them
QUERYPROXY_VAULT_AUTH token token, approle or kubernetes
QUERYPROXY_VAULT_TOKEN none The token, for token
QUERYPROXY_VAULT_ROLE_ID, QUERYPROXY_VAULT_SECRET_ID none AppRole credentials
QUERYPROXY_VAULT_APPROLE_MOUNT approle Mount of the AppRole method
QUERYPROXY_VAULT_K8S_ROLE none Vault role for the Kubernetes method
QUERYPROXY_VAULT_K8S_MOUNT kubernetes Mount of the Kubernetes method
QUERYPROXY_VAULT_K8S_JWT_PATH /var/run/secrets/kubernetes.io/serviceaccount/token Service account token to log in with
QUERYPROXY_VAULT_KV_VERSION 2 2 or 1
QUERYPROXY_VAULT_MOUNTS none KV mount paths, comma-separated
QUERYPROXY_VAULT_CA_FILE none CA bundle for a Vault with a private certificate

QUERYPROXY_VAULT_MOUNTS is required for KV v1, where nothing else tells a KV mount from any other Vault API path. It is also needed for mounts whose name has a slash, such as kv/prod; the longest matching mount is used. Once set it is an allowlist: only those mounts are read. Whatever the setting, references into auth, sys, identity and cubbyhole are refused, so a reference cannot be used to read Vault’s own API paths.

AWS Secrets Manager

QueryProxy uses the AWS SDK’s default credential chain: environment variables, shared credential files, IRSA web identity, ECS task role and instance profile. There are no QueryProxy-specific keys.

Variable Default Meaning
QUERYPROXY_AWS_REGION none Region. AWS_REGION and AWS_DEFAULT_REGION are used if this is not set; an ARN carries its own region

Azure Key Vault

Variable Default Meaning
QUERYPROXY_AZURE_AUTH auto auto, client_secret, workload_identity or managed_identity. auto uses a client secret if one is set, else workload identity if a token file is set, else managed identity
QUERYPROXY_AZURE_TENANT_ID, QUERYPROXY_AZURE_CLIENT_ID none Tenant and application. AZURE_TENANT_ID and AZURE_CLIENT_ID are used if these are not set
QUERYPROXY_AZURE_CLIENT_SECRET none Client secret, for client_secret
QUERYPROXY_AZURE_FEDERATED_TOKEN_FILE none Token file for workload identity (AKS injects AZURE_FEDERATED_TOKEN_FILE)
QUERYPROXY_AZURE_VAULT_DNS_SUFFIX vault.azure.net DNS suffix the vault host is built from; the host comes from the vault name and this suffix only, so a reference cannot steer a request elsewhere

QUERYPROXY_VAULT_TOKEN, QUERYPROXY_VAULT_SECRET_ID and QUERYPROXY_AZURE_CLIENT_SECRET can come from a file with the usual _FILE suffix; see Secrets from files.

Next steps

Arrow keys to move, Enter to open.