Skip to content

Add Azure workload identity for Blob storage and PostgreSQL - #134

Merged
lukebuehler merged 3 commits into
mainfrom
azure-workload-id
Nov 5, 2026
Merged

lukebuehler merged 3 commits into
mainfrom
azure-workload-id

Conversation

@lukebuehler

Copy link
Copy Markdown
Contributor

Problem

Azure deployments that prohibit account keys, SAS tokens, and stored database passwords can't run Lightspeed today. The Runtime only authenticates to Blob storage with a key or SAS token, and both the Runtime and Platform only log in to PostgreSQL with a password.

What changes

Both now authenticate with a projected Kubernetes service account token, exchanged for a Microsoft Entra access token. No secret is stored, and rotation doesn't need a restart. Nothing changes unless a deployment selects it.

Setting Applies to Default
LIGHTSPEED_OBJECT_STORE_AZURE_AUTH_MODE=workload_identity Runtime Blob storage Inferred from the configured key or SAS token
LIGHTSPEED_POSTGRES_AUTH_MODE=workload_identity Runtime PostgreSQL password
LIGHTSPEED_PLATFORM_DATABASE_AUTH_MODE=workload_identity Platform PostgreSQL password

All three read AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE, and optionally AZURE_AUTHORITY_HOST. Those variables do nothing unless a setting selects this mode. A selected mode never falls back to another credential, and it rejects a conflicting key, SAS token, or URL password.

Blob storage uses object_store's own provider for the exchange and token caching. Two wrappers in store-pg add what the library doesn't provide:

  • Its Debug output prints the cached access token, so the provider is held behind one that shows only the file path.
  • Its errors include raw response bodies, so a connector reduces error bodies to identifiers such as AADSTS codes, storage error codes, and request IDs.

PostgreSQL sends the token as the login password. Database URLs must set sslmode=verify-full, because the token would otherwise go to an unverified server.

  • Runtime: sqlx has no per-connection credential callback, and the Blob provider's scope is fixed. store-pg therefore performs the exchange and updates the pool's connect options at half of each token's lifetime.
  • Platform: uses @azure/identity through pg's password function.

Changes that also affect password deployments

Pool settings now limit reconnects in every mode:

Runtime (sqlx) Platform (pg)
Connections kept when idle 2, opened up front Up to 2, kept once opened
Idle timeout 10 minutes (unchanged) 10 minutes, was 10 seconds
Maximum lifetime 1 hour, was 30 minutes 1 hour, was unlimited
Dead connections Checked before use (unchanged) TCP keepalive after 60 seconds

The Platform pool also gains an error listener. Without one, losing an idle connection ended the process.

Each process now holds connections longer, so check that the total stays under the server's max_connections.

Compatibility

No database migration, stored-object conversion, or API change. Existing paths, prefixes, and blob references stay valid. @azure/identity is a new Platform production dependency.

Checks

Passed:

  • Workspace Clippy with the CI flags, formatting, and release metadata.
  • store-pg tests and temporal-runtime config tests. Azure traffic runs through the real clients against a local TLS fake, covering wire content, token rotation, certificate and hostname rejection, failed authentication, and redaction.
  • A token rotation test against a real PostgreSQL role on the development stack. The old password was refused, new connections used the renewed token, and an open connection kept working.
  • TypeScript typecheck and consumer tests, Platform migrations, and release packaging.

Not run:

  • azure_blob_live and azure_postgres_live: these need a federated workload and a real Azure account, so nothing here has run against real Azure. Blob should be tested with Shared Key disabled.
  • The full workspace Rust test suite: left to CI.
  • The Platform's workload identity path over TLS: tests only confirm the settings reach pg.

Not included

  • Temporal stays on password authentication and will be handled separately.
  • The Runtime release the feature needs is not part of this PR.

🤖 Generated with Claude Code

lukebuehler and others added 3 commits November 5, 2026 14:11
The Runtime and Platform can now log in to Azure Database for PostgreSQL
with a Microsoft Entra token instead of a stored password. Each selects it
with its own setting, LIGHTSPEED_POSTGRES_AUTH_MODE or
LIGHTSPEED_PLATFORM_DATABASE_AUTH_MODE, and password remains the default.
The URL must name the identity, hold no password, and set
sslmode=verify-full, because the token is sent as a password inside TLS.

The Runtime exchanges the projected token itself and updates the pool's
connect options at half of each token's lifetime. The Platform uses
@azure/identity through pg's password function. Temporal stays on password
authentication and is planned separately.

Pool settings now limit reconnects in both modes. The Platform pool also
gains an error listener, because losing an idle connection previously ended
the process, and TCP keepalive, because pg does not check connections before
handing them out.

Verified with unit tests, a token rotation test against a real PostgreSQL
role, and the Platform migration and release packaging checks. Not yet run
against a real Azure server; azure_postgres_live covers that from a federated
workload.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: lb <542828+lukebuehler@users.noreply.github.com>
Bump the product version and add release notes for workload identity and the
connection pool changes. workflow-compat.sh v0.6.0 reports restart-in-place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: lb <542828+lukebuehler@users.noreply.github.com>
@lukebuehler
lukebuehler merged commit a78d422 into main Nov 5, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant