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 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) 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 Bug Fixes
---------------------- ----------------------
* SOLR-9262: Connection and read timeouts are being ignored by UpdateShardHandler after SOLR-4509. * 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 ""
echo "Usage: solr auth enable [-type basicAuth] -credentials user:pass [-blockUnknown <true|false>] [-updateIncludeFileOnly <true|false>]" 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 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 " solr auth disable [-updateIncludeFileOnly <true|false>]"
echo "" 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 ""
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 " Note: only one of -prompt or -credentials must be provided"
echo "" 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 " Note: only one of -prompt or -credentials must be provided"
echo "" echo ""
echo " -blockUnknown <true|false> When true, this blocks out access to unauthenticated users. When not provided," 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 " 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 " 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 ""
echo " -updateIncludeFileOnly <true|false> Only update the solr.in.sh or solr.in.cmd file, and skip actual enabling/disabling" 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)" 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") AUTH_PARAMS=("${AUTH_PARAMS[@]}" "-credentials" "$AUTH_CREDENTIALS")
shift 2 shift 2
;; ;;
-config)
AUTH_CONFIG="`echo $2| base64`"
AUTH_PARAMS=("${AUTH_PARAMS[@]}" "-config" "$AUTH_CONFIG")
shift 2
;;
-solrIncludeFile) -solrIncludeFile)
SOLR_INCLUDE="$2" SOLR_INCLUDE="$2"
shift 2 shift 2

View File

@ -43,6 +43,7 @@ import java.time.Instant;
import java.time.Period; import java.time.Period;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64;
import java.util.Collection; import java.util.Collection;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; 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.params.ModifiableSolrParams;
import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.ContentStreamBase;
import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.security.Sha256AuthenticationProvider; import org.apache.solr.security.Sha256AuthenticationProvider;
import org.apache.solr.util.configuration.SSLConfigurationsFactory; import org.apache.solr.util.configuration.SSLConfigurationsFactory;
import org.noggit.CharArr; import org.noggit.CharArr;
@ -3548,7 +3550,7 @@ public class SolrCLI {
OptionBuilder OptionBuilder
.withArgName("type") .withArgName("type")
.hasArg() .hasArg()
.withDescription("The authentication mechanism to enable. Defaults to 'basicAuth'.") .withDescription("The authentication mechanism to enable (basicAuth or kerberos). Defaults to 'basicAuth'.")
.create("type"), .create("type"),
OptionBuilder OptionBuilder
.withArgName("credentials") .withArgName("credentials")
@ -3561,6 +3563,11 @@ public class SolrCLI {
.withDescription("Prompts the user to provide the credentials. Use either -credentials or -prompt, not both") .withDescription("Prompts the user to provide the credentials. Use either -credentials or -prompt, not both")
.create("prompt"), .create("prompt"),
OptionBuilder OptionBuilder
.withArgName("config")
.hasArgs()
.withDescription("Configuration parameters (Solr startup parameters). Required for Kerberos authentication")
.create("config"),
OptionBuilder
.withArgName("blockUnknown") .withArgName("blockUnknown")
.withDescription("Blocks all access for unknown users (requires authentication for all endpoints)") .withDescription("Blocks all access for unknown users (requires authentication for all endpoints)")
.hasArg() .hasArg()
@ -3603,11 +3610,141 @@ public class SolrCLI {
} }
String type = cli.getOptionValue("type", "basicAuth"); String type = cli.getOptionValue("type", "basicAuth");
if (type.equalsIgnoreCase("basicAuth") == false) { switch (type) {
System.out.println("Only type=basicAuth supported at the moment."); case "basicAuth":
exit(1); 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]; String cmd = cli.getArgs()[0];
boolean prompt = Boolean.parseBoolean(cli.getOptionValue("prompt", "false")); boolean prompt = Boolean.parseBoolean(cli.getOptionValue("prompt", "false"));
boolean updateIncludeFileOnly = Boolean.parseBoolean(cli.getOptionValue("updateIncludeFileOnly", "false")); boolean updateIncludeFileOnly = Boolean.parseBoolean(cli.getOptionValue("updateIncludeFileOnly", "false"));
@ -3715,7 +3852,7 @@ public class SolrCLI {
"httpBasicAuthUser=" + username + "\nhttpBasicAuthPassword=" + password, StandardCharsets.UTF_8); "httpBasicAuthUser=" + username + "\nhttpBasicAuthPassword=" + password, StandardCharsets.UTF_8);
// update the solr.in.sh file to contain the necessary authentication lines // update the solr.in.sh file to contain the necessary authentication lines
updateIncludeFileEnableAuth(includeFile, basicAuthConfFile.getAbsolutePath()); updateIncludeFileEnableAuth(includeFile, basicAuthConfFile.getAbsolutePath(), null);
return 0; return 0;
case "disable": case "disable":
@ -3754,7 +3891,6 @@ public class SolrCLI {
new HelpFormatter().printHelp("bin/solr auth <enable|disable> [OPTIONS]", getToolOptions(this)); new HelpFormatter().printHelp("bin/solr auth <enable|disable> [OPTIONS]", getToolOptions(this));
return 1; return 1;
} }
private void printAuthEnablingInstructions(String username, String password) { private void printAuthEnablingInstructions(String username, String password) {
if (SystemUtils.IS_OS_WINDOWS) { 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("\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"); + "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); List<String> includeFileLines = FileUtils.readLines(includeFile, StandardCharsets.UTF_8);
for (int i=0; i<includeFileLines.size(); i++) { for (int i=0; i<includeFileLines.size(); i++) {
String line = includeFileLines.get(i); String line = includeFileLines.get(i);
@ -3780,18 +3934,34 @@ public class SolrCLI {
} }
} }
includeFileLines.add(""); // blank line includeFileLines.add(""); // blank line
if (SystemUtils.IS_OS_WINDOWS) {
includeFileLines.add("REM The following lines added by solr.cmd for enabling BasicAuth"); if (basicAuthConfFile != null) { // for basicAuth
includeFileLines.add("set SOLR_AUTH_TYPE=basic"); if (SystemUtils.IS_OS_WINDOWS) {
includeFileLines.add("set SOLR_AUTHENTICATION_OPTS=\"-Dsolr.httpclient.config=" + basicAuthConfFile + "\""); includeFileLines.add("REM The following lines added by solr.cmd for enabling BasicAuth");
} else { includeFileLines.add("set SOLR_AUTH_TYPE=basic");
includeFileLines.add("# The following lines added by ./solr for enabling BasicAuth"); includeFileLines.add("set SOLR_AUTHENTICATION_OPTS=\"-Dsolr.httpclient.config=" + basicAuthConfFile + "\"");
includeFileLines.add("SOLR_AUTH_TYPE=\"basic\""); } else {
includeFileLines.add("SOLR_AUTHENTICATION_OPTS=\"-Dsolr.httpclient.config=" + basicAuthConfFile + "\""); includeFileLines.add("# The following lines added by ./solr for enabling BasicAuth");
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); 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 { private void updateIncludeFileDisableAuth(File includeFile) throws IOException {