iron-control is the control plane for iron-proxy, a forwarding proxy that adds credentials to outbound HTTP requests. Applications route their traffic through iron-proxy and never hold the secrets themselves. iron-control stores the credentials, decides which proxy may use which credential on which requests, and hands each proxy its configuration.
It is a Rails application backed by Postgres. It provides a JSON API, an operator console, and encryption at rest for the secrets it stores.
- Stores credentials. Each secret is a typed record. The value is either kept inline and encrypted, or pulled from an external store such as AWS Secrets Manager, AWS SSM, 1Password, or an environment variable. The supported kinds are static secrets, GCP service-account auth, AWS SigV4 auth, OAuth tokens, Postgres connection strings, and HMAC signing keys.
- Controls who can use them. A principal is an identity that a proxy runs as. A role groups credentials so they can be assigned together. A grant gives one credential to a principal or a role. A principal can use its own grants plus the grants of every role it has.
- Limits where they apply. Each grant has request rules for host, methods, and paths, so a credential is only added to the requests it is meant for.
- Configures proxies. A proxy registers with
iron-control, gets assigned a principal, and callsPOST /api/v1/proxy/syncto fetch its configuration. The response includes a config hash that works like an ETag, so a proxy that already has the current config gets an empty response. - Issues short-lived tokens. For
token_brokercredentials,iron-controlmints and rotates the access token itself and sends only the token to the proxy. The underlying credential never leaves the control plane.
operator ──▶ console / JSON API ──▶ iron-control ──▶ Postgres (encrypted secrets)
▲
│ POST /api/v1/proxy/sync (iprx_ token)
│
application ──▶ iron-proxy ──────────────┘
│
└──▶ upstream APIs (credentials added per request rules)
Operators manage credentials, principals, roles, and grants through the API or the console. Each iron-proxy instance signs in with its own token, fetches the configuration for its assigned principal, and adds the granted credentials to matching outbound requests.
iron-control requires an authenticated user and API key before any API endpoint will respond. To bootstrap a fresh deployment without a console, set the following environment variables on startup:
| Variable | Required | Description |
|---|---|---|
IRON_CONTROL_INITIAL_USER_EMAIL |
yes | Email for the initial user. |
IRON_CONTROL_INITIAL_USER_PASSWORD |
yes | Password for the initial user (minimum 12 characters). |
IRON_CONTROL_INITIAL_API_KEY |
no | Plaintext API key for the initial user. Must match iak_ followed by 64 lowercase hex characters (a 32-byte hex string). If omitted, a token is generated and logged once at startup. |
Behavior:
- Bootstrap runs after Rails initialization on every boot, but is a no-op if any user already exists. It is safe to leave the env vars set across rolling restarts.
- If
IRON_CONTROL_INITIAL_USER_EMAILis set withoutIRON_CONTROL_INITIAL_USER_PASSWORD, the process exits with a clear error. - Concurrent pods racing the first boot are serialized with a Postgres advisory lock; exactly one user is created.
When deploying to Kubernetes, source these values from a Secret, not from a ConfigMap.
The operator console always supports email and password sign-in. To add Google or Slack SSO buttons to the login page, configure an OAuth/OIDC client with the provider and set the matching client credentials in the environment. A provider is shown only when both its client ID and client secret are present.
| Variable | Required | Description |
|---|---|---|
IRON_CONTROL_PUBLIC_URL |
recommended | Public origin for this deployment, for example https://control.example.com. Use this when iron-control runs behind a proxy or load balancer whose internal host does not match the public URL. |
IRON_CONTROL_GOOGLE_CLIENT_ID |
for Google | Google OAuth client ID for console login. |
IRON_CONTROL_GOOGLE_CLIENT_SECRET |
for Google | Google OAuth client secret for console login. |
IRON_CONTROL_SLACK_CLIENT_ID |
for Slack | Slack OpenID Connect client ID for console login. |
IRON_CONTROL_SLACK_CLIENT_SECRET |
for Slack | Slack OpenID Connect client secret for console login. |
IRON_CONTROL_BOOTSTRAP_ADMINS |
no | Comma- or whitespace-separated email allowlist. Matching users become active admins on first SSO login. Other new SSO users are created as pending users. |
Register these callback URLs with the provider:
- Google:
<IRON_CONTROL_PUBLIC_URL>/auth/google/callback - Slack:
<IRON_CONTROL_PUBLIC_URL>/auth/slack/callback
Both providers request the openid, email, and profile scopes. Client credentials may also be stored in Rails credentials under console_auth.<provider>.client_id and console_auth.<provider>.client_secret, but environment variables take precedence.
Google OAuth consent apps for broker credentials are configured separately in the console under OAuth Apps. Those app callbacks use /oauth/<slug>/callback and currently support Google only; Slack support here applies to operator console sign-in.
iron-control uses ActiveRecord encryption to protect secrets stored in the control plane (for example, the control_plane secret source type). The following environment variables configure the encryption keys:
| Variable | Required | Description |
|---|---|---|
IRON_CONTROL_AR_ENCRYPTION_PRIMARY_KEY |
yes (in production) | Primary key used for non-deterministic encryption. |
IRON_CONTROL_AR_ENCRYPTION_DETERMINISTIC_KEY |
yes (in production) | Key used for deterministic encryption. |
IRON_CONTROL_AR_ENCRYPTION_KEY_DERIVATION_SALT |
yes (in production) | Salt used to derive per-attribute keys. |
Generate suitable values with bin/rails db:encryption:init and store them in your secret manager. In production, the process refuses to boot if any of the three are missing. In development and test, fixed fallback values are used so the suite runs without configuration.
Rotating any of these keys makes previously encrypted data unreadable. Treat them as long-lived secrets and back them up alongside other production credentials.
iron-control exposes a JSON API under /api/v1. All resource endpoints authenticate with an API key sent as a bearer token (Authorization: Bearer iak_...); the one exception is POST /api/v1/proxy/sync, which iron-proxy instances call with a proxy bearer token.
See docs/API.md for the full reference: authentication, request/response conventions, pagination, error formats, the shared secret-source and request-rule shapes, and detailed payloads for every endpoint (static secrets, GCP auth secrets, OAuth token secrets, principals, roles, grants, API keys, proxies, and proxy sync).