Skip to content

Commit dfb8b0d

Browse files
committed
SOLR-18233 Strengthen Basic Authentication password policy and harden template users created by bin/solr auth enable (#4477)
(cherry picked from commit bc5a2eb)
1 parent 18ed6eb commit dfb8b0d

9 files changed

Lines changed: 105 additions & 20 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
title: Strengthen Basic Authentication password policy and harden template users created by bin/solr auth enable
2+
type: fixed
3+
authors:
4+
- name: Jan Høydahl
5+
links:
6+
- name: SOLR-18233
7+
url: https://issues.apache.org/jira/browse/SOLR-18233

solr/core/src/java/org/apache/solr/cli/AuthTool.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -467,10 +467,12 @@ private int handleBasicAuth(CommandLine cli) throws Exception {
467467
} while (password.length() == 0);
468468
}
469469

470-
boolean blockUnknown =
471-
Boolean.parseBoolean(
472-
SolrCLI.getOptionWithDeprecatedAndDefault(
473-
cli, "block-unknown", "blockUnknown", "true"));
470+
if (username.equals(password)) {
471+
CLIO.err(
472+
"Error: username and password must not be identical."
473+
+ " This credential would never authenticate.");
474+
runtime.exit(1);
475+
}
474476

475477
String resourceName = "security.json";
476478
final URL resource = SolrCore.class.getClassLoader().getResource(resourceName);
@@ -480,7 +482,14 @@ private int handleBasicAuth(CommandLine cli) throws Exception {
480482

481483
ObjectMapper mapper = new ObjectMapper();
482484
JsonNode securityJson1 = mapper.readTree(resource.openStream());
483-
((ObjectNode) securityJson1).put("blockUnknown", blockUnknown);
485+
// Only override blockUnknown if explicitly passed; otherwise let the template decide
486+
if (cli.hasOption("block-unknown") || cli.hasOption("blockUnknown")) {
487+
boolean blockUnknown =
488+
Boolean.parseBoolean(
489+
SolrCLI.getOptionWithDeprecatedAndDefault(
490+
cli, "block-unknown", "blockUnknown", "true"));
491+
((ObjectNode) securityJson1.get("authentication")).put("blockUnknown", blockUnknown);
492+
}
484493
JsonNode credentialsNode = securityJson1.get("authentication").get("credentials");
485494
((ObjectNode) credentialsNode)
486495
.put(username, Sha256AuthenticationProvider.getSaltedHashedValue(password));
@@ -536,6 +545,16 @@ private int handleBasicAuth(CommandLine cli) throws Exception {
536545
String.format(
537546
Locale.ROOT, "Successfully enabled basic auth with username [%s].", username);
538547
echo(successMessage);
548+
if (!updateIncludeFileOnly) {
549+
CLIO.out(
550+
"\nIMPORTANT: The following template users have been created with NO password set"
551+
+ " and cannot log in until passwords are assigned:");
552+
CLIO.out(" - admin (roles: admin, index, search)");
553+
CLIO.out(" - index (roles: index, search)");
554+
CLIO.out(" - search (roles: search)");
555+
CLIO.out(
556+
"Set their passwords using the Admin UI Security page or the authentication API.");
557+
}
539558
return 0;
540559

541560
case "disable":

solr/core/src/java/org/apache/solr/security/Sha256AuthenticationProvider.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public void init(Map<String, Object> pluginConfig) {
9494

9595
@Override
9696
public boolean authenticate(String username, String password) {
97+
if (username != null && username.equals(password)) return false;
9798
String cred = credentials.get(username);
9899
if (cred == null || cred.isEmpty()) return false;
99100
cred = cred.trim();
@@ -166,6 +167,10 @@ public Map<String, Object> edit(Map<String, Object> latestConf, List<CommandOper
166167
cmd.addError("name and password must be non-null");
167168
return null;
168169
}
170+
if (e.getKey().equals(String.valueOf(e.getValue()))) {
171+
cmd.addError("Password must not be the same as the username");
172+
return null;
173+
}
169174
putUser(e.getKey(), String.valueOf(e.getValue()), map);
170175
}
171176
}

solr/core/src/resources/security.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
"blockUnknown": false,
44
"class": "solr.BasicAuthPlugin",
55
"credentials": {
6-
"search": "9ch2qWOmNSeGpfcgLRXafhm5z3KeRti5qCNLn7SmK1I= aXNjZWd4YW9mMzZ0cjE1Nw==",
7-
"index": "of9xlSadImtR0MH4obzJvKSZkuE5DIJh5NOui2hWDeA= dTRuYzU4Y3F4N2hxd2sxeA==",
8-
"admin": "6clS8rTEj1x1LP/uRCxOZsLdps7Sovokru09WdJX+7A= NGMyZGFhN2lrNHFsdXZybA==",
9-
"superadmin": "9wzPajmLBIIi8BmToy8lxveDxfL6Vl/BX/Ss3xrs3XQ= OWZna2hwendocXFnODU5ZQ=="
6+
"search": "",
7+
"index": "",
8+
"admin": ""
109
}
1110
},
1211
"authorization": {
@@ -67,8 +66,7 @@
6766
"user-role": {
6867
"search": ["search"],
6968
"index": ["index", "search"],
70-
"admin": ["admin", "index", "search"],
71-
"superadmin": ["superadmin", "admin", "index", "search"]
69+
"admin": ["admin", "index", "search"]
7270
}
7371
}
7472
}

