Security

This section documents the current Solr line (10.0; 9.10.x the maintained 9.x branch), written and verified against the Apache Solr Reference Guide. No specific patch version is pinned. Some capabilities (the Solr Operator on Kubernetes, the package-manager ecosystem, Learning To Rank model training, and expert plugin development) are linked, not documented in depth.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Solr ships unsecured by default — any client that can reach the HTTP port can read, write, or administer a cluster. Securing it means layering four independent pieces: an authentication plugin that resolves a request to a principal, an authorization plugin that decides what that principal may do, audit logging of the outcome, and TLS on every connection so credentials and data are not sent in the clear. The first three are all declared in one file, security.json, stored in ZooKeeper and shared by every node in the cluster; TLS and ZooKeeper’s own access control are configured separately. See Securing Solr for the full checklist.

security.json

security.json carries up to three top-level sections — authentication, authorization, and auditlogging — and lives as a znode at /security.json in ZooKeeper, not as a file on disk, so every node in the cluster reads the same configuration and a change to it takes effect cluster-wide without a restart. See Configuring Authentication and Authorization.

{
  "authentication": {
    "class": "solr.BasicAuthPlugin",
    "blockUnknown": true,
    "credentials": {
      "admin": "CHANGE_ME_HASH=CHANGE_ME_SALT="
    }
  },
  "authorization": {
    "class": "solr.RuleBasedAuthorizationPlugin",
    "user-role": { "admin": "admin" },
    "permissions": [
      { "name": "security-edit", "role": "admin" }
    ]
  }
}
# Upload security.json to ZooKeeper before any node starts, or push a live update
# through the /admin/authentication and /admin/authorization endpoints (see below).
bin/solr zk cp security.json zk:/security.json -z localhost:2181/solr

Push a fresh security.json into a running cluster and every node picks it up automatically because they all watch the same znode — there is no per-node config to restart. A cluster with no authorization section but a configured authentication plugin lets any authenticated user do anything; add a RuleBasedAuthorizationPlugin section from the start if that gap matters.

Authentication plugins

An authentication plugin resolves each incoming request to a principal (or rejects it) before Solr’s request-handler pipeline runs. Only one plugin is active at a time, chosen by the authentication.class value in security.json; a solr.MultiAuthPlugin wrapper can combine two of them (for example Basic for humans and JWT for services) when a single scheme is not enough.

Basic

solr.BasicAuthPlugin checks an HTTP Basic Authorization header against a map of username to salted password hash stored directly in security.json. It is the simplest plugin to stand up and the one shown above; manage users through the /admin/authentication API rather than hand-editing the hash.

# Add or update a user (prompts for the password, or pass -credentials user:pass).
bin/solr auth enable -type basicAuth -credentials admin:CHANGE_ME_PASSWORD -z localhost:2181/solr

# Same thing over the HTTP API once basic auth is already enabled.
curl -u admin:CHANGE_ME_PASSWORD -X POST http://localhost:8983/solr/admin/authentication \
  -H 'Content-Type: application/json' \
  -d '{ "set-user": { "app_reader": "CHANGE_ME_PASSWORD" } }'

JWT / OIDC

solr.JWTAuthPlugin accepts a bearer JWT and validates its signature against an external identity provider’s JWKS endpoint, so Solr never sees a password — the typical case is an OpenID Connect IdP issuing the token after an interactive login. It ships in the separate jwt-auth Solr module, which must be enabled before the plugin can be referenced from security.json.

{
  "authentication": {
    "class": "solr.JWTAuthPlugin",
    "blockUnknown": true,
    "jwkUrl": "https://idp.example.com/.well-known/jwks.json",
    "iss": "https://idp.example.com/",
    "aud": "solr",
    "rolesClaim": "roles"
  }
}

Requests then carry Authorization: Bearer <token> instead of Basic credentials; the token’s expiry, not any Solr-side session, is what ends access. See JWT Authentication Plugin.

Kerberos

solr.KerberosPlugin authenticates a SPNEGO ticket against a service principal and keytab, for environments that already run an MIT Kerberos or Active Directory KDC; because ZooKeeper access and inter-node requests use the same principal, Kerberos secures node-to-node traffic as well as client requests, not only the HTTP API.

{
  "authentication": {
    "class": "solr.KerberosPlugin"
  }
}
# Passed as system properties when starting each node, not stored in security.json.
bin/solr start -z localhost:2181/solr \
  -Dsolr.kerberos.principal=HTTP/solr-node1.example.com@EXAMPLE.COM \
  -Dsolr.kerberos.keytab=/etc/solr/solr.keytab

Certificate

solr.CertAuthPlugin authenticates a client’s X.509 certificate presented on the TLS handshake — no password or token — and derives the principal from the certificate, by default its subject DN. It requires client-certificate TLS (see below) to already be in place, since the plugin only reads a certificate the transport layer has already verified.

{
  "authentication": {
    "class": "solr.CertAuthPlugin"
  }
}

A custom CertPrincipalResolver can replace the default subject-DN mapping when principals should come from a different certificate field. See Certificate Authentication Plugin.

Rule-based authorization

solr.RuleBasedAuthorizationPlugin grants permissions — named or custom-defined sets of collection and path patterns — to roles, and maps each authenticated principal to one or more roles through user-role. A request is allowed only if some permission it matches lists a role the requesting principal holds; requests matching no permission are allowed by default unless "{}": "true" style default rules or an explicit deny is added.

