SOLR-10282: bin/solr support for enabling Kerberos authentication

This commit is contained in:
Ishan Chattopadhyaya 2017-07-07 15:31:03 +05:30
parent 7051a7919f
commit 5f28780e5d
3 changed files with 200 additions and 20 deletions

View File

@ -272,6 +272,8 @@ New Features
multivalued fields, a new JSON request language, and more. DocValues are now required for any field used in the analytics
expression whereas previously docValues was not required. Please see SOLR-10123 for details. (Houston Putman)
* SOLR-10282: bin/solr support for enabling Kerberos authentication (Ishan Chattopadhyaya)
Bug Fixes
----------------------
* SOLR-9262: Connection and read timeouts are being ignored by UpdateShardHandler after SOLR-4509.

View File

@ -555,20 +555,23 @@ function print_usage() {
echo ""
echo "Usage: solr auth enable [-type basicAuth] -credentials user:pass [-blockUnknown <true|false>] [-updateIncludeFileOnly <true|false>]"
echo " solr auth enable [-type basicAuth] -prompt <true|false> [-blockUnknown <true|false>] [-updateIncludeFileOnly <true|false>]"
echo " solr auth enable -type kerberos -config "<kerberos configs>" [-updateIncludeFileOnly <true|false>]"
echo " solr auth disable [-updateIncludeFileOnly <true|false>]"
echo ""
echo " -type <type> The authentication mechanism to enable. Defaults to 'basicAuth'."
echo " -type <type> The authentication mechanism (basicAuth or kerberos) to enable. Defaults to 'basicAuth'."
echo ""
echo " -credentials <user:pass> The username and password of the initial user"
echo " -credentials <user:pass> The username and password of the initial user. Applicable for basicAuth only."
echo " Note: only one of -prompt or -credentials must be provided"
echo ""
echo " -prompt <true|false> Prompts the user to provide the credentials"
echo " -config "<configs>" Configuration parameters (Solr startup parameters). Required and applicable only for Kerberos"
echo ""
echo " -prompt <true|false> Prompts the user to provide the credentials. Applicable for basicAuth only."
echo " Note: only one of -prompt or -credentials must be provided"
echo ""
echo " -blockUnknown <true|false> When true, this blocks out access to unauthenticated users. When not provided,"
echo " this defaults to false (i.e. unauthenticated users can access all endpoints, except the"
echo " operations like collection-edit, security-edit, core-admin-edit etc.). Check the reference"
echo " guide for Basic Authentication for more details."
echo " guide for Basic Authentication for more details. Applicable for basicAuth only."
echo ""
echo " -updateIncludeFileOnly <true|false> Only update the solr.in.sh or solr.in.cmd file, and skip actual enabling/disabling"
echo " authentication (i.e. don't update security.json)"
@ -1242,6 +1245,11 @@ if [[ "$SCRIPT_CMD" == "auth" ]]; then
AUTH_PARAMS=("${AUTH_PARAMS[@]}" "-credentials" "$AUTH_CREDENTIALS")
shift 2
;;
-config)
AUTH_CONFIG="`echo $2| base64`"
AUTH_PARAMS=("${AUTH_PARAMS[@]}" "-config" "$AUTH_CONFIG")
shift 2
;;
-solrIncludeFile)
SOLR_INCLUDE="$2"
shift 2

View File

