Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions content/self-hosting/configuration/hardening.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
title: Hardening self-hosted Langfuse
description: Operator-facing security knobs for self-hosted Langfuse — restrict sign-ups, enforce SSO, limit outbound URLs, and tighten admin access.
label: "Version: v3"
sidebarTitle: "Hardening"
---

# Hardening

Langfuse ships with safe defaults and is penetration-tested for the same configuration that runs on [Langfuse Cloud](/security). This guide collects the additional operator-facing knobs that self-hosters typically want to turn on for production: locking down sign-ups, enforcing SSO, restricting which destinations user-configured integrations may reach, and tightening admin surfaces.

For related operational guides, see [Authentication and SSO](/self-hosting/security/authentication-and-sso), [Networking](/self-hosting/security/networking), [Encryption](/self-hosting/configuration/encryption), and [Deployment Strategies](/self-hosting/security/deployment-strategies).

## Restrict who can sign in [#access]

By default, anyone who can reach the Langfuse web container can sign up with email and password. Pick one of the following profiles before exposing the instance.

### Disable open sign-up [#disable-signup]

`AUTH_DISABLE_SIGNUP=true` blocks any new account creation, including users who have an open project invite but no account yet. Combine with [headless initialization](/self-hosting/administration/headless-initialization) or invite existing users from inside the app.

### Force SSO for specific email domains [#sso-enforcement]

`AUTH_DOMAINS_WITH_SSO_ENFORCEMENT=acme.com,corp.example` rejects email/password sign-in (including the password-reset OTP path) for the listed domains and redirects those users to your configured SSO provider. Use this when only part of your user base is on SSO.

### Require email verification on signup [#email-verification]

