Skip to content

Commit

Permalink
SOLR-15249 Attempt to repair security.json ACLs
Browse files Browse the repository at this point in the history
  • Loading branch information
madrob committed Apr 6, 2021
1 parent 06fad78 commit 2e4ddc7
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 5 deletions.
37 changes: 34 additions & 3 deletions solr/core/src/java/org/apache/solr/cloud/ZkController.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import org.apache.zookeeper.Op;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -856,9 +857,39 @@ public static void createClusterZkNodes(SolrZkClient zkClient)
cmdExecutor.ensureExists(ZkStateReader.ALIASES, zkClient);
byte[] emptyJson = "{}".getBytes(StandardCharsets.UTF_8);
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");
repairSecurityJson(zkClient);
}

private static void repairSecurityJson(SolrZkClient zkClient) throws KeeperException, InterruptedException {
List<ACL> securityConfAcl = zkClient.getACL(ZkStateReader.SOLR_SECURITY_CONF_PATH, null, true);
ZkACLProvider aclProvider = zkClient.getZkACLProvider();

boolean tryUpdate = false;

if (OPEN_ACL_UNSAFE.equals(securityConfAcl)) {
List<ACL> aclToAdd = aclProvider.getACLsToAdd(ZkStateReader.SOLR_SECURITY_CONF_PATH);
if (OPEN_ACL_UNSAFE.equals(aclToAdd)) {
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");
} else {
tryUpdate = true;
}
} else if (aclProvider instanceof SecurityAwareZkACLProvider) {
// Use Set to explicitly ignore order
Set<ACL> nonSecureACL = new HashSet<>(aclProvider.getACLsToAdd(null));
// case where security.json was not treated as a secure path
if (nonSecureACL.equals(new HashSet<>(securityConfAcl))) {
tryUpdate = true;
}
}

if (tryUpdate) {
if (Boolean.getBoolean("solr.security.aclautorepair.disable")) {
log.warn("Detected inconsistent ACLs for zookeeper /security.json, but self-repair is disabled.");
} else {
log.info("Detected inconsistent ACLs for zookeeper /security.json, attempting to repair.");
zkClient.updateACLs(ZkStateReader.SOLR_SECURITY_CONF_PATH);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;

public class VMParamsZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {

private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Expand Down Expand Up @@ -151,6 +153,32 @@ public void testReadonlyCredentials() throws Exception {
false, false, false, false, false);
}
}

@Test
public void testRepairACL() throws Exception {
clearSecuritySystemProperties();
try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
// Currently no credentials on ZK connection, because those same VM-params are used for adding ACLs, and here we want
// no (or completely open) ACLs added. Therefore hack your way into being authorized for creating anyway
zkClient.getSolrZooKeeper().addAuthInfo("digest", ("connectAndAllACLUsername:connectAndAllACLPassword")
.getBytes(StandardCharsets.UTF_8));

zkClient.create("/security.json", "{}".getBytes(StandardCharsets.UTF_8), CreateMode.PERSISTENT, false);
assertEquals(OPEN_ACL_UNSAFE, zkClient.getACL("/security.json", null, false));
}

setSecuritySystemProperties();
try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
ZkController.createClusterZkNodes(zkClient);
assertNotEquals(OPEN_ACL_UNSAFE, zkClient.getACL("/security.json", null, false));
}

useReadonlyCredentials();
try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
NoAuthException e = assertThrows(NoAuthException.class, () -> zkClient.getData("/security.json", null, null, false));
assertEquals("/security.json", e.getPath());
}
}

protected static void doTest(
SolrZkClient zkClient,
Expand Down Expand Up @@ -202,7 +230,7 @@ private void useNoCredentials() {
private void useWrongCredentials() {
clearSecuritySystemProperties();

System.setProperty(SolrZkClient.ZK_ACL_PROVIDER_CLASS_NAME_VM_PARAM_NAME, VMParamsSingleSetCredentialsDigestZkCredentialsProvider.class.getName());
System.setProperty(SolrZkClient.ZK_CRED_PROVIDER_CLASS_NAME_VM_PARAM_NAME, VMParamsSingleSetCredentialsDigestZkCredentialsProvider.class.getName());
System.setProperty(VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_USERNAME_VM_PARAM_NAME, "connectAndAllACLUsername");
System.setProperty(VMParamsSingleSetCredentialsDigestZkCredentialsProvider.DEFAULT_DIGEST_PASSWORD_VM_PARAM_NAME, "connectAndAllACLPasswordWrong");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public abstract class SecurityAwareZkACLProvider implements ZkACLProvider {


@Override
public List<ACL> getACLsToAdd(String zNodePath) {
public final List<ACL> getACLsToAdd(String zNodePath) {
if (isSecurityZNodePath(zNodePath)) {
return getSecurityACLsToAdd();
} else {
Expand Down

0 comments on commit 2e4ddc7

Please sign in to comment.