@ -43,6 +43,7 @@ import java.time.Instant;
import java.time.Period;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
@ -115,6 +116,7 @@ import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.ContentStreamBase;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.security.Sha256AuthenticationProvider;
import org.apache.solr.util.configuration.SSLConfigurationsFactory;
import org.noggit.CharArr;
@ -3548,7 +3550,7 @@ public class SolrCLI {
OptionBuilder
.withArgName("type")
.hasArg()
.withDescription("The authentication mechanism to enable. Defaults to 'basicAuth'.")
.withDescription("The authentication mechanism to enable (basicAuth or kerberos). Defaults to 'basicAuth'.")
.create("type"),
OptionBuilder
.withArgName("credentials")
@ -3561,6 +3563,11 @@ public class SolrCLI {
.withDescription("Prompts the user to provide the credentials. Use either -credentials or -prompt, not both")
.create("prompt"),
OptionBuilder
.withArgName("config")
.hasArgs()
.withDescription("Configuration parameters (Solr startup parameters). Required for Kerberos authentication")
.create("config"),
OptionBuilder
.withArgName("blockUnknown")
.withDescription("Blocks all access for unknown users (requires authentication for all endpoints)")
.hasArg()
@ -3603,11 +3610,141 @@ public class SolrCLI {
}
String type = cli.getOptionValue("type", "basicAuth");
if (type.equalsIgnoreCase("basicAuth") == false) {
System.out.println("Only type=basicAuth supported at the moment.");
switch (type) {
case "basicAuth":
return handleBasicAuth(cli);
case "kerberos":
return handleKerberos(cli);
default:
System.out.println("Only type=basicAuth or kerberos supported at the moment.");
exit(1);
}
return 1;
}
private int handleKerberos(CommandLine cli) throws Exception {
String cmd = cli.getArgs()[0];
boolean updateIncludeFileOnly = Boolean.parseBoolean(cli.getOptionValue("updateIncludeFileOnly", "false"));
String securityJson = "{" +
"\n \"authentication\":{" +
"\n \"class\":\"solr.KerberosPlugin\"" +
"\n }" +
"\n}";
switch (cmd) {
case "enable":
String zkHost = null;
boolean zkInaccessible = false;
if (!updateIncludeFileOnly) {
try {
zkHost = getZkHost(cli);
} catch (Exception ex) {
System.out.println("Unable to access ZooKeeper. Please add the following security.json to ZooKeeper (in case of SolrCloud):\n"
+ securityJson + "\n");
zkInaccessible = true;
}
if (zkHost == null) {
if (zkInaccessible == false) {
System.out.println("Unable to access ZooKeeper. Please add the following security.json to ZooKeeper (in case of SolrCloud):\n"
+ securityJson + "\n");
zkInaccessible = true;
}
}
// check if security is already enabled or not
if (!zkInaccessible) {
try (SolrZkClient zkClient = new SolrZkClient(zkHost, 10000)) {
if (zkClient.exists("/security.json", true)) {
byte oldSecurityBytes[] = zkClient.getData("/security.json", null, null, true);
if (!"{}".equals(new String(oldSecurityBytes, StandardCharsets.UTF_8).trim())) {
System.out.println("Security is already enabled. You can disable it with 'bin/solr auth disable'. Existing security.json: \n"
+ new String(oldSecurityBytes, StandardCharsets.UTF_8));
exit(1);
}
}
} catch (Exception ex) {
if (zkInaccessible == false) {
System.out.println("Unable to access ZooKeeper. Please add the following security.json to ZooKeeper (in case of SolrCloud):\n"
+ securityJson + "\n");
zkInaccessible = true;
}
}
}
}
if (!updateIncludeFileOnly) {
if (!zkInaccessible) {
System.out.println("Uploading following security.json: " + securityJson);
try (SolrZkClient zkClient = new SolrZkClient(zkHost, 10000)) {
zkClient.setData("/security.json", securityJson.getBytes(StandardCharsets.UTF_8), true);
} catch (Exception ex) {
if (zkInaccessible == false) {
System.out.println("Unable to access ZooKeeper. Please add the following security.json to ZooKeeper (in case of SolrCloud):\n"
+ securityJson);
zkInaccessible = true;
}
}
}
}
String config = StrUtils.join(Arrays.asList(cli.getOptionValues("config")), ' ');
// config is base64 encoded (to get around parsing problems), decode it
config = config.replaceAll(" ", "");
config = new String(Base64.getDecoder().decode(config.getBytes("UTF-8")), "UTF-8");
config = config.replaceAll("\n", "").replaceAll("\r", "");
String solrIncludeFilename = cli.getOptionValue("solrIncludeFile");
File includeFile = new File(solrIncludeFilename);
if (includeFile.exists() == false || includeFile.canWrite() == false) {
System.out.println("Solr include file " + solrIncludeFilename + " doesn't exist or is not writeable.");
printAuthEnablingInstructions(config);
System.exit(0);
}
// update the solr.in.sh file to contain the necessary authentication lines
updateIncludeFileEnableAuth(includeFile, null, config);
System.out.println("Please restart any running Solr nodes.");
return 0;
case "disable":
if (!updateIncludeFileOnly) {
zkHost = getZkHost(cli);
if (zkHost == null) {
stdout.print("ZK Host not found. Solr should be running in cloud mode");
exit(1);
}
System.out.println("Uploading following security.json: {}");
try (SolrZkClient zkClient = new SolrZkClient(zkHost, 10000)) {
zkClient.setData("/security.json", "{}".getBytes(StandardCharsets.UTF_8), true);
}
}
solrIncludeFilename = cli.getOptionValue("solrIncludeFile");
includeFile = new File(solrIncludeFilename);
if (!includeFile.exists() || !includeFile.canWrite()) {
System.out.println("Solr include file " + solrIncludeFilename + " doesn't exist or is not writeable.");
System.out.println("Security has been disabled. Please remove any SOLR_AUTH_TYPE or SOLR_AUTHENTICATION_OPTS configuration from solr.in.sh/solr.in.cmd.\n");
System.exit(0);
}
// update the solr.in.sh file to comment out the necessary authentication lines
updateIncludeFileDisableAuth(includeFile);
return 0;
default:
System.out.println("Valid auth commands are: enable, disable");
exit(1);
}
System.out.println("Options not understood.");
new HelpFormatter().printHelp("bin/solr auth <enable|disable> [OPTIONS]", getToolOptions(this));
return 1;
}
private int handleBasicAuth(CommandLine cli) throws Exception {
String cmd = cli.getArgs()[0];
boolean prompt = Boolean.parseBoolean(cli.getOptionValue("prompt", "false"));
boolean updateIncludeFileOnly = Boolean.parseBoolean(cli.getOptionValue("updateIncludeFileOnly", "false"));
@ -3715,7 +3852,7 @@ public class SolrCLI {
"httpBasicAuthUser=" + username + "\nhttpBasicAuthPassword=" + password, StandardCharsets.UTF_8);
// update the solr.in.sh file to contain the necessary authentication lines
updateIncludeFileEnableAuth(includeFile, basicAuthConfFile.getAbsolutePath());
updateIncludeFileEnableAuth(includeFile, basicAuthConfFile.getAbsolutePath(), null);
return 0;
case "disable":
@ -3754,7 +3891,6 @@ public class SolrCLI {
new HelpFormatter().printHelp("bin/solr auth <enable|disable> [OPTIONS]", getToolOptions(this));
return 1;
}
private void printAuthEnablingInstructions(String username, String password) {
if (SystemUtils.IS_OS_WINDOWS) {
System.out.println("\nAdd the following lines to the solr.in.cmd file so that the solr.cmd script can use subsequently.\n");
@ -3766,8 +3902,26 @@ public class SolrCLI {
+ "SOLR_AUTHENTICATION_OPTS=\"-Dbasicauth=" + username + ":" + password + "\"\n");
}
}
private void printAuthEnablingInstructions(String kerberosConfig) {
if (SystemUtils.IS_OS_WINDOWS) {
System.out.println("\nAdd the following lines to the solr.in.cmd file so that the solr.cmd script can use subsequently.\n");
System.out.println("set SOLR_AUTH_TYPE=kerberos\n"
+ "set SOLR_AUTHENTICATION_OPTS=\"" + kerberosConfig + "\"\n");
} else {
System.out.println("\nAdd the following lines to the solr.in.sh file so that the ./solr script can use subsequently.\n");
System.out.println("SOLR_AUTH_TYPE=\"kerberos\"\n"
+ "SOLR_AUTHENTICATION_OPTS=\"" + kerberosConfig + "\"\n");
}
}
private void updateIncludeFileEnableAuth(File includeFile, String basicAuthConfFile) throws IOException {
/**
* This will update the include file (e.g. solr.in.sh / solr.in.cmd) with the authentication parameters.
* @param includeFile The include file
* @param basicAuthConfFile If basicAuth, the path of the file containing credentials. If not, null.
* @param kerberosConfig If kerberos, the config string containing startup parameters. If not, null.
*/
private void updateIncludeFileEnableAuth(File includeFile, String basicAuthConfFile, String kerberosConfig) throws IOException {
assert !(basicAuthConfFile != null && kerberosConfig != null); // only one of the two needs to be populated
List<String> includeFileLines = FileUtils.readLines(includeFile, StandardCharsets.UTF_8);
for (int i=0; i<includeFileLines.size(); i++) {
String line = includeFileLines.get(i);
@ -3780,6 +3934,8 @@ public class SolrCLI {
}
}
includeFileLines.add(""); // blank line
if (basicAuthConfFile != null) { // for basicAuth
if (SystemUtils.IS_OS_WINDOWS) {
includeFileLines.add("REM The following lines added by solr.cmd for enabling BasicAuth");
includeFileLines.add("set SOLR_AUTH_TYPE=basic");
@ -3789,9 +3945,23 @@ public class SolrCLI {
includeFileLines.add("SOLR_AUTH_TYPE=\"basic\"");
includeFileLines.add("SOLR_AUTHENTICATION_OPTS=\"-Dsolr.httpclient.config=" + basicAuthConfFile + "\"");
}
} else { // for kerberos
if (SystemUtils.IS_OS_WINDOWS) {
includeFileLines.add("REM The following lines added by solr.cmd for enabling BasicAuth");
includeFileLines.add("set SOLR_AUTH_TYPE=kerberos");
includeFileLines.add("set SOLR_AUTHENTICATION_OPTS=\"-Dsolr.httpclient.config=" + basicAuthConfFile + "\"");
} else {
includeFileLines.add("# The following lines added by ./solr for enabling BasicAuth");
includeFileLines.add("SOLR_AUTH_TYPE=\"kerberos\"");
includeFileLines.add("SOLR_AUTHENTICATION_OPTS=\"" + kerberosConfig + "\"");
}
}
FileUtils.writeLines(includeFile, StandardCharsets.UTF_8.name(), includeFileLines);
System.out.println("Written out credentials file: " + basicAuthConfFile + ", updated Solr include file: " + includeFile.getAbsolutePath() + ".");
if (basicAuthConfFile != null) {
System.out.println("Written out credentials file: " + basicAuthConfFile);
}
System.out.println("Updated Solr include file: " + includeFile.getAbsolutePath());
}
private void updateIncludeFileDisableAuth(File includeFile) throws IOException {