You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by th...@apache.org on 2021/07/16 15:16:26 UTC
[solr] branch main updated: SOLR-15525: Read ZK credentials from a
file specified using a system property instead of exposing plain-text
credentials (#222)
This is an automated email from the ASF dual-hosted git repository.
thelabdude 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 9501d3f SOLR-15525: Read ZK credentials from a file specified using a system property instead of exposing plain-text credentials (#222)
9501d3f is described below
commit 9501d3fced2253d60e51c2291ce189d20fe081a6
Author: Timothy Potter <th...@gmail.com>
AuthorDate: Fri Jul 16 09:16:15 2021 -0600
SOLR-15525: Read ZK credentials from a file specified using a system property instead of exposing plain-text credentials (#222)
---
solr/CHANGES.txt | 3 ++
.../VMParamsZkACLAndCredentialsProvidersTest.java | 57 +++++++++++++++++++++-
.../src/zookeeper-access-control.adoc | 2 +
.../VMParamsAllAndReadonlyDigestZkACLProvider.java | 16 ++++--
...eSetCredentialsDigestZkCredentialsProvider.java | 31 ++++++++++--
5 files changed, 98 insertions(+), 11 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index e9cdb6f..82641c1 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -374,6 +374,9 @@ Improvements
* SOLR-15499: StatsStream implement ParallelMetricsRollup to allow for tiered computation of SQL metrics
over collection aliases backed by many collections, potentially with many shards in each (Timothy Potter)
+* SOLR-15525: Read ZK credentials from a file specified using a system property instead of exposing plain-text
+ credentials as system properties (Timothy Potter)
+
Optimizations
---------------------
* SOLR-15433: Replace transient core cache LRU by Caffeine cache. (Bruno Roustant)
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 47408ab..3b441bf 100644
--- a/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
@@ -16,10 +16,13 @@
*/
package org.apache.solr.cloud;
+import java.io.FileWriter;
+import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.nio.charset.Charset;
-import java.nio.file.Path;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.Properties;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.cloud.SecurityAwareZkACLProvider;
@@ -70,7 +73,7 @@ public class VMParamsZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
zkServer.run(false);
System.setProperty("zkHost", zkServer.getZkAddress());
-
+
setSecuritySystemProperties();
SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(),
@@ -155,6 +158,28 @@ public class VMParamsZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
}
@Test
+ public void testReadonlyCredentialsFromFile() throws Exception {
+ useReadonlyCredentialsFromFile();
+
+ try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
+ doTest(zkClient,
+ true, true, false, false, false,
+ false, false, false, false, false);
+ }
+ }
+
+ @Test
+ public void testAllCredentialsFromFile() throws Exception {
+ useAllCredentialsFromFile();
+
+ try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
+ doTest(zkClient,
+ true, true, true, true, true,
+ true, true, true, true, true);
+ }
+ }
+
+ @Test
public void testRepairACL() throws Exception {
clearSecuritySystemProperties();
try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
@@ -250,6 +275,33 @@ public class VMParamsZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
System.setProperty(VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_USERNAME_VM_PARAM_NAME, "readonlyACLUsername");
System.setProperty(VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_PASSWORD_VM_PARAM_NAME, "readonlyACLPassword");
}
+
+ private void useReadonlyCredentialsFromFile() throws IOException {
+ useCredentialsFromFile("readonlyACLUsername", "readonlyACLPassword");
+ }
+
+ private void useAllCredentialsFromFile() throws IOException {
+ useCredentialsFromFile("connectAndAllACLUsername", "connectAndAllACLPassword");
+ }
+
+ private void useCredentialsFromFile(String username, String password) throws IOException {
+ clearSecuritySystemProperties();
+
+ System.setProperty(SolrZkClient.ZK_CRED_PROVIDER_CLASS_NAME_VM_PARAM_NAME, VMParamsSingleSetCredentialsDigestZkCredentialsProvider.class.getName());
+
+ Properties props = new Properties();
+ props.setProperty(VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_USERNAME_VM_PARAM_NAME, username);
+ props.setProperty(VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_PASSWORD_VM_PARAM_NAME, password);
+ saveCredentialsFile(props);
+ }
+
+ private void saveCredentialsFile(Properties props) throws IOException {
+ Path tmp = createTempFile("zk-creds", "properties");
+ try (FileWriter w = new FileWriter(tmp.toFile(), StandardCharsets.UTF_8)) {
+ props.store(w, "test");
+ }
+ System.setProperty(VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_FILE_VM_PARAM_NAME, tmp.toAbsolutePath().toString());
+ }
private void setSecuritySystemProperties() {
System.setProperty(SolrZkClient.ZK_ACL_PROVIDER_CLASS_NAME_VM_PARAM_NAME, VMParamsAllAndReadonlyDigestZkACLProvider.class.getName());
@@ -267,6 +319,7 @@ public class VMParamsZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
System.clearProperty(VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_PASSWORD_VM_PARAM_NAME);
System.clearProperty(VMParamsAllAndReadonlyDigestZkACLProvider.DEFAULT_DIGEST_READONLY_USERNAME_VM_PARAM_NAME);
System.clearProperty(VMParamsAllAndReadonlyDigestZkACLProvider.DEFAULT_DIGEST_READONLY_PASSWORD_VM_PARAM_NAME);
+ System.clearProperty(VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_FILE_VM_PARAM_NAME);
}
}
diff --git a/solr/solr-ref-guide/src/zookeeper-access-control.adoc b/solr/solr-ref-guide/src/zookeeper-access-control.adoc
index c4313d7..946a9f4 100644
--- a/solr/solr-ref-guide/src/zookeeper-access-control.adoc
+++ b/solr/solr-ref-guide/src/zookeeper-access-control.adoc
@@ -86,6 +86,7 @@ It supports at most one set of credentials.
The username and password are defined by system properties `zkDigestUsername` and `zkDigestPassword`.
This set of credentials will be added to the list of credentials returned by `getCredentials()` if both username and password are provided.
** If the one set of credentials above is not added to the list, this implementation will fall back to default behavior and use the (empty) credentials list from `DefaultZkCredentialsProvider`.
+** Alternatively, you can set the `zkDigestCredentialsFile` system property to load the `zkDigestUsername` and `zkDigestPassword` from a file instead of exposing the credentials as system properties. The provided file must be a Java properties file and contain both the `zkDigestUsername` and `zkDigestPassword` properties.
=== Controlling ACLs
@@ -110,6 +111,7 @@ The two sets of roles will be defined as:
*** The permission is `READ` and the schema is `digest`.
*** The username and password are defined by system properties `zkDigestReadonlyUsername` and `zkDigestReadonlyPassword`, respectively.
*** This ACL will not be added to the list of ACLs unless both username and password are provided.
+** Alternatively, you can set the `zkDigestCredentialsFile` system property to load the `zkDigestUsername` and `zkDigestPassword` from a file instead of exposing the credentials as system properties. The provided file must be a Java properties file and contain both the `zkDigestUsername` and `zkDigestPassword` properties for the `ALL` user, as well as the `zkDigestReadonlyUsername` and `zkDigestReadonlyPassword` properties for the `READONLY` user.
* `org.apache.solr.common.cloud.SaslZkACLProvider`: Requires SASL authentication.
Gives all permissions for the user specified in system property `solr.authorization.superuser` (default: `solr`) when using SASL, and gives read permissions for anyone else.
Designed for a setup where configurations have already been set up and will not be modified, or where configuration changes are controlled via Solr APIs.
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 041762a..e75ceb8 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
@@ -19,6 +19,7 @@ package org.apache.solr.common.cloud;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Properties;
import org.apache.solr.common.StringUtils;
import org.apache.zookeeper.ZooDefs;
@@ -26,6 +27,9 @@ import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;
+import static org.apache.solr.common.cloud.VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_FILE_VM_PARAM_NAME;
+import static org.apache.solr.common.cloud.VMParamsSingleSetCredentialsDigestZkCredentialsProvider.readCredentialsFile;
+
public class VMParamsAllAndReadonlyDigestZkACLProvider extends SecurityAwareZkACLProvider {
public static final String DEFAULT_DIGEST_READONLY_USERNAME_VM_PARAM_NAME = "zkDigestReadonlyUsername";
@@ -35,6 +39,7 @@ public class VMParamsAllAndReadonlyDigestZkACLProvider extends SecurityAwareZkAC
final String zkDigestAllPasswordVMParamName;
final String zkDigestReadonlyUsernameVMParamName;
final String zkDigestReadonlyPasswordVMParamName;
+ final Properties credentialsProps;
public VMParamsAllAndReadonlyDigestZkACLProvider() {
this(
@@ -51,6 +56,9 @@ public class VMParamsAllAndReadonlyDigestZkACLProvider extends SecurityAwareZkAC
this.zkDigestAllPasswordVMParamName = zkDigestAllPasswordVMParamName;
this.zkDigestReadonlyUsernameVMParamName = zkDigestReadonlyUsernameVMParamName;
this.zkDigestReadonlyPasswordVMParamName = zkDigestReadonlyPasswordVMParamName;
+
+ String pathToFile = System.getProperty(DEFAULT_DIGEST_FILE_VM_PARAM_NAME);
+ credentialsProps = (pathToFile != null) ? readCredentialsFile(pathToFile) : System.getProperties();
}
/**
@@ -70,10 +78,10 @@ public class VMParamsAllAndReadonlyDigestZkACLProvider extends SecurityAwareZkAC
}
protected List<ACL> createACLsToAdd(boolean includeReadOnly) {
- String digestAllUsername = System.getProperty(zkDigestAllUsernameVMParamName);
- String digestAllPassword = System.getProperty(zkDigestAllPasswordVMParamName);
- String digestReadonlyUsername = System.getProperty(zkDigestReadonlyUsernameVMParamName);
- String digestReadonlyPassword = System.getProperty(zkDigestReadonlyPasswordVMParamName);
+ String digestAllUsername = credentialsProps.getProperty(zkDigestAllUsernameVMParamName);
+ String digestAllPassword = credentialsProps.getProperty(zkDigestAllPasswordVMParamName);
+ String digestReadonlyUsername = credentialsProps.getProperty(zkDigestReadonlyUsernameVMParamName);
+ String digestReadonlyPassword = credentialsProps.getProperty(zkDigestReadonlyPasswordVMParamName);
return createACLsToAdd(includeReadOnly,
digestAllUsername, digestAllPassword,
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsSingleSetCredentialsDigestZkCredentialsProvider.java b/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsSingleSetCredentialsDigestZkCredentialsProvider.java
index 50f1771..a8bda51 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsSingleSetCredentialsDigestZkCredentialsProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsSingleSetCredentialsDigestZkCredentialsProvider.java
@@ -16,21 +16,38 @@
*/
package org.apache.solr.common.cloud;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Properties;
+import org.apache.solr.common.SolrException;
import org.apache.solr.common.StringUtils;
public class VMParamsSingleSetCredentialsDigestZkCredentialsProvider extends DefaultZkCredentialsProvider {
-
+
+ public static final String DEFAULT_DIGEST_FILE_VM_PARAM_NAME = "zkDigestCredentialsFile";
public static final String DEFAULT_DIGEST_USERNAME_VM_PARAM_NAME = "zkDigestUsername";
public static final String DEFAULT_DIGEST_PASSWORD_VM_PARAM_NAME = "zkDigestPassword";
+
+ static Properties readCredentialsFile(String pathToFile) throws SolrException {
+ Properties props = new Properties();
+ try (Reader reader = new InputStreamReader(new FileInputStream(pathToFile), StandardCharsets.UTF_8)) {
+ props.load(reader);
+ } catch (IOException ioExc) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, ioExc);
+ }
+ return props;
+ }
final String zkDigestUsernameVMParamName;
final String zkDigestPasswordVMParamName;
-
+
public VMParamsSingleSetCredentialsDigestZkCredentialsProvider() {
this(DEFAULT_DIGEST_USERNAME_VM_PARAM_NAME, DEFAULT_DIGEST_PASSWORD_VM_PARAM_NAME);
}
@@ -42,9 +59,13 @@ public class VMParamsSingleSetCredentialsDigestZkCredentialsProvider extends Def
@Override
protected Collection<ZkCredentials> createCredentials() {
- List<ZkCredentials> result = new ArrayList<ZkCredentials>();
- String digestUsername = System.getProperty(zkDigestUsernameVMParamName);
- String digestPassword = System.getProperty(zkDigestPasswordVMParamName);
+ List<ZkCredentials> result = new ArrayList<>();
+
+ String pathToFile = System.getProperty(DEFAULT_DIGEST_FILE_VM_PARAM_NAME);
+ Properties props = (pathToFile != null) ? readCredentialsFile(pathToFile) : System.getProperties();
+
+ String digestUsername = props.getProperty(zkDigestUsernameVMParamName);
+ String digestPassword = props.getProperty(zkDigestPasswordVMParamName);
if (!StringUtils.isEmpty(digestUsername) && !StringUtils.isEmpty(digestPassword)) {
result.add(new ZkCredentials("digest",
(digestUsername + ":" + digestPassword).getBytes(StandardCharsets.UTF_8)));