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;
   };
 

Reply via email to