{
  "authorization": {
    "class": "solr.RuleBasedAuthorizationPlugin",
    "user-role": {
      "admin": "admin",
      "svc-search": "app_reader"
    },
    "permissions": [
      { "name": "security-edit", "role": "admin" },
      { "name": "collection-admin-edit", "role": "admin" },
      {
        "name": "app-read",
        "collection": "app-*",
        "path": "/select",
        "role": "app_reader"
      },
      { "name": "all", "role": "admin" }
    ]
  }
}

Permissions are evaluated in list order and the first match wins, so put narrower rules before the broad all catch-all shown last above. Named permissions such as security-edit, collection-admin-edit, core-admin-edit, and schema-edit cover the built-in admin surfaces; a custom permission adds its own collection/path/method pattern. Manage roles and permissions live through the same /admin/authorization endpoint used to seed them:

curl -u admin:CHANGE_ME_PASSWORD -X POST http://localhost:8983/solr/admin/authorization \
  -H 'Content-Type: application/json' \
  -d '{ "set-user-role": { "svc-search": ["app_reader"] } }'

Audit logging

The auditlogging section of security.json selects an AuditLoggerPlugin implementation and which event types it emits; solr.SolrLogAuditLoggerPlugin writes structured entries to Solr’s own logging framework (typically routed to a file by the log4j2 configuration), and solr.CallbackAuditLoggerPlugin posts events to an arbitrary URL for a SIEM or log pipeline to consume instead.

{
  "auditlogging": {
    "class": "solr.SolrLogAuditLoggerPlugin",
    "async": true,
    "eventTypes": [
      "REJECTED",
      "UNAUTHORIZED",
      "AUTHENTICATED",
      "ANONYMOUS_REJECTED"
    ]
  }
}

Set async so logging never adds latency to the request path, and trim eventTypes to the outcomes worth keeping — logging every AUTHENTICATED success on a busy cluster generates a lot of volume for little value. See Audit Logging for the full event-type list and the muting/chaining options.

TLS/SSL

TLS is configured outside security.json, through Java keystore/truststore system properties passed to each node at startup, and applies to two connections independently: client-to-Solr HTTP traffic, and node-to-node traffic within a SolrCloud cluster (which also needs the ZooKeeper connection itself secured — see below). bin/solr can generate a self-signed certificate for local testing; use a CA-issued certificate for anything beyond that.

# Local/self-signed certificate for a quick trial -- writes a keystore under server/etc/.
bin/solr start -e cloud --tls

# Production: point every node at real keystore/truststore files, and require inter-node
# requests to also present a client certificate.
bin/solr start -z zk1:2181,zk2:2181,zk3:2181/solr \
  -Dsolr.jetty.https.port=8983 \
  -Dsolr.ssl.checkPeerName=true \
  -Djavax.net.ssl.keyStore=/etc/solr/solr-ssl.keystore.p12 \
  -Djavax.net.ssl.keyStorePassword=CHANGE_ME \
  -Djavax.net.ssl.trustStore=/etc/solr/solr-ssl.truststore.p12 \
  -Djavax.net.ssl.trustStorePassword=CHANGE_ME

solr.ssl.checkPeerName turns on hostname verification for inter-node requests; enabling it also requires each node’s certificate to carry the right SAN entries, since a mismatch then fails the connection instead of being silently ignored. Once client-facing TLS is on, every node’s own base URL switches to https://, so ZooKeeper’s /live_nodes and /collections/…​/state.json entries (see SolrCloud architecture) start advertising https URLs, and any client or inter-node request against a plain http:// URL then simply fails to connect. See Enabling SSL.

ZooKeeper access control

Everything SolrCloud coordinates through ZooKeeper — cluster state, security.json itself, and the configsets it hands out to every node (see SolrCloud architecture) — is only as protected as the ZooKeeper ensemble is. By default any client that can reach the ensemble can read and write those znodes with no credentials at all, so a shared or multi-tenant ZooKeeper deployment needs its own ACLs and, ideally, TLS on the ZooKeeper client port too.

# Give Solr's ZooKeeper credentials via system properties -- the same ones every
# node and every bin/solr zk / zkcli invocation must then use to reach the ensemble.
bin/solr start -z localhost:2181/solr \
  -DzkACLProvider=org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider \
  -DzkCredentialsProvider=org.apache.solr.common.cloud.VMParamsSingleSetCredentialsDigestZkCredentialsProvider \
  -DzkDigestUsername=solr-admin -DzkDigestPassword=CHANGE_ME \
  -DzkDigestReadonlyUsername=solr-readonly -DzkDigestReadonlyPassword=CHANGE_ME_RO

The VMParamsAllAndReadonlyDigestZkACLProvider gives the admin credentials full read/write on every znode Solr creates and the readonly credentials read-only access, which covers the common case of letting a monitoring tool watch cluster state without being able to change it; a custom ZkACLProvider/ZkCredentialsProvider pair can implement any other scheme. See ZooKeeper Access Control for the provider interfaces and the full digest-auth setup.

  • SolrCloud architecture — the ZooKeeper ensemble, cluster state, and inter-node traffic that authentication, TLS, and ZooKeeper ACLs are protecting.

  • Deployment & upgrades — rolling out a security.json change, certificate rotation, and other cluster-wide operational concerns.

  • Elasticsearch Security and MongoDB Security — how the same authentication-plugin/role-based-authorization split looks in the other databases in this reference.