From a1d11330f3fcbb3e0f94231ff19431334cd3c567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 28 May 2026 20:07:55 +0200 Subject: [PATCH 1/9] SOLR-18268 Strengthen Basic Auth security: disallow username==password Clean up bin/solr auth enable template accounts --- changelog/unreleased/SOLR-18268.yml | 7 +++++++ .../src/java/org/apache/solr/cli/AuthTool.java | 8 ++++++++ .../security/Sha256AuthenticationProvider.java | 5 +++++ solr/core/src/resources/security.json | 10 ++++------ .../pages/basic-authentication-plugin.adoc | 1 + .../pages/solr-control-script-reference.adoc | 15 ++++++++++++++- .../webapp/web/js/angular/controllers/security.js | 6 ++++++ 7 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 changelog/unreleased/SOLR-18268.yml diff --git a/changelog/unreleased/SOLR-18268.yml b/changelog/unreleased/SOLR-18268.yml new file mode 100644 index 000000000000..00c12f50daca --- /dev/null +++ b/changelog/unreleased/SOLR-18268.yml @@ -0,0 +1,7 @@ +title: Strengthen Basic Authentication password policy and harden template users created by bin/solr auth enable +type: improvement +authors: + - name: Jan Høydahl +links: + - name: SOLR-18268 + url: https://issues.apache.org/jira/browse/SOLR-18268 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 5c19ac300d7f..93b0687cf9c8 100644 --- a/solr/core/src/java/org/apache/solr/cli/AuthTool.java +++ b/solr/core/src/java/org/apache/solr/cli/AuthTool.java @@ -286,6 +286,14 @@ private void handleBasicAuth(CommandLine cli) throws Exception { String.format( Locale.ROOT, "Successfully enabled basic auth with username [%s].", username); echo(successMessage); + 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 31c38537f695..e827c0dfc614 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 void init(Map pluginConfig) { @Override public boolean authenticate(String username, String password) { + if (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 Map edit(Map latestConf, List Date: Fri, 29 May 2026 23:44:58 +0200 Subject: [PATCH 2/9] Rename changelog --- ...18233-Strengthen-Basic-Authentication-password-policy.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename changelog/unreleased/{SOLR-18268.yml => SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml} (68%) diff --git a/changelog/unreleased/SOLR-18268.yml b/changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml similarity index 68% rename from changelog/unreleased/SOLR-18268.yml rename to changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml index 00c12f50daca..3a64ec7b0abe 100644 --- a/changelog/unreleased/SOLR-18268.yml +++ b/changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml @@ -3,5 +3,5 @@ type: improvement authors: - name: Jan Høydahl links: - - name: SOLR-18268 - url: https://issues.apache.org/jira/browse/SOLR-18268 + - name: SOLR-18233 + url: https://issues.apache.org/jira/browse/SOLR-18233 From 5e0dcdc1d109c27b4aed8d9c274ab9629520c903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Sat, 30 May 2026 22:33:04 +0200 Subject: [PATCH 3/9] fix: null-safe username==password check in Sha256AuthenticationProvider.authenticate() username.equals(password) threw NPE when username was null. The previous code handled null usernames safely via credentials.get(null), so this was a regression introduced by the new equality check. --- .../org/apache/solr/security/Sha256AuthenticationProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e827c0dfc614..6e1646a5666f 100644 --- a/solr/core/src/java/org/apache/solr/security/Sha256AuthenticationProvider.java +++ b/solr/core/src/java/org/apache/solr/security/Sha256AuthenticationProvider.java @@ -93,7 +93,7 @@ public void init(Map pluginConfig) { @Override public boolean authenticate(String username, String password) { - if (username.equals(password)) return false; + if (username != null && username.equals(password)) return false; String cred = credentials.get(username); if (cred == null || cred.isEmpty()) return false; cred = cred.trim(); From 8496fbec69de9b536ad8244a7b68d2faa604d930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Sat, 30 May 2026 22:33:56 +0200 Subject: [PATCH 4/9] test: add coverage for username==password rejection in authenticate() and set-user Adds two test methods to TestSha256AuthenticationProvider: - testAuthenticateRejectsUsernameEqualPassword: verifies that authenticate() returns false when username==password even if the hash is stored - testSetUserRejectsUsernameEqualPassword: verifies that the set-user edit command rejects entries where username==password --- .../TestSha256AuthenticationProvider.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) 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 632fd16b35ad..5bc84e2284a1 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 void testBasicAuthDeleteFinalUser() throws IOException { } } + 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 config = new HashMap<>(); + Map 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 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 createConfigMap(String user, String pw) { Map config = new HashMap<>(); Map credentials = new HashMap<>(); From 6731c20098936bf1aafd7ac74759ca7c6b75de17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Sat, 30 May 2026 22:34:12 +0200 Subject: [PATCH 5/9] fix: reject username==password in bin/solr auth enable to avoid lockout If an operator runs 'bin/solr auth enable --credentials admin:admin' with --block-unknown (the default), the created account will never authenticate, immediately locking them out. Fail early with a clear error message instead. --- solr/core/src/java/org/apache/solr/cli/AuthTool.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 93b0687cf9c8..0bf40ea8c6a9 100644 --- a/solr/core/src/java/org/apache/solr/cli/AuthTool.java +++ b/solr/core/src/java/org/apache/solr/cli/AuthTool.java @@ -227,6 +227,13 @@ private void handleBasicAuth(CommandLine cli) throws Exception { } while (password.isEmpty()); } + if (username.equals(password)) { + CLIO.err( + "Error: username and password must not be identical." + + " This credential would never authenticate."); + runtime.exit(1); + } + boolean blockUnknown = Boolean.parseBoolean(cli.getOptionValue(BLOCK_UNKNOWN_OPTION, "true")); From a4b6ca8777decd34aa823e049a3e0399c96ccb81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Sat, 30 May 2026 22:34:27 +0200 Subject: [PATCH 6/9] fix: set blockUnknown inside authentication config, not at security.json top level The --block-unknown flag was writing blockUnknown to the top level of security.json, but Solr reads it from security.json.authentication.blockUnknown. This meant the option had no effect and the template's value (false) was always used, a security-relevant bug since operators believed auth was mandatory when it was not. --- solr/core/src/java/org/apache/solr/cli/AuthTool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0bf40ea8c6a9..44dc9a1dd140 100644 --- a/solr/core/src/java/org/apache/solr/cli/AuthTool.java +++ b/solr/core/src/java/org/apache/solr/cli/AuthTool.java @@ -245,7 +245,7 @@ private void handleBasicAuth(CommandLine cli) throws Exception { ObjectMapper mapper = new ObjectMapper(); JsonNode securityJson1 = mapper.readTree(resource.openStream()); - ((ObjectNode) securityJson1).put("blockUnknown", blockUnknown); + ((ObjectNode) securityJson1.get("authentication")).put("blockUnknown", blockUnknown); JsonNode credentialsNode = securityJson1.get("authentication").get("credentials"); ((ObjectNode) credentialsNode) .put(username, Sha256AuthenticationProvider.getSaltedHashedValue(password)); From 582608405313285e0d68416a54c59ba89ffd9724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Sat, 30 May 2026 22:34:42 +0200 Subject: [PATCH 7/9] fix: correct broken security.json link in bin/solr auth enable docs The bullet describing what 'bin/solr auth enable' does pointed to solr/core/resources/security.json (missing src/). The correct path is solr/core/src/resources/security.json, matching the link added earlier in the same section. --- .../deployment-guide/pages/solr-control-script-reference.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f1a09402e0e7..07d1c5340f50 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 @@ -893,7 +893,7 @@ For more information on Basic Authentication support specifically, see the secti 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. +* Take the base https://github.com/apache/solr/blob/main/solr/core/src/resources/security.json[security.json] file, evolves it using `auth` command parameters, and uploads the new file 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`: + From 33eec7de6aac13a03bd2d3bea812677b5cf25ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Sat, 30 May 2026 22:49:35 +0200 Subject: [PATCH 8/9] Fix changelog to 'fixed' --- ...LR-18233-Strengthen-Basic-Authentication-password-policy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml b/changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml index 3a64ec7b0abe..3c06a95dfe6a 100644 --- a/changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml +++ b/changelog/unreleased/SOLR-18233-Strengthen-Basic-Authentication-password-policy.yml @@ -1,5 +1,5 @@ title: Strengthen Basic Authentication password policy and harden template users created by bin/solr auth enable -type: improvement +type: fixed authors: - name: Jan Høydahl links: From 5afd77496e8ae44d90f9c8628e381436db9879ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Sun, 31 May 2026 00:05:10 +0200 Subject: [PATCH 9/9] Fix review comments --- .../src/java/org/apache/solr/cli/AuthTool.java | 18 ++++++++++-------- .../pages/solr-control-script-reference.adoc | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) 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 44dc9a1dd140..e61b378a4c7e 100644 --- a/solr/core/src/java/org/apache/solr/cli/AuthTool.java +++ b/solr/core/src/java/org/apache/solr/cli/AuthTool.java @@ -293,14 +293,16 @@ private void handleBasicAuth(CommandLine cli) throws Exception { String.format( Locale.ROOT, "Successfully enabled basic auth with username [%s].", username); echo(successMessage); - 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."); + 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/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 07d1c5340f50..c29c306e8f58 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 @@ -893,7 +893,7 @@ For more information on Basic Authentication support specifically, see the secti 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/src/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] file, applies `auth` command parameters, and uploads the new file 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`: +