BizFirst Observe
Loki Access Controls
Loki's multi-tenancy feature isolates each tenant's logs at the storage level. When auth_enabled: true is set, Loki requires an X-Scope-OrgID header on every request — ensuring that each tenant can only query their own log streams, regardless of how Grafana dashboards are configured.
Enabling Loki Multi-Tenancy
# loki-config.yaml — enable multi-tenancy
auth_enabled: true # REQUIRED for tenant isolation in production
# With auth_enabled: true:
# - Every ingest request MUST include X-Scope-OrgID: <tenant-id>
# - Every query request MUST include X-Scope-OrgID: <tenant-id>
# - Loki stores each tenant's chunks in a separate keyspace
# - Tenant A cannot query Tenant B's logs — enforced at the Loki storage layer
OTel Collector — Sending Tenant ID to Loki
# otel-collector-config.yaml — pass tenant ID as Loki header
exporters:
loki:
endpoint: http://loki:3100/loki/api/v1/push
headers:
# Forward the tenant ID from the OTel resource attribute:
X-Scope-OrgID: "${TENANT_ID}"
# For multi-tenant deployments where one collector handles multiple tenants:
# Use the routing connector to route each tenant's logs to a separate Loki pipeline
# with the correct X-Scope-OrgID header.
# Alternative: Use the tenant processor:
processors:
tenant:
from_attribute: tenant.id # Read from OTel resource attribute
# Loki exporter automatically uses this as X-Scope-OrgID
Grafana — Tenant-Scoped Loki Data Source
# grafana-provisioning/datasources/loki.yaml — per-tenant data source
datasources:
# Platform-wide Loki data source (no tenant scope — admin use only)
- name: Loki-All
type: loki
url: http://loki:3100
# No X-Scope-OrgID — used by platform admins only (Admin role required)
# Tenant-specific data source (restricted to one tenant's data)
- name: Loki-TenantABC
type: loki
url: http://loki:3100
jsonData:
httpHeaderName1: "X-Scope-OrgID"
secureJsonData:
httpHeaderValue1: "tenant-abc"
# Assign to the tenant-abc team in Grafana access control
# This data source only returns logs for tenant-abc
Label-Based Stream Isolation
# Even with auth_enabled: true, validate that your label strategy enforces isolation.
# All BizFirstGO log streams must include tenant_id as a label:
# Correct (tenant_id label present):
{job="processengine", tenant_id="tenant-abc", environment="production"}
# Incorrect (no tenant_id label — all tenants' logs visible to anyone querying this stream):
{job="processengine", environment="production"}
# Verify tenant_id is always present by checking Loki label values:
curl -G "http://localhost:3100/loki/api/v1/label/tenant_id/values" \
-H "X-Scope-OrgID: tenant-abc"
# Should only return "tenant-abc" — never other tenant IDs
auth_enabled: false Is Only For Development
With auth_enabled: false (the default), Loki uses a fixed "fake" tenant — all logs are stored together and any query returns data from all tenants. This is fine for single-tenant development but is a critical security misconfiguration in production multi-tenant deployments. Always set auth_enabled: true in production.