Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
19 changes: 18 additions & 1 deletion solr/core/src/java/org/apache/solr/cli/AuthTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -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"));

Expand All @@ -238,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));
Expand Down Expand Up @@ -286,6 +293,16 @@ private void handleBasicAuth(CommandLine cli) throws Exception {
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":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public void init(Map<String, Object> pluginConfig) {

@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;
Comment thread
janhoy marked this conversation as resolved.
Comment thread
janhoy marked this conversation as resolved.
cred = cred.trim();
Expand Down Expand Up @@ -165,6 +166,10 @@ public Map<String, Object> edit(Map<String, Object> latestConf, List<CommandOper
cmd.addError("name and password must be non-null");
return null;
}
if (e.getKey().equals(String.valueOf(e.getValue()))) {
Comment thread
janhoy marked this conversation as resolved.
cmd.addError("Password must not be the same as the username");
return null;
}
putUser(e.getKey(), String.valueOf(e.getValue()), map);
}
}
Expand Down
10 changes: 4 additions & 6 deletions solr/core/src/resources/security.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -67,8 +66,7 @@
"user-role": {
"search": ["search"],
"index": ["index", "search"],
"admin": ["admin", "index", "search"],
"superadmin": ["superadmin", "admin", "index", "search"]
"admin": ["admin", "index", "search"]
}
}
Comment thread
janhoy marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<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<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ 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`).
Comment thread
janhoy marked this conversation as resolved.


=== Enabling Basic Authentication
Expand All @@ -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.
* 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`:
+
Expand All @@ -905,6 +905,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]
Expand Down
6 changes: 6 additions & 0 deletions solr/webapp/web/js/angular/controllers/security.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Comment thread
janhoy marked this conversation as resolved.
$scope.validationError = "Password must not be the same as the username";
return false;
}

return true;
};

Expand Down
Loading