`AUTH_EMAIL_VERIFICATION_REQUIRED=true` adds a one-time-password step before a new email/password user can set their password. SSO sign-in is unaffected. Requires [transactional emails](/self-hosting/configuration/transactional-emails) to be configured. See [Email verification on signup](/self-hosting/security/authentication-and-sso#email-verification-on-signup) for the full flow.

### Session lifetime [#session-lifetime]

`AUTH_SESSION_MAX_AGE` controls how long a signed-in session (JWT) stays valid, in minutes. Default is `43200` (30 days); the minimum is 5 minutes because the front-end refreshes its session every 5 minutes. Lower this for shared or kiosk environments.

### Disable email/password entirely [#disable-password]

If every user signs in via SSO, set `AUTH_DISABLE_USERNAME_PASSWORD=true`. Account linking can be enabled per-provider with `AUTH_<PROVIDER>_ALLOW_ACCOUNT_LINKING=true` — only enable it when your IdP guarantees verified emails.

## Organization and project-level access [#org-access]

Langfuse uses two role layers — organization (`OWNER`, `ADMIN`, `MEMBER`, `VIEWER`, `NONE`) and project. The defaults are tight; the items below are worth reviewing.

- **Organization API keys** can only be managed by `OWNER`. `ADMIN` cannot mint org-scoped keys, so an ADMIN cannot self-escalate via the SCIM API.
- **Last-owner protection** — SCIM `DELETE` / `PUT active:false` / `PATCH active:false` cannot remove the final remaining `OWNER` of an organization. The same invariant has long applied to the tRPC path.
- **Org-admin REST endpoints** (`/api/public/organizations/{memberships,projects,apiKeys}`) are rate-limited per scope. A compromised org-scoped key cannot be used to enumerate users at scale.
- **LLM connection test endpoints** require `llmApiKeys:create` or `llmApiKeys:update`; lower-privilege roles cannot exercise them.

See [RBAC](/docs/administration/rbac) for the full role and scope matrix and [SCIM & Organization API](/docs/administration/scim-and-org-api) for automated provisioning.

## Outbound URL allowlists (SSRF defense) [#outbound-url-allowlists]

Several features let project members configure a URL that the Langfuse worker calls on their behalf — LLM API base URLs, webhook targets, image URLs in trace inputs, and blob storage integration endpoints. Langfuse rejects URLs whose resolved IPs fall into private, link-local, loopback, or special-use ranges (including NAT64 and 6to4 IPv6 ranges), strips credentials passed via URL userinfo, and re-validates the IP at request time and on every redirect hop to defend against DNS rebinding.

On Langfuse Cloud this is always strict. On self-hosted instances, the same defaults apply, with explicit allowlists for internal services:

| Use case | Allowlist variables |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| User-configured LLM connections | `LANGFUSE_LLM_CONNECTION_WHITELISTED_HOST`<br/>`LANGFUSE_LLM_CONNECTION_WHITELISTED_IPS`<br/>`LANGFUSE_LLM_CONNECTION_WHITELISTED_IP_SEGMENTS` |
| User-configured blob storage integrations | `LANGFUSE_BLOB_STORAGE_ENDPOINT_WHITELISTED_HOST`<br/>`LANGFUSE_BLOB_STORAGE_ENDPOINT_WHITELISTED_IPS`<br/>`LANGFUSE_BLOB_STORAGE_ENDPOINT_WHITELISTED_IP_SEGMENTS` |

Each variable accepts a comma-separated list. Hostnames in `_HOST` short-circuit the IP check entirely; `_IPS` / `_IP_SEGMENTS` are matched against every IP a hostname resolves to. Set these only for trusted internal endpoints (your in-cluster MinIO, an internal LLM gateway, …) — values configured here defeat the SSRF protections for those targets. Blob storage validation activates only once one of these vars is set on a self-hosted instance; this default will change to strict in a future major release.

Webhook URLs follow the same rules and additionally drop sensitive headers (`Authorization`, `Cookie`, `Proxy-Authorization`, `x-langfuse-signature`) on cross-origin redirects. Provide credentials via headers configured in the webhook UI rather than via URL userinfo — URLs with embedded `user:password@` are rejected.

## Admin API [#admin-api]

The self-hosted-only [Instance Management API](/self-hosting/administration/instance-management-api) is gated by `ADMIN_API_KEY`. When you enable it:

- Pick a high-entropy random value (≥ 32 bytes). Authentication uses a constant-time comparison, so the key length itself is not leaked, but short keys remain trivially brute-forceable.
- Treat the key as you would a root credential — restrict it to a small set of operators, rotate it on personnel changes, and never expose it to the browser or to less-privileged services.
- Do not expose admin endpoints to the public internet. Bind them to an internal load balancer or restrict them with a reverse proxy.
- Langfuse Cloud blocks the admin API entirely; nothing in this section applies there.

## SSO configuration [#sso]

A few SSO-specific knobs worth knowing about:

- **Multi-tenant SSO config cache TTL** is 10 minutes — changes you make through the self-service flow propagate to all instances within that window. This affects Cloud / multi-tenant deployments only.
- **DNS-verified domains** — for self-service SSO configuration, the configuring organization must first prove control over the email domain via a TXT record. This prevents cross-tenant hijacking via lookalike email domains.
- **Issuer validation** is enforced for GitHub OAuth and custom OIDC providers; misconfigured `AUTH_<PROVIDER>_ISSUER` values fail at save / sign-in rather than silently accepting tokens from the wrong issuer.
- Use the ID token signing algorithm configuration to align the expected algorithm with your IdP if it rejects the default.

See the full reference in [Authentication and SSO → Additional configuration](/self-hosting/security/authentication-and-sso#additional-configuration).

## Network exposure [#network-exposure]

Only the `langfuse/langfuse` (web) container needs to be reachable by users, the SDKs, and your application code. Langfuse is designed to be exposed publicly, but for high-security environments you can:

- Terminate TLS at a load balancer in front of the web container (see [Encryption](/self-hosting/configuration/encryption)).
- Put the web container behind a VPN, internal load balancer, or zero-trust proxy.
- Peer your application VPCs with the Langfuse VPC for private SDK ingestion traffic. Worker, Postgres, ClickHouse, Redis, and blob storage should never be reachable from the public internet.

See [Networking](/self-hosting/security/networking) for the full architecture diagram and per-component exposure recommendations.

## Data protection [#data]

- **Encryption at rest** — Postgres and ClickHouse should run with disk encryption. Configure [server-side encryption](/self-hosting/deployment/infrastructure/blobstorage#using-aws-kms-encryption) on the event-upload and media buckets.
- **Sensitive integration secrets** (LLM API keys, blob storage credentials, SSO client secrets, webhook secrets) are encrypted at the application layer using `ENCRYPTION_KEY`. Rotate the key per the [Encryption](/self-hosting/configuration/encryption) guide.
- **Data masking** — see [Data masking](/self-hosting/security/data-masking) to redact sensitive content from ingested traces before it lands in storage.
- **Data retention** — configure project-level [data retention](/docs/data-retention) policies to delete traces, observations, and media after N days.
- **Audit logs** — administrative actions, including blob storage integration validate/run-now and public blob-storage deletion, are recorded.

## Telemetry [#telemetry]

Langfuse self-hosted reports anonymized usage telemetry by default. Set `TELEMETRY_ENABLED=false` to opt out; see [Telemetry](/self-hosting/security/telemetry) for the exact fields collected.

## Reporting security issues [#disclosure]

If you discover a vulnerability, please report it via our [responsible disclosure program](/security/responsible-disclosure) rather than opening a public GitHub issue.
1 change: 1 addition & 0 deletions content/self-hosting/configuration/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"---Essential---",
"index",
"scaling",
"hardening",
"backups",
"---Advanced---",
"caching",
Expand Down
32 changes: 31 additions & 1 deletion content/self-hosting/deployment/infrastructure/blobstorage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ If you use versioned buckets, delete markers and non-current versions need to be

</Callout>

#### Using AWS KMS Encryption
#### Using AWS KMS Encryption [#using-aws-kms-encryption]

Amazon S3 provides server-side encryption (SSE) options to encrypt your data at rest.
Langfuse supports AWS KMS encryption for all S3 interactions through the following environment variables:
Expand Down Expand Up @@ -383,3 +383,33 @@ ALTER TABLE blob_storage_file_log MODIFY TTL created_at + INTERVAL <DAYS> DAY DE
Where `<DAYS>` matches the configured bucket lifecycle expiration period.

</Callout>

## Endpoint validation (SSRF defense) [#endpoint-validation]

In addition to the platform-level `LANGFUSE_S3_*` buckets configured via environment variables, project members can configure a blob storage **integration** in the Langfuse UI (Project Settings → Integrations → Blob Storage Export). Because that endpoint is user-supplied, Langfuse validates the host it points at — both when the integration is saved and again every time a connection is opened — to prevent the worker from being pointed at internal services (cloud metadata endpoints, link-local addresses, private RFC1918 / ULA ranges, NAT64/6to4 transition ranges) or being redirected there via DNS rebinding.

Validation behavior:

- **Save-time**: the endpoint hostname is resolved and rejected if any resolved IP falls into a private, link-local, loopback, or special-use range.
- **Connection-time**: the S3 SDK's HTTP handler and the Azure SDK's request policy re-validate the resolved IP just before each request, so a hostname whose DNS answer changes after save is still blocked.
- **Cloud**: validation is strict and cannot be relaxed.
- **Self-hosted**: validation activates when at least one of the allowlist environment variables below is configured. This keeps backward compatibility with internal MinIO and Azurite deployments on private networks. The default will become strict in a future major release.
- **GCS** and **OCI** are not supported as targets for the blob storage _integration_ (the native object storage integrations configured via environment variables are unaffected).

### Self-hosted allowlist environment variables

Set these on both the Langfuse Web and Worker containers if your internal blob storage endpoint resolves to a private address.

| Variable | Required / Default | Description |
| -------------------------------------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `LANGFUSE_BLOB_STORAGE_ENDPOINT_WHITELISTED_HOST` | | Comma-separated list of hostnames that bypass the IP-range check. Example: `minio.internal,storage.svc.cluster.local`. |
| `LANGFUSE_BLOB_STORAGE_ENDPOINT_WHITELISTED_IPS` | | Comma-separated list of individual IPs to allow. Example: `10.0.0.5,fd00::1`. |
| `LANGFUSE_BLOB_STORAGE_ENDPOINT_WHITELISTED_IP_SEGMENTS` | | Comma-separated list of CIDR ranges to allow. Example: `10.0.0.0/8,192.168.0.0/16`. |

These mirror the existing `LANGFUSE_LLM_CONNECTION_WHITELISTED_*` variables used for user-configured LLM connections — see [Hardening → Outbound URL allowlists](/self-hosting/configuration/hardening#outbound-url-allowlists).

<Callout type="info">

Azure container names must be 3–63 characters and contain only lowercase letters, digits, and hyphens. Names that don't meet this constraint are rejected at submission time.

</Callout>
2 changes: 1 addition & 1 deletion content/self-hosting/security/authentication-and-sso.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ https://<YOUR_LANGFUSE_URL>/auth/sso-initiate?provider=<PROVIDER>
- Use the `Redirect to app to initiate login (OIDC Compliant)` option in your identity provider's settings.
- IdP-initiated SSO is available on version `>=v3.126.0` of Langfuse.

## Additional configuration
## Additional configuration [#additional-configuration]

These are additional configuration variables. Replace `<PROVIDER>` with the provider name (e.g., `GOOGLE`, `GITHUB`, `AZURE_AD`, etc., see other variables of provider above).

Expand Down
Loading