Skip to content

Configuration

The broker reads its configuration from the environment and refuses to start, naming the variable, when one is missing, malformed or out of range.

Variable Default Accepted values What it sets
BROKER_DATABASE_URL — — The Postgres connection URL, the one variable that says which database the broker uses and how it signs in. Required. The broker applies its own migrations at startup. A URL that names a user and no password (neither in the URL nor from PGPASSWORD or a passfile) and whose host is an Amazon RDS endpoint (one ending in .rds.amazonaws.com) signs in by RDS IAM authentication: each new connection signs in with an auth token the broker mints for that user and host from the AWS SDK’s default credentials, in the region AWS_REGION, AWS_DEFAULT_REGION or the shared AWS config names, which need rds-db:connect on the database user. Such a URL must name that one host, with sslmode=verify-full and an sslrootcert file (the broker image ships the RDS CA bundle at /etc/ssl/rds/global-bundle.pem), or the broker refuses to start, since a token is a password to the database for 15 minutes; sslrootcert=system is refused too, since the system trust store holds no RDS CA. Any other URL, one carrying its own password or a passwordless one to a local Postgres that trusts its clients, connects as given. Nothing is substituted into it: a URL naming the removed ${BROKER_DATABASE_PASSWORD} placeholder is refused, as BROKER_DATABASE_PASSWORD itself is.
BROKER_ENVOY_TOKEN
BROKER_ENVOY_TOKEN_FILE
— — The bearer token for BROKER_ENVOY_URL, given directly or as a file whose trimmed contents win over the variable; read only when BROKER_ENVOY_URL is set.
BROKER_ENVOY_URL — — An Envoy listener the broker notifies, best effort, when a session’s pending request expires undecided. Unset, the broker sends nothing.
BROKER_FAKE_SECRETS_FILE — — For local development only, a JSON file standing in for the secret store, in Secrets Manager’s shape whatever BROKER_SECRETS_STORE says, {“secrets”: [{“name”, “kms_key_id”, “tags”, “value”, “deleted_at”}]}: the broker lists the agent secrets and reads their values from it instead of from the store, reading the file again on each of those calls, so an edit to it is a write as Secrets Manager would see one. A secret with no “value” is one created without a value, which the broker refuses as no-current-value; one with a “deleted_at” (RFC 3339) is scheduled for deletion, which the broker no longer lists. Its “kms_key_id” names the agent-secrets key as the selected store configures it: BROKER_SECRETS_KMS_KEY_ARN, or BROKER_SECRETS_KMS_KEY_NAME under gcp-secret-manager.
BROKER_K8S_OIDC_AUDIENCE — — The audience a pod’s projected service-account token must carry.
BROKER_K8S_OIDC_ISSUER — — The issuer of the Kubernetes service-account tokens pods enroll with. Set it with BROKER_K8S_OIDC_AUDIENCE, or neither, in which case no pod can enroll.
BROKER_LAUNCHER_CREDENTIAL_SECONDS 604800 whole number from 1 to 2592000 How long a machine login’s credential lasts once approved; past it the machine logs in again, with a new key, a new code and a new approval.
BROKER_LEASE_SECONDS 900 whole number from 1 to 3600 An enrollment’s lease. A session renews it while it runs; one whose lease runs out is no longer enrolled and its calls are refused.
BROKER_LISTEN_ADDR 127.0.0.1:13380 — The host:port the broker listens on.
BROKER_PROOF_SKEW_SECONDS 60 whole number from 1 to 300 How far the issue time of a signed proof or request object may differ from the broker’s clock.
BROKER_PUBLIC_URL — — The broker’s own address as its callers reach it, an absolute URL with no path. Required. Every signed proof and request object names it, so a client’s AGENT_SECRETS_URL must be exactly this.
BROKER_SECRETS_KMS_KEY_ARN
BROKER_SECRETS_KMS_KEY_NAME
— — The agent-secrets key every agent secret is encrypted with, one variable per store, each required with its store and refused with the other. With aws-secrets-manager, BROKER_SECRETS_KMS_KEY_ARN, the AWS KMS key’s ARN; with gcp-secret-manager, BROKER_SECRETS_KMS_KEY_NAME, the Cloud KMS key’s resource name projects/<project>/locations/global/keyRings/<ring>/cryptoKeys/<key> (global, since a secret with automatic replication takes a global key). A secret under the prefix encrypted with any other key, the provider’s default one included, is refused.
BROKER_SECRETS_PREFIX — — The namespace of the agent secrets. Required. With aws-secrets-manager, a Secrets Manager name prefix ending in “/” (for example production/agent-secrets/); with gcp-secret-manager, projects/<project number>/secrets/<secret-id prefix>- (for example projects/000000000000/secrets/agent-secrets-), the project by its number, since Secret Manager answers every resource name with the number. The broker serves every secret under it that carries an owner and a tier tag (Secret Manager: annotation) and is encrypted with the agent-secrets key, and rereads them every five minutes.
BROKER_SECRETS_STORE — — Which secret store the agent secrets live in: aws-secrets-manager (the default when unset, AWS Secrets Manager with the agent-secrets key in AWS KMS) or gcp-secret-manager (Google Cloud Secret Manager with the agent-secrets key in Cloud KMS, read under Application Default Credentials: on GKE, Workload Identity for the broker’s ServiceAccount, with no key file). It decides BROKER_SECRETS_PREFIX’s form and which key variable is required.
BROKER_SERVICES — — The registered services, whitespace-separated name=<service account> entries, such as legion-daemon=system:serviceaccount:legion:legion-worker. A name (lowercase letters, digits and hyphens, at most 64, not shared) is what a secret’s owner tag may name; the service account (system:serviceaccount:<namespace>:<name>) is the one the service’s pods run as, and each account is bound to one service only. A session is the service’s when a launcher logged in as the service enrolled it and it is a pod whose projected token proved it runs as that account. A machine login’s service name is the machine’s own claim, approved by whoever its login names, so the pod’s verified service account is what proves the service, and an account bound to two names would prove neither. A namespace’s default account (system:serviceaccount:<namespace>:default) is refused: every pod in the namespace whose spec names no account runs as it and can mount a token for the broker’s audience, so it proves nothing about which pods are the service’s. A secret owned by a listed service goes at once to those pods and is refused to every other session. Unset, no service is registered, and a secret whose owner tag names one is refused as owner-tag-malformed.
BROKER_SERVICE_LOGINS — — The ServiceAccounts that log in as a service with their own projected token (POST /v1/launcher-credentials/token), whitespace-separated <service>=<service account> entries, such as legion-daemon=system:serviceaccount:legion-system:legion-daemon-legion. Each account (system:serviceaccount:<namespace>:<name>, named exactly: there is no wildcard) logs in as the one service its entry names and is given once; one service may have several accounts, one per machine. A namespace’s default account is refused, since every pod there that names no account runs as it, and so is an account BROKER_SERVICES binds for a service’s pods, since every such pod holds a token for it. Keyed by account, each answering its service. A token whose subject is not listed is refused, and a credential an account’s login issued stops authenticating once its entry is gone. Needs BROKER_K8S_OIDC_ISSUER, whose tokens it trusts. Unset, no service logs in with a token.
BROKER_SWEEP_SECONDS 5 whole number from 1 to 60 How often the broker ends the enrollments whose lease lapsed and expires the pending requests and machine logins nobody decided in time.
BROKER_TRUSTED_PROXY_HEADER — — The request header (e.g. X-Forwarded-For) whose last entry the machine-login and secret-reread rate limiters take as the caller’s address. Unset, they use the connection’s own address, which is right only when nothing proxies the broker. Set it only when every request passes through your own reverse proxy, which appends that entry; otherwise a caller can forge the header and pick its own rate-limit bucket.
BROKER_UI_TOKEN
BROKER_UI_TOKEN_FILE
— — The bearer token Dispatch’s server presents on the broker’s approval routes, given directly or as a file whose trimmed contents win over the variable. Required. It vouches for the approver each decision names, so only Dispatch’s server may hold it.

The broker no longer reads these, and refuses to start while one is set, so a deployment that still sets one fails loudly instead of running on configuration that means nothing.

Variable Why
BROKER_ASK_POLL_SECONDS The broker holds no Dispatch credential
BROKER_DATABASE_PASSWORD The broker substitutes no password into BROKER_DATABASE_URL: on Amazon RDS it signs in by IAM token (https://sjawhar.github.io/legion/broker/operate/#signing-in-to-rds-by-iam-token), and any other database’s password goes in the URL itself
BROKER_DISPATCH_PROJECT The broker holds no Dispatch credential
BROKER_DISPATCH_TOKEN The broker holds no Dispatch credential
BROKER_DISPATCH_TOKEN_FILE The broker holds no Dispatch credential
BROKER_DISPATCH_URL The broker holds no Dispatch credential
BROKER_MAX_GRANT_SECONDS A request’s –ttl chooses a shorter grant lifetime, otherwise its grant lasts until the requesting session ends
BROKER_RULES_FILE The broker reads each secret’s owner and tier from the secret’s own tags under BROKER_SECRETS_PREFIX, so there is no rules file
BROKER_RULES_RELOAD_SECONDS The broker reads each secret’s owner and tier from the secret’s own tags under BROKER_SECRETS_PREFIX, so there is no rules file
BROKER_RULES_S3_URI The broker reads each secret’s owner and tier from the secret’s own tags under BROKER_SECRETS_PREFIX, so there is no rules file
BROKER_UI_ORIGIN Approval is by Dispatch login, so the broker checks no WebAuthn origin