This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_10x by this push:
new 20c0a6cd389 SOLR-18233 Strengthen Basic Authentication password policy
and harden template users created by bin/solr auth enable (#4477)
20c0a6cd389 is described below
commit 20c0a6cd389e58d8f69a41b940af1ef0262cc99a
Author: Jan Høydahl <[email protected]>
AuthorDate: Wed Jun 10 08:43:10 2026 +0200
SOLR-18233 Strengthen Basic Authentication password policy and harden
template users created by bin/solr auth enable (#4477)
(cherry picked from commit bc5a2eb09e075d5f5115064c812cdf1e16bae087)
---
...ngthen-Basic-Authentication-password-policy.yml | 7 +++++
.../src/java/org/apache/solr/cli/AuthTool.java | 24 ++++++++++++++---
.../security/Sha256AuthenticationProvider.java | 5 ++++
solr/core/src/resources/security.json | 10 +++-----
.../security/TestSha256AuthenticationProvider.java | 27 +++++++++++++++++++
.../pages/basic-authentication-plugin.adoc | 8 +++---
.../pages/solr-control-script-reference.adoc | 30 ++++++++++++++++++----
solr/webapp/web/js/angular/controllers/security.js | 6 +++++
8 files changed, 100 insertions(+), 17 deletions(-)
diff --git
a/changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml
b/changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml
new file mode 100644
index 00000000000..3c06a95dfe6
--- /dev/null
+++
b/changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml
@@ -0,0 +1,7 @@
+title: Strengthen Basic Authentication password policy and harden template
users created by bin/solr auth enable
+type: fixed
+authors:
+ - name: Jan Høydahl
+links:
+ - name: SOLR-18233
+ url: https://issues.apache.org/jira/browse/SOLR-18233
diff --git a/solr/core/src/java/org/apache/solr/cli/AuthTool.java
b/solr/core/src/java/org/apache/solr/cli/AuthTool.java
index 5c19ac300d7..3487787684d 100644
--- a/solr/core/src/java/org/apache/solr/cli/AuthTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/AuthTool.java
@@ -227,8 +227,12 @@ public class AuthTool extends ToolBase {
} while (password.isEmpty());
}
- boolean blockUnknown =
- Boolean.parseBoolean(cli.getOptionValue(BLOCK_UNKNOWN_OPTION,
"true"));
+ if (username.equals(password)) {
+ CLIO.err(
+ "Error: username and password must not be identical."
+ + " This credential would never authenticate.");
+ runtime.exit(1);
+ }
String resourceName = "security.json";
final URL resource =
SolrCore.class.getClassLoader().getResource(resourceName);
@@ -238,7 +242,11 @@ public class AuthTool extends ToolBase {
ObjectMapper mapper = new ObjectMapper();
JsonNode securityJson1 = mapper.readTree(resource.openStream());
- ((ObjectNode) securityJson1).put("blockUnknown", blockUnknown);
+ // Only override blockUnknown if explicitly passed; otherwise let
the template decide
+ if (cli.hasOption(BLOCK_UNKNOWN_OPTION)) {
+ boolean blockUnknown =
Boolean.parseBoolean(cli.getOptionValue(BLOCK_UNKNOWN_OPTION));
+ ((ObjectNode)
securityJson1.get("authentication")).put("blockUnknown", blockUnknown);
+ }
JsonNode credentialsNode =
securityJson1.get("authentication").get("credentials");
((ObjectNode) credentialsNode)
.put(username,
Sha256AuthenticationProvider.getSaltedHashedValue(password));
@@ -286,6 +294,16 @@ public class AuthTool extends ToolBase {
String.format(
Locale.ROOT, "Successfully enabled basic auth with username
[%s].", username);
echo(successMessage);
+ if (!updateIncludeFileOnly) {
+ CLIO.out(
+ "\nIMPORTANT: The following template users have been created
with NO password set"
+ + " and cannot log in until passwords are assigned:");
+ CLIO.out(" - admin (roles: admin, index, search)");
+ CLIO.out(" - index (roles: index, search)");
+ CLIO.out(" - search (roles: search)");
+ CLIO.out(
+ "Set their passwords using the Admin UI Security page or the
authentication API.");
+ }
return;
}
case "disable":
diff --git
a/solr/core/src/java/org/apache/solr/security/Sha256AuthenticationProvider.java
b/solr/core/src/java/org/apache/solr/security/Sha256AuthenticationProvider.java
index 31c38537f69..6e1646a5666 100644
---
a/solr/core/src/java/org/apache/solr/security/Sha256AuthenticationProvider.java
+++
b/solr/core/src/java/org/apache/solr/security/Sha256AuthenticationProvider.java
@@ -93,6 +93,7 @@ public class Sha256AuthenticationProvider
@Override
public boolean authenticate(String username, String password) {
+ if (username != null && username.equals(password)) return false;
String cred = credentials.get(username);
if (cred == null || cred.isEmpty()) return false;
cred = cred.trim();
@@ -165,6 +166,10 @@ public class Sha256AuthenticationProvider
cmd.addError("name and password must be non-null");
return null;
}
+ if (e.getKey().equals(String.valueOf(e.getValue()))) {
+ cmd.addError("Password must not be the same as the username");
+ return null;
+ }
putUser(e.getKey(), String.valueOf(e.getValue()), map);
}
}
diff --git a/solr/core/src/resources/security.json
b/solr/core/src/resources/security.json
index f503f7c4e30..4406cf52a1b 100644
--- a/solr/core/src/resources/security.json
+++ b/solr/core/src/resources/security.json
@@ -3,10 +3,9 @@
"blockUnknown": false,
"class": "solr.BasicAuthPlugin",
"credentials": {
- "search": "9ch2qWOmNSeGpfcgLRXafhm5z3KeRti5qCNLn7SmK1I=
aXNjZWd4YW9mMzZ0cjE1Nw==",
- "index": "of9xlSadImtR0MH4obzJvKSZkuE5DIJh5NOui2hWDeA=
dTRuYzU4Y3F4N2hxd2sxeA==",
- "admin": "6clS8rTEj1x1LP/uRCxOZsLdps7Sovokru09WdJX+7A=
NGMyZGFhN2lrNHFsdXZybA==",
- "superadmin": "9wzPajmLBIIi8BmToy8lxveDxfL6Vl/BX/Ss3xrs3XQ=
OWZna2hwendocXFnODU5ZQ=="
+ "search": "",
+ "index": "",
+ "admin": ""
}
},
"authorization": {
@@ -67,8 +66,7 @@
"user-role": {
"search": ["search"],
"index": ["index", "search"],
- "admin": ["admin", "index", "search"],
- "superadmin": ["superadmin", "admin", "index", "search"]
+ "admin": ["admin", "index", "search"]
}
}
}
diff --git
a/solr/core/src/test/org/apache/solr/security/TestSha256AuthenticationProvider.java
b/solr/core/src/test/org/apache/solr/security/TestSha256AuthenticationProvider.java
index 632fd16b35a..5bc84e2284a 100644
---
a/solr/core/src/test/org/apache/solr/security/TestSha256AuthenticationProvider.java
+++
b/solr/core/src/test/org/apache/solr/security/TestSha256AuthenticationProvider.java
@@ -104,6 +104,33 @@ public class TestSha256AuthenticationProvider extends
SolrTestCaseJ4 {
}
}
+ public void testAuthenticateRejectsUsernameEqualPassword() {
+ // Simulate a credential store that has the username's own hash as the
password
+ // (e.g. set up before this policy was in effect) and verify
authenticate() still rejects it.
+ String user = "alice";
+ String hashedValue =
Sha256AuthenticationProvider.getSaltedHashedValue(user);
+ Map<String, Object> config = new HashMap<>();
+ Map<String, String> credentials = new HashMap<>();
+ credentials.put(user, hashedValue);
+ config.put("credentials", credentials);
+
+ Sha256AuthenticationProvider provider = new Sha256AuthenticationProvider();
+ provider.init(config);
+ assertFalse(
+ "authenticate() must reject username==password even when hash matches",
+ provider.authenticate(user, user));
+ }
+
+ public void testSetUserRejectsUsernameEqualPassword() {
+ Sha256AuthenticationProvider provider = new Sha256AuthenticationProvider();
+ provider.init(createConfigMap("ignore", "me"));
+ Map<String, Object> latestConf = createConfigMap("ignore", "me");
+ String user = "bob";
+ CommandOperation cmd = new CommandOperation("set-user", Map.of(user,
user));
+ provider.edit(latestConf, List.of(cmd));
+ assertTrue("set-user should report an error when username==password",
cmd.hasError());
+ }
+
private Map<String, Object> createConfigMap(String user, String pw) {
Map<String, Object> config = new HashMap<>();
Map<String, String> credentials = new HashMap<>();
diff --git
a/solr/solr-ref-guide/modules/deployment-guide/pages/basic-authentication-plugin.adoc
b/solr/solr-ref-guide/modules/deployment-guide/pages/basic-authentication-plugin.adoc
index 19536088939..9f1a062feb6 100644
---
a/solr/solr-ref-guide/modules/deployment-guide/pages/basic-authentication-plugin.adoc
+++
b/solr/solr-ref-guide/modules/deployment-guide/pages/basic-authentication-plugin.adoc
@@ -24,10 +24,11 @@ To control user permissions, you may need to configure an
authorization plugin a
== Enable Basic Authentication
-To use Basic authentication, you must first create a `security.json` file.
-This file and where to put it is described in detail in the section
xref:authentication-and-authorization-plugins.adoc#configuring-security-json[Configuring
security.json].
+When running in cloud mode, Basic authentication can be enabled from the
command line using the `bin/solr auth enable` command, which applies a
best-practice security template with pre-configured roles and permissions.
+See
xref:solr-control-script-reference.adoc#enabling-basic-authentication[Enabling
Basic Authentication] in the Solr Control Script Reference for details.
-If running in cloud mode, you can use the `bin/solr auth` command-line utility
to enable security for a new installation, see: `bin/solr auth --help` for more
details.
+Alternatively, you can create the `security.json` file manually.
+This file and where to put it is described in detail in the section
xref:authentication-and-authorization-plugins.adoc#configuring-security-json[Configuring
security.json].
For Basic authentication, `security.json` must have an `authentication` block
which defines the class being used for authentication.
Usernames and passwords could be added when the file is created, or can be
added later with the Authentication API, described below.
@@ -185,6 +186,7 @@ If users need to be restricted to a specific collection,
that can be done with t
=== Add a User or Edit a Password
The `set-user` command allows you to add users and change their passwords.
+Passwords must not be identical to the username.
For example, the following defines two users and their passwords:
[tabs#set-user]
diff --git
a/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc
b/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc
index f507216396b..aae46c7c6cc 100644
---
a/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc
+++
b/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc
@@ -916,19 +916,23 @@ The `bin/solr` script allows enabling or disabling
Authentication, allowing you
Currently this command is only available when using SolrCloud mode and must be
run on the machine hosting Solr.
-For Basic Authentication the script provides
https://github.com/apache/solr/blob/main/solr/core/src/resources/security.json[user
roles and permission mappings], and maps the created user to the `superadmin`
role.
+For Basic Authentication the script provides
https://github.com/apache/solr/blob/main/solr/core/src/resources/security.json[user
roles and permission mappings], and maps the created user to all roles
(`superadmin`, `admin`, `index`, `search`).
=== Enabling Basic Authentication
The command `bin/solr auth enable` configures Solr to use Basic Authentication
when accessing the User Interface, using `bin/solr` and any API requests.
+NOTE: This command currently requires SolrCloud mode — it uploads the
generated `security.json` to ZooKeeper so that all nodes pick it up
automatically.
+For user-managed (standalone) clusters, you must create the `security.json`
file manually and place it in each node's Solr home directory.
+See xref:basic-authentication-plugin.adoc[] for details.
+
TIP: For more information about Solr's authentication plugins, see the section
xref:securing-solr.adoc[].
For more information on Basic Authentication support specifically, see the
section xref:basic-authentication-plugin.adoc[].
The `bin/solr auth enable` command makes several changes to enable Basic
Authentication:
-* Take the base
https://github.com/apache/solr/blob/main/solr/core/resources/security.json[security.json]
file, evolves it using `auth` command parameters, and uploads the new file to
ZooKeeper.
+* Takes the base
https://github.com/apache/solr/blob/main/solr/core/src/resources/security.json[security.json]
template with best-practice roles and permissions, applies `auth` command
parameters, and uploads the result to ZooKeeper.
+
* Adds two lines to `bin/solr.in.sh` or `bin\solr.in.cmd` to set the
authentication type, and the path to `basicAuth.conf`:
+
@@ -940,6 +944,19 @@
SOLR_AUTHENTICATION_OPTS="-Dsolr.httpclient.config=/path/to/solr-{solr-full-vers
----
* Creates the file `server/solr/basicAuth.conf` to store the credential
information that is used with `bin/solr` commands.
+In addition to the operator-created user, the command also creates three
template users with predefined role assignments.
+These users have no password set and cannot log in until passwords are
explicitly assigned:
+
+[cols="1,2",options="header"]
+|===
+|Username |Roles
+|`admin` |admin, index, search
+|`index` |index, search
+|`search` |search
+|===
+
+After enabling Basic Authentication, set passwords for these template users
using the Admin UI Security page or the
xref:basic-authentication-plugin.adoc#add-a-user-or-edit-a-password[authentication
API].
+
Here are some example usages:
[source,plain]
@@ -981,11 +998,14 @@ Either `--credentials` or `--prompt` *must* be specified.
+
[%autowidth,frame=none]
|===
-|Optional |Default: `true`
+|Optional |Default: use value from `security.json` template (`false`)
|===
+
-When `true`, this blocks out access to unauthenticated users from accessing
Solr.
-When `false`, unauthenticated users will still be able to access Solr, but
only for operations not explicitly requiring a user role in the Authorization
plugin configuration.
+Controls whether unauthenticated requests are blocked.
+The default `security.json` template sets `blockUnknown` to `false` because it
includes a `RuleBasedAuthorizationPlugin` with fine-grained permissions —
unauthenticated users can only access endpoints explicitly granted to the
`null` role (by default *health* and *metrics-read*, left open so that load
balancers and monitoring tools can operate without credentials).
+All other operations require an authenticated user with the appropriate role.
++
+If you want to require authentication for _all_ requests (including health
checks and metrics), pass `--block-unknown true` explicitly.
`--solr-include-file <includeFilePath>`::
+
diff --git a/solr/webapp/web/js/angular/controllers/security.js
b/solr/webapp/web/js/angular/controllers/security.js
index fd65a289988..1ffdf85460a 100644
--- a/solr/webapp/web/js/angular/controllers/security.js
+++ b/solr/webapp/web/js/angular/controllers/security.js
@@ -412,6 +412,12 @@ solrAdminApp.controller('SecurityController', function
($scope, $timeout, $cooki
return false;
}
+ var username = $scope.upsertUser.username ?
$scope.upsertUser.username.trim() : "";
+ if (password === username) {
+ $scope.validationError = "Password must not be the same as the username";
+ return false;
+ }
+
return true;
};