solr/core/src/test/org/apache/solr/cli/AuthToolTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public void testEnableAuth() throws Exception {
6767
"--solr-include-file",
6868
solrIncludeFile.toAbsolutePath().toString(),
6969
"--credentials",
70-
"solr:solr",
70+
"solr:SolrRocks",
7171
"--block-unknown",
7272
"true"
7373
};

solr/core/src/test/org/apache/solr/security/TestSha256AuthenticationProvider.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Arrays;
2323
import java.util.Collections;
2424
import java.util.HashMap;
25+
import java.util.List;
2526
import java.util.Map;
2627
import org.apache.solr.SolrTestCaseJ4;
2728
import org.apache.solr.common.util.CommandOperation;
@@ -106,6 +107,33 @@ public void testBasicAuthDeleteFinalUser() throws IOException {
106107
}
107108
}
108109

110+
public void testAuthenticateRejectsUsernameEqualPassword() {
111+
// Simulate a credential store that has the username's own hash as the password
112+
// (e.g. set up before this policy was in effect) and verify authenticate() still rejects it.
113+
String user = "alice";
114+
String hashedValue = Sha256AuthenticationProvider.getSaltedHashedValue(user);
115+
Map<String, Object> config = new HashMap<>();
116+
Map<String, String> credentials = new HashMap<>();
117+
credentials.put(user, hashedValue);
118+
config.put("credentials", credentials);
119+
120+
Sha256AuthenticationProvider provider = new Sha256AuthenticationProvider();
121+
provider.init(config);
122+
assertFalse(
123+
"authenticate() must reject username==password even when hash matches",
124+
provider.authenticate(user, user));
125+
}
126+
127+
public void testSetUserRejectsUsernameEqualPassword() {
128+
Sha256AuthenticationProvider provider = new Sha256AuthenticationProvider();
129+
provider.init(createConfigMap("ignore", "me"));
130+
Map<String, Object> latestConf = createConfigMap("ignore", "me");
131+
String user = "bob";
132+
CommandOperation cmd = new CommandOperation("set-user", Map.of(user, user));
133+
provider.edit(latestConf, List.of(cmd));
134+
assertTrue("set-user should report an error when username==password", cmd.hasError());
135+
}
136+
109137
private Map<String, Object> createConfigMap(String user, String pw) {
110138
Map<String, Object> config = new HashMap<>();
111139
Map<String, String> credentials = new HashMap<>();

solr/solr-ref-guide/modules/deployment-guide/pages/basic-authentication-plugin.adoc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ To control user permissions, you may need to configure an authorization plugin a
2424

2525
== Enable Basic Authentication
2626

27-
To use Basic authentication, you must first create a `security.json` file.
28-
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].
27+
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.
28+
See xref:solr-control-script-reference.adoc#enabling-basic-authentication[Enabling Basic Authentication] in the Solr Control Script Reference for details.
2929

30-
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.
30+
Alternatively, you can create the `security.json` file manually.
31+
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].
3132

3233
For Basic authentication, `security.json` must have an `authentication` block which defines the class being used for authentication.
3334
Usernames and passwords (Format: `base64(sha256(sha256(salt+password))) base64(salt)`) could be added when the file is created, or can be added later with the Authentication API, described below.
@@ -150,6 +151,7 @@ If users need to be restricted to a specific collection, that can be done with t
150151
=== Add a User or Edit a Password
151152

