This guide builds on Infrastructure Setup for Enterprise Apache Druid on Kubernetes – Building the Foundation, Installing a Production-Ready Apache Druid Cluster on Kubernetes — Part 2: Druid Deployment Preparation, and Apache Druid on Kubernetes: Production‑ready with TLS, MM‑less, Zookeeper‑less, GitOps. Here we focus on production-grade authentication and authorization patterns for Druid running on Kubernetes.
This post is part of a series: Infrastructure Setup for Enterprise Apache Druid on Kubernetes – Building the Foundation, Installing a Production-Ready Apache Druid Cluster on Kubernetes — Part 2: Druid Deployment Preparation, and Apache Druid on Kubernetes: Production-ready with TLS, MM‑less, Zookeeper‑less, GitOps. For conversational troubleshooting and query exploration, see Apache Druid MCP Server: Conversational AI for Time Series.
In this series on Apache Druid on Kubernetes
Related project: Apache Druid MCP Server: Conversational AI for Time Series
If you’re planning or running Apache Druid in production, we offer pragmatic Apache Druid consulting and support for time‑series projects—from architecture reviews and Kubernetes‑native deployments to performance tuning and production runbooks. If expert guidance would accelerate your roadmap, feel free to reach out.
Enterprise Druid deployments must protect both machine-to-machine traffic and human access to the web console and APIs. In practice, this means implementing multiple authenticators in a chain, using SSO (OIDC/OAuth 2.0) for people, and a metadata-backed internal authenticator/authorizer for service accounts. In this article, we walk through how to design a practical authentication chain with sensible fallbacks and priorities, add OAuth login via PAC4J (for example with Azure Active Directory), pair it with Druid’s Basic Security for RBAC and the Escalator for safe internal calls, and finally bootstrap credentials.
In short, combine an authenticatorChain such as ["db", "pac4j"] so that internal service accounts continue to work while humans log in via SSO. Remember that PAC4J only authenticates; perform authorization with the Druid Basic Authorizer (or another RBAC/PDP). Enable the druid-basic-security extension, configure metadata-backed authentication and authorization, and rely on the Escalator for safe cluster-internal calls. Keep initial admin and internal credentials in Kubernetes Secrets and serve only over TLS. For Azure AD, register an app with the redirect URI /druid-ext/druid-pac4j/callback and set the OIDC values in Druid accordingly.
Modern enterprises rarely rely on a single authentication mechanism. Druid supports an authentication chain where each authenticator is tried in order until one succeeds. In a typical setup you place an internal, metadata-backed Basic Authenticator first to handle service-to-service flows and system accounts, and you follow it with PAC4J (OIDC/OAuth) to authenticate human users via SSO. If the first authenticator fails and skip-on-failure is enabled, Druid proceeds to the next one; otherwise it stops early for performance and clarity. As a rule of thumb, put the most common and cheapest authenticator first, enable db.skipOnFailure for resiliency during brief metadata store hiccups, minimize external calls when an internal check could handle the request, and ensure each authenticator emits clear success/failure logs for auditing.
Example chain definition in common.runtime.properties:
# Two authenticators: internal (db) then web SSO (pac4j) druid.auth.authenticatorChain=["db","pac4j"]
Configuration checklist:
druid.auth.authenticatorChain=["db","pac4j"].druid.auth.authenticator.db.skipOnFailure=true for resiliency.We’ll define each authenticator in the following sections.
Internal authentication covers service-to-service calls, technical system accounts and automation (for example, ingestion tasks and CI/CD). It typically uses Basic Auth against metadata-backed users and is best paired with the Druid Basic Authorizer for RBAC. External authentication is for humans who access the Druid console and APIs via SSO using OIDC/OAuth with PAC4J, delegating identity to providers such as Azure Active Directory or Google. In practice, Basic credentials live in the Druid metadata store and work as long as that store is available, while PAC4J relies on OAuth/OIDC tokens and a session established with your IdP. Regardless of how users authenticate, authorization is enforced by Druid’s Authorizer.
PAC4J is the Druid extension that enables OIDC/OAuth login for the web console and APIs. It handles the OAuth flow, redirects users to your IdP, and creates a session on return. Important: PAC4J authenticates, it does not authorize.
Reference configuration (adapted from repository pac4j.md):
# PAC4J web user authenticator (OIDC) druid.auth.authenticator.pac4j.name=pac4j druid.auth.authenticator.pac4j.type=pac4j # PAC4J does not enforce authorization; pair with Basic Authorizer for RBAC druid.auth.authenticator.pac4j.authorizerName=pac4j # Typical OIDC scopes; include email for consistent identity mapping druid.auth.pac4j.oidc.scope=openid profile email # Secrets (store in Kubernetes Secrets and mount as env) druid.auth.pac4j.cookiePassphrase=<STRONG_RANDOM> druid.auth.pac4j.oidc.clientID=<CLIENT_ID> druid.auth.pac4j.oidc.clientSecret=<CLIENT_SECRET> # For Azure AD (replace <TENANT>) druid.auth.pac4j.oidc.discoveryURI=https://login.microsoftonline.com/<TENANT>/.well-known/openid-configuration
Notes: The callback path is handled at /druid-ext/druid-pac4j/callback by the extension. Use a long, random cookiePassphrase and rotate it on a schedule. Prefer group or email claims from the identity provider for stable user identification. Because PAC4J sessions are stateful, set reasonable TTLs and enforce HTTPS.
Setup checklist:
druid-pac4j extension to your loadList (as covered in Part 3).discoveryURI, clientID, clientSecret and a strong cookiePassphrase via SOPS (see Part 1)./druid-ext/druid-pac4j/callback.For Azure AD, register an application, configure redirect URIs, and grant appropriate permissions.
You can use Terraform to deploy the Azure AD Settings:
locals {
druid_enterprise_app_name = "k8siunera-auth-druid"
}
module "k8siunera-analyticsinternal-druid" {
source = "../modules/oauth-aad"
username = local.druid_enterprise_app_name
homepage_url = "https://druid.example.com"
redirect_uris = ["https://druid.example.com/druid-ext/druid-pac4j/callback"]
}
What the module provisions (excerpts from terraform/modules/oauth-aad):
# Application with Web platform configured
resource "azuread_application" "default" {
display_name = var.username
web {
homepage_url = var.homepage_url
redirect_uris = var.redirect_uris
}
# Request standard OIDC scopes on Microsoft Graph
required_resource_access {
resource_app_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph
dynamic "resource_access" {
for_each = var.required_resource_access_scopes
content {
id = resource_access.value
type = "Scope"
}
}
}
}
Reference: Terraform azuread_application resource docs: https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application
Required resource access scopes in Azure AD are:
# Defaults include the standard OIDC scopes on Microsoft Graph
variable "required_resource_access_scopes" {
default = [
"37f7f235-527c-4136-accd-4a02d197296e", # openid
"64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0", # email
"14dad69e-099b-42c9-810b-d002981feec1", # profile
]
}
Azure AD portal steps: In the Azure portal, register a new application (Quickstart: Register an application) and choose the supported account types (single-tenant or multi-tenant) that fit your organization. Add a Web redirect URI (Add redirect URI) pointing to https://druid.example.com/druid-ext/druid-pac4j/callback. Generate a client secret and store it in a Kubernetes Secret, mapping it to druid.auth.pac4j.oidc.clientSecret. Capture the Application (client) ID and Directory (tenant) ID and map them to clientID and discoveryURI respectively. Configure the openid, profile and email scopes and, if required, include group claims. If you enforce Conditional Access, confirm the policies apply as intended and ensure the Druid cluster has egress to login.microsoftonline.com.
Reference these in your Druid runtime properties (via env mapping), for example:
# PAC4J OIDC (values supplied via env from the Secret above) druid.auth.pac4j.oidc.clientID=<DRUID_AUTH_PAC4J_OIDC_CLIENT_ID> druid.auth.pac4j.oidc.clientSecret=<DRUID_AUTH_PAC4J_OIDC_CLIENT_SECRET> druid.auth.pac4j.oidc.discoveryURI=<DRUID_AUTH_PAC4J_OIDC_DISCOVERY_URI> druid.auth.pac4j.cookiePassphrase=<DRUID_AUTH_PAC4J_COOKIEPASSPHRASE>
Operational notes:
Multi-tenancy notes: The discoveryURI is tenant-specific; for organizational tenants, prefer a fixed tenant to preserve policy control. If you depend on group membership for permissions, sync groups to Druid roles via the management API or through your own automation.
Troubleshooting: If you encounter an HTTP 400 at the callback, verify that the redirect URI matches exactly and that HTTPS is enforced. For login loops, check the cookiePassphrase and confirm your reverse proxy sets X-Forwarded-Proto=https.
Security hardening in production reads better as a set of habits than a checklist. Expose Druid only over HTTPS, disable plaintext ports and enforce HSTS at the ingress. Keep secrets in Kubernetes Secrets or wire in an External Secrets Operator, and rotate client secrets and cookie passphrases on a schedule. Apply least‑privilege RBAC—most humans should have read‑only access while CI/CD and operators receive narrowly scoped elevated rights. Turn on audit logging for authentication and authorization actions and forward the logs to your SIEM, then monitor for spikes in failures, escalator errors or sudden permission changes. Finally, align Druid roles with your data domains and master‑data ownership model (see this article).
Treat Kubernetes as a first-class security boundary. Use NetworkPolicies to isolate Druid pods by role and allow only the egress required for your identity provider and metadata database. Lock down Kubernetes RBAC so that only GitOps controllers (not humans) can patch the ConfigMaps and Secrets that shape Druid’s behavior. Apply Pod Security Standards controls to run as non‑root, drop unnecessary capabilities and, where possible, use a read‑only root filesystem. For a reference point, compare deployment options and defaults with our Helm charts at https://iunera.github.io/helm-charts/.
The druid-basic-security extension provides an internal, metadata-backed Basic Authenticator for service accounts, a Basic Authorizer that applies Druid RBAC over resources (DATASOURCE, STATE, CONFIG and EXTERNAL), and an Escalator used to perform privileged cluster‑internal requests safely.
Enable extension (already shown in Part 3 loadList) and configure properties:
# Enable both internal (db) and SSO (pac4j) authenticators druid.auth.authenticatorChain=["db","pac4j"] # Metadata-backed Basic Authenticator (name: db) druid.auth.authenticator.db.type=basic druid.auth.authenticator.db.name=db # Recommended for resiliency: continue to next authenticator if DB temporarily unavailable druid.auth.authenticator.db.skipOnFailure=true # Basic Authorizer (name: db) druid.auth.authorizer.db.type=basic druid.auth.authorizer.db.name=db # Escalator: used for internal system calls druid.escalator.type=basic druid.escalator.internalClientUsername=<internal-client> druid.escalator.internalClientPassword=<strong-secret> druid.escalator.authorizerName=db
Bootstrap checklist:
initialAdminPassword and internalClientPassword via Kubernetes Secrets.type=basic, authorizerName=db, and provide internalClientUsername/password.Bootstrap initial admin and internal client via environment (use Secrets in Kubernetes):
env:
- name: druid_auth_authenticator_db_initialAdminPassword
valueFrom:
secretKeyRef:
name: druid-initial-secrets
key: initialAdminPassword
- name: druid_escalator_internalClientPassword
valueFrom:
secretKeyRef:
name: druid-initial-secrets
key: internalClientPassword
After the cluster starts, log in as the initial admin and create users/roles via the management API. Examples adapted from the Postman collection in this repository:
# Create a role
curl -s -X POST \
-H "Content-Type: application/json" \
-u admin:*** \
https://<COORDINATOR_OR_OVERLORD>/druid-ext/basic-security/authorization/db/db/roles/readRole
# List roles
curl -s -u admin:*** \
https://<COORDINATOR_OR_OVERLORD>/druid-ext/basic-security/authorization/db/db/roles/
# Grant permissions to a role
curl -s -X POST -H "Content-Type: application/json" -u admin:*** \
https://<COORDINATOR_OR_OVERLORD>/druid-ext/basic-security/authorization/db/db/roles/readRole/permissions \
-d '[
{"resource": {"name": ".*", "type": "DATASOURCE"}, "action": "READ"},
{"resource": {"name": ".*", "type": "STATE"}, "action": "READ"},
{"resource": {"name": ".*", "type": "CONFIG"}, "action": "READ"},
{"resource": {"name": "EXTERNAL", "type": "EXTERNAL"}, "action": "READ"}
]'
# Create a user and assign a role
curl -s -X POST -H "Content-Type: application/json" -u admin:*** \
https://<COORDINATOR_OR_OVERLORD>/druid-ext/basic-security/authentication/db/users/alice
curl -s -X POST -H "Content-Type: application/json" -u admin:*** \
https://<COORDINATOR_OR_OVERLORD>/druid-ext/basic-security/authorization/db/db/users/alice/roles \
-d '["readRole"]'
Rotation procedure: After bootstrap, remove the initial password environment properties from your manifests. Create new secrets with rotated admin and internal credentials, then roll the pods so they pick up the updated values. Finally, verify access with /status/self and other protected endpoints.
Security hardening: Harden the deployment by enforcing HTTPS-only, disabling plaintext ports and ensuring the reverse proxy sets X-Forwarded-Proto. Limit the escalator account to internal use, store its secret in a dedicated Secret and reference it by name. Finally, enable audit logs on authentication and authorization events.
/druid-ext/druid-pac4j/callback).X-Forwarded-Proto: https.druid.auth.pac4j.cookiePassphrase and keep it identical across all pods; rotate carefully (existing sessions become invalid).cookiePassphrase across nodes can invalidate sessions; ensure it’s uniform via a single Secret./druid-ext/druid-pac4j/callback isn’t altered or blocked by the proxy.Authorization header (Basic for service accounts) or establish a PAC4J session for the browser.druid.auth.authenticatorChain=["db","pac4j"]druid.auth.authorizers=["db","pac4j"] with db as the RBAC authorizer, pac4j typically allowAll.GET /status/self to see the recognized identity and authorizer.druid.auth.authenticator.db.skipOnFailure=true so requests can fall through to the next authenticator in brief outages.druid.escalator.type=basicdruid.escalator.authorizerName=dbdruid.escalator.internalClientUsername and druid.escalator.internalClientPassword (from Secrets) and corresponding user exists.druid.enablePlaintextPort=false, update all health probes and clients to use HTTPS (see component ports in Part 3).simple-client-sslcontext extension) and set X-Forwarded-* headers at the proxy.druid.auth.authenticator.db.initialAdminPassword is only used on first startup; after creating users, rotate and remove it from manifests.Check identity:
curl -sk https://<ROUTER_OR_COORDINATOR>/status/self -u <user>:<pass>
List roles and users via the Basic Security endpoints to validate RBAC configuration.
No. You can operate with Basic Auth (metadata-backed) for service accounts and add pac4j later for human SSO. This guide shows how to run both together via the authenticator chain.
No. pac4j authenticates only. Use the Druid Basic Authorizer (RBAC) or another PDP for authorization.
Use druid.auth.authenticatorChain=["db","pac4j"]. Put db first for fast internal calls; pac4j handles browser SSO. Authorize via the Basic Authorizer (name db).
Druid doesn’t automatically map groups. Read group/email claims from the IdP and sync them to Druid roles via the management API or your automation pipeline.
In Kubernetes Secrets managed via GitOps/secret encryption (e.g., SOPS). Mount them as environment variables as shown in this article. Rotate regularly.
Existing PAC4J sessions become invalid. Perform a rolling restart and expect users to re-authenticate.
With db.skipOnFailure=true, requests can fall through to pac4j during brief outages. Internal Basic-auth users may be impacted until the store recovers.
Call GET /status/self (via Router/Coordinator) with your credentials or PAC4J session; it returns the authenticated identity and authorizer information.
Combining PAC4J for user SSO with the Druid Basic Security extension for internal auth and RBAC delivers a pragmatic, enterprise-grade security posture. The authenticator chain pattern lets you support both service accounts and human users safely. By enforcing TLS, automating secret rotation, and integrating Kubernetes-native controls, your Druid cluster is well-positioned for secure, compliant analytics at scale.
Finally, if you’re building advanced AI assistants over time-series with Druid, treat identity and authorization as first-class architecture concerns. See our Druid MCP Server project for ideas and guardrails: https://www.iunera.com/kraken/projects/apache-druid-mcp-server-conversational-ai-for-time-series/
For deeper dives, see the Druid Basic Security documentation (Authenticator/Authorizer/Escalator): https://druid.apache.org/docs/latest/development/extensions-core/druid-basic-security/, the operations guide on user authentication: https://druid.apache.org/docs/latest/operations/security-user-auth/, a gist with Management API examples: https://gist.github.com/davidagee/c0c839cd23f047b838e8a3ea73320346, and our Helm charts for comparing defaults and values: https://iunera.github.io/helm-charts/.
Everyone loves talking about what uncensored AI models can do. Fewer interruptions. No unnecessary refusals.… Read More
If you've spent any time in AI developer communities lately, you've probably seen the same… Read More
Most teams evaluating uncensored models spend a lot of time on model selection. They compare… Read More
There's a pattern that plays out almost every time a team switches from an aligned… Read More
Uncensored language models are having a moment. Developers are frustrated. Researchers are annoyed. Enterprise teams… Read More
When people talk about running AI privately, two arguments dominate the conversation , data privacy… Read More