This is an automated email from the ASF dual-hosted git repository.
mdrob pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new efc7331 SOLR-15249 Properly set ZK ACLs on /security.json
efc7331 is described below
commit efc73319a7c29d8c2c96a287941f740a27fe0a76
Author: Mike Drob <[email protected]>
AuthorDate: Mon Mar 15 15:50:45 2021 -0500
SOLR-15249 Properly set ZK ACLs on /security.json
---
.../randomization/policies/solr-tests.policy | 4 +
solr/CHANGES.txt | 2 +
.../java/org/apache/solr/cloud/ZkController.java | 7 +-
.../apache/solr/cloud/KerberosTestServices.java | 52 ++++-----
.../src/test/org/apache/solr/cloud/LocaleTest.java | 90 +++++++++++++++
.../apache/solr/cloud/SaslZkACLProviderTest.java | 12 +-
.../VMParamsZkACLAndCredentialsProvidersTest.java | 122 +++++++--------------
.../authentication-and-authorization-plugins.adoc | 2 +
.../src/basic-authentication-plugin.adoc | 9 +-
.../src/zookeeper-access-control.adoc | 38 +++++--
.../solr/common/cloud/SaslZkACLProvider.java | 17 ++-
.../common/cloud/SecurityAwareZkACLProvider.java | 9 +-
.../org/apache/solr/common/cloud/SolrZkClient.java | 15 ++-
.../VMParamsAllAndReadonlyDigestZkACLProvider.java | 4 +-
.../apache/solr/common/cloud/ZkCmdExecutor.java | 30 ++++-
.../java/org/apache/solr/cloud/ZkTestServer.java | 37 +++++--
16 files changed, 274 insertions(+), 176 deletions(-)
diff --git a/gradle/testing/randomization/policies/solr-tests.policy
b/gradle/testing/randomization/policies/solr-tests.policy
index fa51ca4..5330bb2 100644
--- a/gradle/testing/randomization/policies/solr-tests.policy
+++ b/gradle/testing/randomization/policies/solr-tests.policy
@@ -147,6 +147,10 @@ grant {
// SASL/Kerberos related properties for Solr tests
permission javax.security.auth.PrivateCredentialPermission
"javax.security.auth.kerberos.KerberosTicket * \"*\"", "read";
+
+ // Needed by zookeeper to configure SASL Auth in tests
+ permission javax.security.auth.AuthPermission "createLoginContext.Server";
+ permission javax.security.auth.AuthPermission "createLoginContext.Client";
// may only be necessary with Java 7?
permission javax.security.auth.PrivateCredentialPermission
"javax.security.auth.kerberos.KeyTab * \"*\"", "read";
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 0cc414e..0b0e237 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -240,6 +240,8 @@ Bug Fixes
* SOLR-15162: Allow readOnly parameter to be used with v2 modify collection
command (Eric Pugh)
+* SOLR-15249: Properly set ZK ACLs on /security.json (Mike Drob)
+
================== 8.9.0 ==================
Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this
release.
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 232b045..0a7e733 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -109,6 +109,7 @@ import static
org.apache.solr.common.cloud.ZkStateReader.ELECTION_NODE_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.NODE_NAME_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.REJOIN_AT_HEAD_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
+import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;
/**
* Handle ZooKeeper interactions.
@@ -885,7 +886,11 @@ public class ZkController implements Closeable {
cmdExecutor.ensureExists(ZkStateReader.COLLECTIONS_ZKNODE, zkClient);
cmdExecutor.ensureExists(ZkStateReader.ALIASES, zkClient);
byte[] emptyJson = "{}".getBytes(StandardCharsets.UTF_8);
- cmdExecutor.ensureExists(ZkStateReader.SOLR_SECURITY_CONF_PATH, emptyJson,
CreateMode.PERSISTENT, zkClient);
+ cmdExecutor.ensureExists(ZkStateReader.SOLR_SECURITY_CONF_PATH, emptyJson,
zkClient);
+ if (zkClient.getACL(ZkStateReader.SOLR_SECURITY_CONF_PATH, null,
true).equals(OPEN_ACL_UNSAFE)) {
+ log.warn("Contents of zookeeper /security.json are world-readable;" +
+ " consider setting up ACLs as described in
https://solr.apache.org/guide/zookeeper-access-control.html");
+ }
bootstrapDefaultConfigSet(zkClient);
}
diff --git a/solr/core/src/test/org/apache/solr/cloud/KerberosTestServices.java
b/solr/core/src/test/org/apache/solr/cloud/KerberosTestServices.java
index 0e9587b..35ce634 100644
--- a/solr/core/src/test/org/apache/solr/cloud/KerberosTestServices.java
+++ b/solr/core/src/test/org/apache/solr/cloud/KerberosTestServices.java
@@ -39,19 +39,17 @@ public class KerberosTestServices {
private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private volatile MiniKdc kdc;
- private volatile JaasConfiguration jaasConfiguration;
- private volatile Configuration savedConfig;
+ private final JaasConfiguration jaasConfiguration;
+ private final Configuration savedConfig;
private volatile Locale savedLocale;
- private volatile File workDir;
+ private final File workDir;
private KerberosTestServices(File workDir,
JaasConfiguration jaasConfiguration,
- Configuration savedConfig,
- Locale savedLocale) {
+ Configuration savedConfig) {
this.jaasConfiguration = jaasConfiguration;
this.savedConfig = savedConfig;
- this.savedLocale = savedLocale;
this.workDir = workDir;
}
@@ -60,7 +58,8 @@ public class KerberosTestServices {
}
public void start() throws Exception {
- if
(brokenLanguagesWithMiniKdc.contains(Locale.getDefault().getLanguage())) {
+ if
(incompatibleLanguagesWithMiniKdc.contains(Locale.getDefault().getLanguage())) {
+ savedLocale = Locale.getDefault();
Locale.setDefault(Locale.US);
}
@@ -94,7 +93,7 @@ public class KerberosTestServices {
if (kdc != null) kdc.stop();
Configuration.setConfiguration(savedConfig);
Krb5HttpClientBuilder.regenerateJaasConfiguration();
- Locale.setDefault(savedLocale);
+ if (savedLocale != null) Locale.setDefault(savedLocale);
}
public static Builder builder() {
@@ -144,7 +143,7 @@ public class KerberosTestServices {
clientOptions.put("debug", "true");
}
clientEntry = new AppConfigurationEntry[]{
- new AppConfigurationEntry(getKrb5LoginModuleName(),
+ new AppConfigurationEntry(krb5LoginModuleName,
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
clientOptions)};
if(serverPrincipal!=null && serverKeytab!=null) {
@@ -152,7 +151,7 @@ public class KerberosTestServices {
serverOptions.put("principal", serverPrincipal);
serverOptions.put("keytab", serverKeytab.getAbsolutePath());
serverEntry = new AppConfigurationEntry[]{
- new AppConfigurationEntry(getKrb5LoginModuleName(),
+ new AppConfigurationEntry(krb5LoginModuleName,
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
serverOptions)};
}
@@ -181,27 +180,19 @@ public class KerberosTestServices {
}
return null;
}
-
- private String getKrb5LoginModuleName() {
- String krb5LoginModuleName;
- if (System.getProperty("java.vendor").contains("IBM")) {
- krb5LoginModuleName = "com.ibm.security.auth.module.Krb5LoginModule";
- } else {
- krb5LoginModuleName = "com.sun.security.auth.module.Krb5LoginModule";
- }
- return krb5LoginModuleName;
- }
}
+ public final static String krb5LoginModuleName =
+ System.getProperty("java.vendor").contains("IBM") ?
+ "com.ibm.security.auth.module.Krb5LoginModule" :
+ "com.sun.security.auth.module.Krb5LoginModule";
+
/**
- * These Locales don't generate dates that are compatibile with Hadoop
MiniKdc.
+ * These Locales don't generate dates that are compatible with Hadoop
MiniKdc.
+ * See LocaleTest.java and https://issues.apache.org/jira/browse/DIRKRB-753
*/
- private final static List<String> brokenLanguagesWithMiniKdc =
- Arrays.asList(
- new Locale("th").getLanguage(),
- new Locale("ja").getLanguage(),
- new Locale("hi").getLanguage()
- );
+ private final static List<String> incompatibleLanguagesWithMiniKdc =
Arrays.asList(
+ "mzn", "ps", "mr", "uz", "ks", "bn", "my", "sd", "pa", "ar", "th", "dz",
"ja", "ne", "ckb", "fa", "lrc", "ur", "ig");
public static class Builder {
private File kdcWorkDir;
@@ -210,11 +201,8 @@ public class KerberosTestServices {
private String serverPrincipal;
private File serverKeytab;
private String appName;
- private Locale savedLocale;
- public Builder() {
- savedLocale = Locale.getDefault();
- }
+ public Builder() { }
public Builder withKdc(File kdcWorkDir) {
this.kdcWorkDir = kdcWorkDir;
@@ -248,7 +236,7 @@ public class KerberosTestServices {
new JaasConfiguration(clientPrincipal, clientKeytab,
serverPrincipal, serverKeytab) :
new JaasConfiguration(clientPrincipal, clientKeytab, appName);
}
- return new KerberosTestServices(kdcWorkDir, jaasConfiguration,
oldConfig, savedLocale);
+ return new KerberosTestServices(kdcWorkDir, jaasConfiguration,
oldConfig);
}
}
}
diff --git a/solr/core/src/test/org/apache/solr/cloud/LocaleTest.java
b/solr/core/src/test/org/apache/solr/cloud/LocaleTest.java
new file mode 100644
index 0000000..040fe97
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/LocaleTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.cloud;
+
+import org.apache.hadoop.minikdc.MiniKdc;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.solr.SolrTestCase;
+import org.apache.solr.util.LogLevel;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.io.File;
+import java.lang.invoke.MethodHandles;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
[email protected](bugUrl =
"https://issues.apache.org/jira/browse/DIRKRB-753")
+@LogLevel("org.apache.kerby=WARN")
+public class LocaleTest extends SolrTestCase {
+ private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ @Test
+ public void testWithKdc() throws Exception {
+ final String principal = "server";
+
+ Path kdcDir = createTempDir().resolve("miniKdc");
+ File keytabFile = kdcDir.resolve("keytabs").toFile();
+
+ Properties conf = MiniKdc.createConf();
+ conf.setProperty("kdc.port", "0");
+ MiniKdc kdc = new MiniKdc(conf, kdcDir.toFile());
+ kdc.start();
+ kdc.createPrincipal(keytabFile, principal);
+
+ AppConfigurationEntry appConfigEntry = new AppConfigurationEntry(
+ KerberosTestServices.krb5LoginModuleName,
+ AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+ Map.of("principal", principal,
+ "storeKey", "true",
+ "useKeyTab", "true",
+ "useTicketCache", "false",
+ "refreshKrb5Config", "true",
+ "keyTab", keytabFile.getAbsolutePath(),
+ "keytab", keytabFile.getAbsolutePath())
+ );
+ Configuration configuration = new Configuration() {
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+ return new AppConfigurationEntry[]{appConfigEntry};
+ }
+ };
+
+ List<Locale> locales = new ArrayList<>();
+ for (Locale locale : Locale.getAvailableLocales()) {
+ try {
+ Locale.setDefault(locale);
+ new LoginContext("Server", null, null, configuration).login();
+ } catch (LoginException e) {
+ locales.add(locale);
+ }
+ }
+ log.error("Could not login with locales {}",
locales.stream().collect(Collectors.groupingBy(Locale::getLanguage)));
+
+ kdc.stop();
+ }
+}
diff --git
a/solr/core/src/test/org/apache/solr/cloud/SaslZkACLProviderTest.java
b/solr/core/src/test/org/apache/solr/cloud/SaslZkACLProviderTest.java
index 0bdb149..f1875d6 100644
--- a/solr/core/src/test/org/apache/solr/cloud/SaslZkACLProviderTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/SaslZkACLProviderTest.java
@@ -20,6 +20,7 @@ import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import org.apache.lucene.util.Constants;
@@ -50,14 +51,12 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
- private static final Charset DATA_ENCODING = Charset.forName("UTF-8");
+ private static final Charset DATA_ENCODING = StandardCharsets.UTF_8;
protected ZkTestServer zkServer;
@BeforeClass
public static void beforeClass() {
- assumeFalse("FIXME: This test fails on Java 9
(https://issues.apache.org/jira/browse/SOLR-8052)",
Constants.JRE_IS_MINIMUM_JAVA9);
-
assumeFalse("FIXME: SOLR-7040: This test fails under IBM J9",
Constants.JAVA_VENDOR.startsWith("IBM"));
System.setProperty("solrcloud.skip.autorecovery", "true");
@@ -122,7 +121,6 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
}
@Test
- @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/SOLR-13075")
public void testSaslZkACLProvider() throws Exception {
// Test with Sasl enabled
SolrZkClient zkClient = new SolrZkClientWithACLs(zkServer.getZkAddress(),
AbstractZkTestCase.TIMEOUT);
@@ -182,7 +180,7 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
* A ZkTestServer with Sasl support
*/
public static class SaslZkTestServer extends ZkTestServer {
- private Path kdcDir;
+ private final Path kdcDir;
private KerberosTestServices kerberosTestServices;
public SaslZkTestServer(Path zkDir, Path kdcDir) throws Exception {
@@ -204,7 +202,6 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
kerberosTestServices = KerberosTestServices.builder()
.withKdc(kdcDir.toFile())
.withJaasConfiguration(zkClientPrincipal, keytabFile,
zkServerPrincipal, keytabFile)
-
.build();
kerberosTestServices.start();
@@ -221,7 +218,8 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
System.clearProperty("zookeeper.kerberos.removeRealmFromPrincipal");
System.clearProperty("zookeeper.kerberos.removeHostFromPrincipal");
super.shutdown();
- kerberosTestServices.stop();
+ if (kerberosTestServices != null)
+ kerberosTestServices.stop();
}
}
}
diff --git
a/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
b/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
index b9db03d..a8a3c74 100644
---
a/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
+++
b/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
@@ -38,7 +38,7 @@ public class VMParamsZkACLAndCredentialsProvidersTest extends
SolrTestCaseJ4 {
private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
- private static final Charset DATA_ENCODING = Charset.forName("UTF-8");
+ private static final Charset DATA_ENCODING = StandardCharsets.UTF_8;
protected ZkTestServer zkServer;
@@ -111,28 +111,22 @@ public class VMParamsZkACLAndCredentialsProvidersTest
extends SolrTestCaseJ4 {
@Test
public void testNoCredentials() throws Exception {
useNoCredentials();
-
- SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(),
AbstractZkTestCase.TIMEOUT);
- try {
+
+ try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(),
AbstractZkTestCase.TIMEOUT)) {
doTest(zkClient,
- false, false, false, false, false,
- false, false, false, false, false);
- } finally {
- zkClient.close();
+ false, false, false, false, false,
+ false, false, false, false, false);
}
}
@Test
public void testWrongCredentials() throws Exception {
useWrongCredentials();
-
- SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(),
AbstractZkTestCase.TIMEOUT);
- try {
+
+ try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(),
AbstractZkTestCase.TIMEOUT)) {
doTest(zkClient,
- false, false, false, false, false,
- false, false, false, false, false);
- } finally {
- zkClient.close();
+ false, false, false, false, false,
+ false, false, false, false, false);
}
}
@@ -140,13 +134,10 @@ public class VMParamsZkACLAndCredentialsProvidersTest
extends SolrTestCaseJ4 {
public void testAllCredentials() throws Exception {
useAllCredentials();
- SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(),
AbstractZkTestCase.TIMEOUT);
- try {
+ try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(),
AbstractZkTestCase.TIMEOUT)) {
doTest(zkClient,
- true, true, true, true, true,
- true, true, true, true, true);
- } finally {
- zkClient.close();
+ true, true, true, true, true,
+ true, true, true, true, true);
}
}
@@ -154,13 +145,10 @@ public class VMParamsZkACLAndCredentialsProvidersTest
extends SolrTestCaseJ4 {
public void testReadonlyCredentials() throws Exception {
useReadonlyCredentials();
- SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(),
AbstractZkTestCase.TIMEOUT);
- try {
+ try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(),
AbstractZkTestCase.TIMEOUT)) {
doTest(zkClient,
- true, true, false, false, false,
- false, false, false, false, false);
- } finally {
- zkClient.close();
+ true, true, false, false, false,
+ false, false, false, false, false);
}
}
@@ -176,65 +164,35 @@ public class VMParamsZkACLAndCredentialsProvidersTest
extends SolrTestCaseJ4 {
}
protected static void doTest(SolrZkClient zkClient, String path, boolean
getData, boolean list, boolean create, boolean setData, boolean delete) throws
Exception {
- try {
- zkClient.getData(path, null, null, false);
- if (!getData) fail("NoAuthException expected ");
- } catch (NoAuthException nae) {
- if (getData) fail("No NoAuthException expected");
- // expected
- }
-
- try {
- zkClient.getChildren(path, null, false);
- if (!list) fail("NoAuthException expected ");
- } catch (NoAuthException nae) {
- if (list) fail("No NoAuthException expected");
- // expected
- }
-
- try {
+ doTest(getData, () -> zkClient.getData(path, null, null, false));
+ doTest(list, () -> zkClient.getChildren(path, null, false));
+
+ doTest(create, () -> {
zkClient.create(path + "/subnode", null, CreateMode.PERSISTENT, false);
- if (!create) fail("NoAuthException expected ");
- else {
- zkClient.delete(path + "/subnode", -1, false);
- }
- } catch (NoAuthException nae) {
- if (create) {
- nae.printStackTrace();
- fail("No NoAuthException expected");
- }
- // expected
- }
-
- try {
+ zkClient.delete(path + "/subnode", -1, false);
+ });
+ doTest(create, () -> {
zkClient.makePath(path + "/subnode/subsubnode", false);
- if (!create) fail("NoAuthException expected ");
- else {
- zkClient.delete(path + "/subnode/subsubnode", -1, false);
- zkClient.delete(path + "/subnode", -1, false);
- }
- } catch (NoAuthException nae) {
- if (create) fail("No NoAuthException expected");
- // expected
- }
-
- try {
- zkClient.setData(path, (byte[])null, false);
- if (!setData) fail("NoAuthException expected ");
- } catch (NoAuthException nae) {
- if (setData) fail("No NoAuthException expected");
- // expected
- }
+ zkClient.delete(path + "/subnode/subsubnode", -1, false);
+ zkClient.delete(path + "/subnode", -1, false);
+ });
- try {
- // Actually about the ACLs on /solr, but that is protected
- zkClient.delete(path, -1, false);
- if (!delete) fail("NoAuthException expected ");
- } catch (NoAuthException nae) {
- if (delete) fail("No NoAuthException expected");
- // expected
- }
+ doTest(setData, () -> zkClient.setData(path, (byte[])null, false));
+ // Actually about the ACLs on /solr, but that is protected
+ doTest(delete, () -> zkClient.delete(path, -1, false));
+ }
+
+ interface ExceptingRunnable {
+ void run() throws Exception;
+ }
+
+ private static void doTest(boolean shouldSucceed, ExceptingRunnable action)
throws Exception {
+ if (shouldSucceed) {
+ action.run();
+ } else {
+ expectThrows(NoAuthException.class, action::run);
+ }
}
private void useNoCredentials() {
diff --git
a/solr/solr-ref-guide/src/authentication-and-authorization-plugins.adoc
b/solr/solr-ref-guide/src/authentication-and-authorization-plugins.adoc
index 5b237c6..965006b 100644
--- a/solr/solr-ref-guide/src/authentication-and-authorization-plugins.adoc
+++ b/solr/solr-ref-guide/src/authentication-and-authorization-plugins.adoc
@@ -88,6 +88,8 @@ Then use the `bin/solr zk` command to upload the file:
>bin/solr zk cp ./security.json zk:security.json -z localhost:2181
----
+NOTE: If you have defined `ZK_HOST` in `solr.in.sh`/`solr.in.cmd` (see
<<setting-up-an-external-zookeeper-ensemble#updating-solr-include-files,instructions>>)
you can omit `-z <zk host string>` from the above command.
+
[WARNING]
====
Whenever you use any security plugins and store `security.json` in ZooKeeper,
we highly recommend that you implement access control in your ZooKeeper nodes.
Information about how to enable this is available in the section
<<zookeeper-access-control.adoc#,ZooKeeper Access Control>>.
diff --git a/solr/solr-ref-guide/src/basic-authentication-plugin.adoc
b/solr/solr-ref-guide/src/basic-authentication-plugin.adoc
index c6c3b37..28ca11a 100644
--- a/solr/solr-ref-guide/src/basic-authentication-plugin.adoc
+++ b/solr/solr-ref-guide/src/basic-authentication-plugin.adoc
@@ -64,14 +64,7 @@ If `blockUnknown` does not appear in the `security.json`
file, it will default t
If `realm` is not defined, it will default to `solr`.
-If you are using SolrCloud, you must upload `security.json` to ZooKeeper. You
can use this example command, ensuring that the ZooKeeper port is correct:
-
-[source,bash]
-----
-$ bin/solr zk cp file:path_to_local_security.json zk:/security.json -z
localhost:9983
-----
-
-NOTE: If you have defined `ZK_HOST` in `solr.in.sh`/`solr.in.cmd` (see
<<setting-up-an-external-zookeeper-ensemble#updating-solr-include-files,instructions>>)
you can omit `-z <zk host string>` from the above command.
+If you are using SolrCloud, you must upload `security.json` to ZooKeeper. An
example command and more information about securing your setup can be found at
<<authentication-and-authorization-plugins#in-solrcloud-mode,Authentication and
Authorization Plugins In SolrCloud Mode>>.
=== Caveats
diff --git a/solr/solr-ref-guide/src/zookeeper-access-control.adoc
b/solr/solr-ref-guide/src/zookeeper-access-control.adoc
index 57e9af5..9a90458 100644
--- a/solr/solr-ref-guide/src/zookeeper-access-control.adoc
+++ b/solr/solr-ref-guide/src/zookeeper-access-control.adoc
@@ -73,7 +73,7 @@ You control which ACLs will be added by configuring
`zkACLProvider` property in
You can always make you own implementation, but Solr comes with:
* `org.apache.solr.common.cloud.DefaultZkACLProvider`: It returns a list of
length one for all `zNodePath`-s. The single ACL entry in the list is
"open-unsafe". This is the default.
-* `org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider`:
This lets you define your ACLs using system properties. Its `getACLsToAdd()`
implementation does not use `zNodePath` for anything, so all znodes will get
the same set of ACLs. It supports adding one or both of these options:
+* `org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider`:
This lets you define your ACLs using system properties. Its `getACLsToAdd()`
implementation will apply only admin ACLs to pre-defined sensitive paths as
defined by `SecurityAwareZkACLProvider` (`/security.json` and `/security/*`)
and both admin and user ACLs to the rest of the contents. The two sets of roles
will be defined as:
** A user that is allowed to do everything.
*** The permission is `ALL` (corresponding to all of `CREATE`, `READ`,
`WRITE`, `DELETE`, and `ADMIN`), and the schema is "digest".
*** The username and password are defined by system properties
`zkDigestUsername` and `zkDigestPassword`, respectively.
@@ -97,8 +97,17 @@ There are two scripts that impact ZooKeeper ACLs:
* For *nix systems: `bin/solr` & `server/scripts/cloud-scripts/zkcli.sh`
* For Windows systems: `bin/solr.cmd` &
`server/scripts/cloud-scripts/zkcli.bat`
+[IMPORTANT]
+Both the solr.in.* and the zkcli.* files will need to be updated with the same
password for everything to work. The contents may appear redundant, but the
scripts will not consult each other during operations.
+
These Solr scripts can enable use of ZooKeeper ACLs by setting the appropriate
system properties: uncomment the following and replace the passwords with ones
you choose to enable the above-described VM parameters ACL and credentials
providers in the following files:
+[.dynamic-tabs]
+--
+[example.tab-pane#nix]
+====
+[.tab-label]**nix*
+
.solr.in.sh
[source,bash]
----
@@ -110,6 +119,21 @@ These Solr scripts can enable use of ZooKeeper ACLs by
setting the appropriate s
#SOLR_OPTS="$SOLR_OPTS $SOLR_ZK_CREDS_AND_ACLS"
----
+.zkcli.sh
+[source,bash]
+----
+# Settings for ZK ACL
+#SOLR_ZK_CREDS_AND_ACLS="-DzkACLProvider=org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider
\
+#
-DzkCredentialsProvider=org.apache.solr.common.cloud.VMParamsSingleSetCredentialsDigestZkCredentialsProvider
\
+# -DzkDigestUsername=admin-user -DzkDigestPassword=CHANGEME-ADMIN-PASSWORD \
+# -DzkDigestReadonlyUsername=readonly-user
-DzkDigestReadonlyPassword=CHANGEME-READONLY-PASSWORD"
+----
+====
+
+[example.tab-pane#windows]
+====
+[.tab-label]*Windows*
+
.solr.in.cmd
[source,powershell]
----
@@ -121,16 +145,6 @@ REM -DzkDigestReadonlyUsername=readonly-user
-DzkDigestReadonlyPassword=CHANGEM
REM set SOLR_OPTS=%SOLR_OPTS% %SOLR_ZK_CREDS_AND_ACLS%
----
-.zkcli.sh
-[source,bash]
-----
-# Settings for ZK ACL
-#SOLR_ZK_CREDS_AND_ACLS="-DzkACLProvider=org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider
\
-#
-DzkCredentialsProvider=org.apache.solr.common.cloud.VMParamsSingleSetCredentialsDigestZkCredentialsProvider
\
-# -DzkDigestUsername=admin-user -DzkDigestPassword=CHANGEME-ADMIN-PASSWORD \
-# -DzkDigestReadonlyUsername=readonly-user
-DzkDigestReadonlyPassword=CHANGEME-READONLY-PASSWORD"
-----
-
.zkcli.bat
[source,powershell]
----
@@ -140,6 +154,8 @@ REM
-DzkCredentialsProvider=org.apache.solr.common.cloud.VMParamsSingleSetCrede
REM -DzkDigestUsername=admin-user -DzkDigestPassword=CHANGEME-ADMIN-PASSWORD ^
REM -DzkDigestReadonlyUsername=readonly-user
-DzkDigestReadonlyPassword=CHANGEME-READONLY-PASSWORD
----
+====
+--
== Changing ACL Schemes
diff --git
a/solr/solrj/src/java/org/apache/solr/common/cloud/SaslZkACLProvider.java
b/solr/solrj/src/java/org/apache/solr/common/cloud/SaslZkACLProvider.java
index c67ad12..793169b 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/SaslZkACLProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/SaslZkACLProvider.java
@@ -16,7 +16,8 @@
*/
package org.apache.solr.common.cloud;
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import org.apache.zookeeper.ZooDefs;
@@ -32,20 +33,18 @@ import org.apache.zookeeper.data.Id;
*/
public class SaslZkACLProvider extends SecurityAwareZkACLProvider {
- private static String superUser =
System.getProperty("solr.authorization.superuser", "solr");
+ private static final String superUser =
System.getProperty("solr.authorization.superuser", "solr");
@Override
protected List<ACL> createNonSecurityACLsToAdd() {
- List<ACL> ret = new ArrayList<ACL>();
- ret.add(new ACL(ZooDefs.Perms.ALL, new Id("sasl", superUser)));
- ret.add(new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE));
- return ret;
+ return Arrays.asList(
+ new ACL(ZooDefs.Perms.ALL, new Id("sasl", superUser)),
+ new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE)
+ );
}
@Override
protected List<ACL> createSecurityACLsToAdd() {
- List<ACL> ret = new ArrayList<ACL>();
- ret.add(new ACL(ZooDefs.Perms.ALL, new Id("sasl", superUser)));
- return ret;
+ return Collections.singletonList(new ACL(ZooDefs.Perms.ALL, new Id("sasl",
superUser)));
}
}
diff --git
a/solr/solrj/src/java/org/apache/solr/common/cloud/SecurityAwareZkACLProvider.java
b/solr/solrj/src/java/org/apache/solr/common/cloud/SecurityAwareZkACLProvider.java
index 1c74d94..2fe2da9 100644
---
a/solr/solrj/src/java/org/apache/solr/common/cloud/SecurityAwareZkACLProvider.java
+++
b/solr/solrj/src/java/org/apache/solr/common/cloud/SecurityAwareZkACLProvider.java
@@ -22,7 +22,7 @@ import org.apache.zookeeper.data.ACL;
/**
* {@link ZkACLProvider} capable of returning a different set of
- * {@link ACL}s for security-related znodes (default: subtree under /security)
+ * {@link ACL}s for security-related znodes (default: subtree under /security
and security.json)
* vs non-security-related znodes.
*/
public abstract class SecurityAwareZkACLProvider implements ZkACLProvider {
@@ -42,11 +42,8 @@ public abstract class SecurityAwareZkACLProvider implements
ZkACLProvider {
}
protected boolean isSecurityZNodePath(String zNodePath) {
- if (zNodePath != null
- && (zNodePath.equals(SECURITY_ZNODE_PATH) ||
zNodePath.startsWith(SECURITY_ZNODE_PATH + "/"))) {
- return true;
- }
- return false;
+ return zNodePath != null
+ && (zNodePath.equals(ZkStateReader.SOLR_SECURITY_CONF_PATH) ||
zNodePath.equals(SECURITY_ZNODE_PATH) ||
zNodePath.startsWith(SECURITY_ZNODE_PATH + "/"));
}
/**
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/SolrZkClient.java
b/solr/solrj/src/java/org/apache/solr/common/cloud/SolrZkClient.java
index 7a12c92..01078b8 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/SolrZkClient.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/SolrZkClient.java
@@ -786,11 +786,23 @@ public class SolrZkClient implements Closeable {
}
/**
+ * @return the ACLs on a single node in ZooKeeper.
+ */
+ public List<ACL> getACL(String path, Stat stat, boolean retryOnConnLoss)
throws KeeperException, InterruptedException {
+ if (retryOnConnLoss) {
+ return zkCmdExecutor.retryOperation(() -> keeper.getACL(path, stat));
+ } else {
+ return keeper.getACL(path, stat);
+ }
+ }
+
+ /**
* Set the ACL on a single node in ZooKeeper. This will replace all existing
ACL on that node.
*
* @param path path to set ACL on e.g. /solr/conf/solrconfig.xml
* @param acls a list of {@link ACL}s to be applied
* @param retryOnConnLoss true if the command should be retried on
connection loss
+ * @return the stat of the node
*/
public Stat setACL(String path, List<ACL> acls, boolean retryOnConnLoss)
throws InterruptedException, KeeperException {
if (retryOnConnLoss) {
@@ -809,9 +821,8 @@ public class SolrZkClient implements Closeable {
try {
setACL(path, getZkACLProvider().getACLsToAdd(path), true);
log.debug("Updated ACL on {}", path);
- } catch (NoNodeException e) {
+ } catch (NoNodeException ignored) {
// If a node was deleted, don't bother trying to set ACLs on it.
- return;
}
});
}
diff --git
a/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsAllAndReadonlyDigestZkACLProvider.java
b/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsAllAndReadonlyDigestZkACLProvider.java
index 8a41d06..041762a 100644
---
a/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsAllAndReadonlyDigestZkACLProvider.java
+++
b/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsAllAndReadonlyDigestZkACLProvider.java
@@ -88,7 +88,7 @@ public class VMParamsAllAndReadonlyDigestZkACLProvider
extends SecurityAwareZkAC
String digestReadonlyUsername, String
digestReadonlyPassword) {
try {
- List<ACL> result = new ArrayList<ACL>();
+ List<ACL> result = new ArrayList<>(2);
// Not to have to provide too much credentials and ACL information to
the process it is assumed that you want "ALL"-acls
// added to the user you are using to connect to ZK (if you are using
VMParamsSingleSetCredentialsDigestZkCredentialsProvider)
@@ -109,7 +109,7 @@ public class VMParamsAllAndReadonlyDigestZkACLProvider
extends SecurityAwareZkAC
return result;
} catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
+ throw new RuntimeException("JVM mis-configured: missing SHA-1
algorithm", e);
}
}
diff --git
a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkCmdExecutor.java
b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkCmdExecutor.java
index 3598b0a..61be129 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkCmdExecutor.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkCmdExecutor.java
@@ -101,19 +101,38 @@ public class ZkCmdExecutor {
return isClosed != null && isClosed.isClosed();
}
+ /**
+ * Create a persistent znode with no data if it does not already exist
+ * @see #ensureExists(String, byte[], CreateMode, SolrZkClient, int)
+ */
public void ensureExists(String path, final SolrZkClient zkClient) throws
KeeperException, InterruptedException {
ensureExists(path, null, CreateMode.PERSISTENT, zkClient, 0);
}
-
-
+
+ /**
+ * Create a persistent znode with the given data if it does not already exist
+ * @see #ensureExists(String, byte[], CreateMode, SolrZkClient, int)
+ */
public void ensureExists(String path, final byte[] data, final SolrZkClient
zkClient) throws KeeperException, InterruptedException {
ensureExists(path, data, CreateMode.PERSISTENT, zkClient, 0);
}
-
+
+ /**
+ * Create a znode with the given mode and data if it does not already exist
+ * @see #ensureExists(String, byte[], CreateMode, SolrZkClient, int)
+ */
public void ensureExists(String path, final byte[] data, CreateMode
createMode, final SolrZkClient zkClient) throws KeeperException,
InterruptedException {
ensureExists(path, data, createMode, zkClient, 0);
}
-
+
+ /**
+ * Create a node if it does not exist
+ * @param path the path at which to create the znode
+ * @param data the optional data to set on the znode
+ * @param createMode the mode with which to create the znode
+ * @param zkClient the client to use to check and create
+ * @param skipPathParts how many path elements to skip
+ */
public void ensureExists(final String path, final byte[] data,
CreateMode createMode, final SolrZkClient zkClient, int skipPathParts)
throws KeeperException, InterruptedException {
@@ -122,10 +141,9 @@ public class ZkCmdExecutor {
}
try {
zkClient.makePath(path, data, createMode, null, true, true,
skipPathParts);
- } catch (NodeExistsException e) {
+ } catch (NodeExistsException ignored) {
// it's okay if another beats us creating the node
}
-
}
/**
diff --git
a/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
b/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
index dbb3916..5112aab 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
@@ -69,6 +69,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
public class ZkTestServer {
@@ -388,11 +389,17 @@ public class ZkTestServer {
zkDb.close();
}
- if (cnxnFactory != null && cnxnFactory.getLocalPort() != 0) {
- waitForServerDown(getZkHost(), 30000);
+ if (cnxnFactory != null) {
+ try {
+ int port = cnxnFactory.getLocalPort();
+ if (port > 0) {
+ waitForServerDown(getZkHost(), 30000);
+ }
+ } catch (NullPointerException ignored) {
+ // server never successfully started
+ }
}
} finally {
-
ObjectReleaseTracker.release(this);
}
}
@@ -532,10 +539,12 @@ public class ZkTestServer {
public void run(boolean solrFormat) throws InterruptedException, IOException
{
log.info("STARTING ZK TEST SERVER");
+ AtomicReference<Throwable> zooError = new AtomicReference<>();
try {
if (zooThread != null) {
throw new IllegalStateException("ZK TEST SERVER IS ALREADY RUNNING");
}
+ Thread parentThread = Thread.currentThread();
// we don't call super.distribSetUp
zooThread = new Thread("ZkTestServer Run Thread") {
@@ -569,7 +578,8 @@ public class ZkTestServer {
try {
zkServer.runFromConfig(config);
} catch (Throwable t) {
- log.error("zkServer error", t);
+ zooError.set(t);
+ parentThread.interrupt();
}
}
};
@@ -581,15 +591,15 @@ public class ZkTestServer {
int port = -1;
try {
port = getPort();
- } catch (IllegalStateException e) {
-
+ } catch (IllegalStateException ignored) {
+ // Possibly fix this API to return null instead of throwing
}
while (port < 1) {
Thread.sleep(100);
try {
port = getPort();
- } catch (IllegalStateException e) {
-
+ } catch (IllegalStateException ignored) {
+ // Possibly fix this API to return null instead of throwing
}
if (cnt == 500) {
throw new RuntimeException("Could not get the port for ZooKeeper
server");
@@ -602,8 +612,15 @@ public class ZkTestServer {
init(solrFormat);
} catch (Exception e) {
- log.error("Error trying to run ZK Test Server", e);
- throw new RuntimeException(e);
+ RuntimeException toThrow = new RuntimeException("Could not get ZK port");
+ Throwable t = zooError.get();
+ if (t != null) {
+ toThrow.initCause(t);
+ toThrow.addSuppressed(e);
+ } else {
+ toThrow.initCause(e);
+ }
+ throw toThrow;
}
}