152153
The `set-user` command allows you to add users and change their passwords.
154+
Passwords must not be identical to the username.
153155
For example, the following defines two users and their passwords:
154156

155157
[tabs#set-user]

solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,20 +1022,24 @@ The `bin/solr` script allows enabling or disabling Authentication, allowing you
10221022

10231023
Currently this command is only available when using SolrCloud mode and must be run on the machine hosting Solr.
10241024

1025-
For Basic Authentication the script provides https://github.com/apache/solr/blob/branch_9x/solr/core/src/resources/security.json[user roles and permission mappings], and maps the created user to the `superadmin` role.
1025+
For Basic Authentication the script provides https://github.com/apache/solr/blob/branch_9x/solr/core/src/resources/security.json[user roles and permission mappings], and maps the created user to all roles (`superadmin`, `admin`, `index`, `search`).
10261026
For Kerberos it only enables the security.json, it doesn't set up any users or role mappings.
10271027

10281028

10291029
=== Enabling Basic Authentication
10301030

10311031
The command `bin/solr auth enable` configures Solr to use Basic Authentication when accessing the User Interface, using `bin/solr` and any API requests.
10321032

1033+
NOTE: This command currently requires SolrCloud mode — it uploads the generated `security.json` to ZooKeeper so that all nodes pick it up automatically.
1034+
For user-managed (standalone) clusters, you must create the `security.json` file manually and place it in each node's Solr home directory.
1035+
See xref:basic-authentication-plugin.adoc[] for details.
1036+
10331037
TIP: For more information about Solr's authentication plugins, see the section xref:securing-solr.adoc[].
10341038
For more information on Basic Authentication support specifically, see the section xref:basic-authentication-plugin.adoc[].
10351039

10361040
The `bin/solr auth enable` command makes several changes to enable Basic Authentication:
10371041

1038-
* 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.
1042+
* Takes the base https://github.com/apache/solr/blob/branch_9x/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.
10391043
+
10401044
* Adds two lines to `bin/solr.in.sh` or `bin\solr.in.cmd` to set the authentication type, and the path to `basicAuth.conf`:
10411045
+
@@ -1047,6 +1051,19 @@ SOLR_AUTHENTICATION_OPTS="-Dsolr.httpclient.config=/path/to/solr-{solr-full-vers
10471051
----
10481052
* Creates the file `server/solr/basicAuth.conf` to store the credential information that is used with `bin/solr` commands.
10491053

1054+
In addition to the operator-created user, the command also creates three template users with predefined role assignments.
1055+
These users have no password set and cannot log in until passwords are explicitly assigned:
1056+
1057+
[cols="1,2",options="header"]
1058+
|===
1059+
|Username |Roles
1060+
|`admin` |admin, index, search
1061+
|`index` |index, search
1062+
|`search` |search
1063+
|===
1064+
1065+
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].
1066+
10501067
Here are some example usages:
10511068

10521069
[source,plain]
@@ -1089,11 +1106,14 @@ Either `--credentials` or `--prompt` *must* be specified.
10891106
+
10901107
[%autowidth,frame=none]
10911108
|===
1092-
|Optional |Default: `true`
1109+
|Optional |Default: use value from `security.json` template (`false`)
10931110
|===
10941111
+
1095-
When `true`, this blocks out access to unauthenticated users from accessing Solr.
1096-
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.
1112+
Controls whether unauthenticated requests are blocked.
1113+
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).
1114+
All other operations require an authenticated user with the appropriate role.
1115+
+
1116+
If you want to require authentication for _all_ requests (including health checks and metrics), pass `--block-unknown true` explicitly.
10971117

10981118
`--solr-include-file <includeFilePath>`::
10991119
+

solr/webapp/web/js/angular/controllers/security.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,12 @@ solrAdminApp.controller('SecurityController', function ($scope, $timeout, $cooki
412412
return false;
413413
}
414414

415+
var username = $scope.upsertUser.username ? $scope.upsertUser.username.trim() : "";
416+
if (password === username) {
417+
$scope.validationError = "Password must not be the same as the username";
418+
return false;
419+
}
420+
415421
return true;
416422
};
417423

0 commit comments

Comments
 (0)