From d95c365e64880e4c514fde7637a75d9145d64542 Mon Sep 17 00:00:00 2001 From: Tim Brooks Date: Thu, 6 Jul 2017 10:37:48 -0500 Subject: [PATCH] Loosen setup mode restrictions for upgrade tests (elastic/x-pack-elasticsearch#1927) This commit is related to elastic/x-pack-elasticsearch#1896. Currently setup mode means that the password must be set post 6.0 for using x-pack. This interferes with upgrade tests as setting the password fails without a properly upgraded security index. This commit loosens two aspects of the security. 1. The old default password will be accept in setup mode (requests from localhost). 2. All request types can be submitted in setup mode. Original commit: elastic/x-pack-elasticsearch@8a2a5770385ea6bf84a63352543479fa0abfd898 --- .../authc/esnative/ReservedRealm.java | 20 +++- .../security/authz/AuthorizationService.java | 11 ++- .../authz/AuthorizationServiceTests.java | 1 + qa/full-cluster-restart/build.gradle | 97 +----------------- .../xpack/restart/FullClusterRestartIT.java | 2 +- qa/rolling-upgrade/build.gradle | 98 +------------------ .../UpgradeClusterClientYamlTestSuiteIT.java | 2 +- .../WatchBackwardsCompatibilityIT.java | 2 +- 8 files changed, 37 insertions(+), 196 deletions(-) diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ReservedRealm.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ReservedRealm.java index aae18d0976a..3b55ce42dbe 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ReservedRealm.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ReservedRealm.java @@ -50,6 +50,7 @@ public class ReservedRealm extends CachingUsernamePasswordRealm { public static final SecureString EMPTY_PASSWORD_TEXT = new SecureString("".toCharArray()); static final char[] EMPTY_PASSWORD_HASH = Hasher.BCRYPT.hash(EMPTY_PASSWORD_TEXT); + static final char[] OLD_DEFAULT_PASSWORD_HASH = Hasher.BCRYPT.hash(new SecureString("changeme".toCharArray())); private static final ReservedUserInfo DEFAULT_USER_INFO = new ReservedUserInfo(EMPTY_PASSWORD_HASH, true, true); private static final ReservedUserInfo DISABLED_USER_INFO = new ReservedUserInfo(EMPTY_PASSWORD_HASH, false, true); @@ -102,9 +103,20 @@ public class ReservedRealm extends CachingUsernamePasswordRealm { Runnable action; if (userInfo != null) { try { - if (userInfo.hasEmptyPassword && isSetupMode(token.principal(), acceptEmptyPassword) == false) { - action = () -> listener.onFailure(Exceptions.authenticationError("failed to authenticate user [{}]", - token.principal())); + if (userInfo.hasEmptyPassword) { + // norelease + // Accepting the OLD_DEFAULT_PASSWORD_HASH is a transition step. We do not want to support + // this in a release. + if (isSetupMode(token.principal(), acceptEmptyPassword) == false) { + action = () -> listener.onFailure(Exceptions.authenticationError("failed to authenticate user [{}]", + token.principal())); + } else if (verifyPassword(userInfo, token) + || Hasher.BCRYPT.verify(token.credentials(), OLD_DEFAULT_PASSWORD_HASH)) { + action = () -> listener.onResponse(getUser(token.principal(), userInfo)); + } else { + action = () -> listener.onFailure(Exceptions.authenticationError("failed to authenticate user [{}]", + token.principal())); + } } else if (verifyPassword(userInfo, token)) { final User user = getUser(token.principal(), userInfo); action = () -> listener.onResponse(user); @@ -113,7 +125,7 @@ public class ReservedRealm extends CachingUsernamePasswordRealm { token.principal())); } } finally { - if (userInfo.passwordHash != EMPTY_PASSWORD_HASH) { + if (userInfo.passwordHash != EMPTY_PASSWORD_HASH && userInfo.passwordHash != OLD_DEFAULT_PASSWORD_HASH) { Arrays.fill(userInfo.passwordHash, (char) 0); } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java index 39a3c992007..10dfb8dc6ff 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java @@ -149,11 +149,14 @@ public class AuthorizationService extends AbstractComponent { throw denial(authentication, action, request); } + // norelease + // TODO: This functionality is disabled as it is not yet compatible with the upgrade process // If the user is the elastic user in setup mode, then only change password requests can be authorized - if (ElasticUser.isElasticUserInSetupMode(authentication.getUser()) - && ChangePasswordAction.NAME.equals(action) == false) { - throw denial(authentication, action, request); - } +// if (ElasticUser.isElasticUserInSetupMode(authentication.getUser()) +// && ChangePasswordAction.NAME.equals(action) == false +// && ClusterHealthAction.NAME.equals(action) == false) { +// throw denial(authentication, action, request); +// } // get the roles of the authenticated user, which may be different than the effective Role permission = userRole; diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java index 89b4eaeae23..98e38474b43 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java @@ -349,6 +349,7 @@ public class AuthorizationServiceTests extends ESTestCase { verifyNoMoreInteractions(auditTrail); } + @AwaitsFix(bugUrl = "https://github.com/elastic/x-pack-elasticsearch/issues/1217") public void testElasticUserOnlyAuthorizedForChangePasswordRequestsInSetupMode() { final User user = new ElasticUser(true, true); final ChangePasswordRequest changePasswordrequest = new ChangePasswordRequestBuilder(mock(Client.class)) diff --git a/qa/full-cluster-restart/build.gradle b/qa/full-cluster-restart/build.gradle index c326f0d9671..ff435e56fca 100644 --- a/qa/full-cluster-restart/build.gradle +++ b/qa/full-cluster-restart/build.gradle @@ -14,99 +14,13 @@ dependencies { testCompile project(path: ':x-pack-elasticsearch:plugin', configuration: 'testArtifacts') } -Closure changePasswordAndWaitWithAuth = { NodeInfo node, AntBuilder ant -> - File tmpFile = new File(node.cwd, 'wait.success') - - String password - if (Version.fromString(node.nodeVersion).onOrAfter('6.0.0')) { - password = "" - } else { - password = "changeme" - } - - for (int i = 0; i < 10; i++) { - HttpURLConnection httpURLConnection = null; - try { - httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_xpack/security/user/elastic/_password") - .openConnection(); - httpURLConnection.setRequestProperty("Authorization", "Basic " + - Base64.getEncoder().encodeToString("elastic:${password}".getBytes(StandardCharsets.UTF_8))); - httpURLConnection.setRequestMethod("PUT"); - httpURLConnection.setDoOutput(true); - httpURLConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); - - httpURLConnection.connect(); - OutputStream out = httpURLConnection.getOutputStream(); - out.write("{\"password\": \"x-pack-test-password\"}".getBytes(StandardCharsets.UTF_8)); - out.close() - - if (httpURLConnection.getResponseCode() == 200) { - break - } - - } catch (Exception e) { - httpURLConnection.disconnect() - if (i == 9) { - logger.error("final attempt to set password", e) - } else { - logger.debug("failed to set elastic password", e) - } - } finally { - if (httpURLConnection != null) { - httpURLConnection.disconnect(); - } - } - - // did not start, so wait a bit before trying again - Thread.sleep(500L); - } - - // wait up to twenty seconds - final long stopTime = System.currentTimeMillis() + 20000L; - Exception lastException = null; - - while (System.currentTimeMillis() < stopTime) { - lastException = null; - // we use custom wait logic here as the elastic user is not available immediately and ant.get will fail when a 401 is returned - HttpURLConnection httpURLConnection = null; - try { - httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health?wait_for_nodes=${node.config.numNodes}&wait_for_status=yellow").openConnection(); - httpURLConnection.setRequestProperty("Authorization", "Basic " + - Base64.getEncoder().encodeToString("elastic:x-pack-test-password".getBytes(StandardCharsets.UTF_8))); - httpURLConnection.setRequestMethod("GET"); - httpURLConnection.setConnectTimeout(1000); - httpURLConnection.setReadTimeout(30000); // read needs to wait for nodes! - httpURLConnection.connect(); - if (httpURLConnection.getResponseCode() == 200) { - tmpFile.withWriter StandardCharsets.UTF_8.name(), { - it.write(httpURLConnection.getInputStream().getText(StandardCharsets.UTF_8.name())) - } - break; - } - } catch (Exception e) { - logger.debug("failed to call cluster health", e) - lastException = e - } finally { - if (httpURLConnection != null) { - httpURLConnection.disconnect(); - } - } - - // did not start, so wait a bit before trying again - Thread.sleep(500L); - } - if (tmpFile.exists() == false && lastException != null) { - logger.error("final attempt of calling cluster health failed", lastException) - } - return tmpFile.exists() -} - - Closure waitWithAuth = { NodeInfo node, AntBuilder ant -> File tmpFile = new File(node.cwd, 'wait.success') + // wait up to twenty seconds final long stopTime = System.currentTimeMillis() + 20000L; Exception lastException = null; + while (System.currentTimeMillis() < stopTime) { lastException = null; // we use custom wait logic here as the elastic user is not available immediately and ant.get will fail when a 401 is returned @@ -114,7 +28,7 @@ Closure waitWithAuth = { NodeInfo node, AntBuilder ant -> try { httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health?wait_for_nodes=${node.config.numNodes}&wait_for_status=yellow").openConnection(); httpURLConnection.setRequestProperty("Authorization", "Basic " + - Base64.getEncoder().encodeToString("elastic:x-pack-test-password".getBytes(StandardCharsets.UTF_8))); + Base64.getEncoder().encodeToString("elastic:changeme".getBytes(StandardCharsets.UTF_8))); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setConnectTimeout(1000); httpURLConnection.setReadTimeout(30000); // read needs to wait for nodes! @@ -202,7 +116,7 @@ subprojects { numBwcNodes = 2 numNodes = 2 clusterName = 'full-cluster-restart' - waitCondition = changePasswordAndWaitWithAuth + waitCondition = waitWithAuth setting 'xpack.security.transport.ssl.enabled', 'true' setting 'xpack.ssl.keystore.path', 'testnode.jks' setting 'xpack.ssl.keystore.password', 'testnode' @@ -277,8 +191,7 @@ subprojects { } } - // NORELEASE : this test must be unmuted once https://github.com/elastic/dev/issues/741 is completed -// check.dependsOn(integTest) + check.dependsOn(integTest) dependencies { testCompile project(path: ':x-pack-elasticsearch:plugin', configuration: 'runtime') diff --git a/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/FullClusterRestartIT.java b/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/FullClusterRestartIT.java index 740ef381573..81a1b1aff0e 100644 --- a/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/FullClusterRestartIT.java +++ b/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/FullClusterRestartIT.java @@ -58,7 +58,7 @@ public class FullClusterRestartIT extends ESRestTestCase { @Override protected Settings restClientSettings() { - String token = "Basic " + Base64.getEncoder().encodeToString("elastic:x-pack-test-password".getBytes(StandardCharsets.UTF_8)); + String token = "Basic " + Base64.getEncoder().encodeToString("elastic:changeme".getBytes(StandardCharsets.UTF_8)); return Settings.builder() .put(ThreadContext.PREFIX + ".Authorization", token) // we increase the timeout here to 90 seconds to handle long waits for a green diff --git a/qa/rolling-upgrade/build.gradle b/qa/rolling-upgrade/build.gradle index 5b1050b7018..55ed25ebdbc 100644 --- a/qa/rolling-upgrade/build.gradle +++ b/qa/rolling-upgrade/build.gradle @@ -23,6 +23,7 @@ Closure waitWithAuth = { NodeInfo node, AntBuilder ant -> Exception lastException = null; while (System.currentTimeMillis() < stopTime) { + lastException = null; // we use custom wait logic here as the elastic user is not available immediately and ant.get will fail when a 401 is returned HttpURLConnection httpURLConnection = null; @@ -30,96 +31,7 @@ Closure waitWithAuth = { NodeInfo node, AntBuilder ant -> // TODO this sucks having to hardcode number of nodes, but node.config.numNodes isn't necessarily accurate for rolling httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health?wait_for_nodes=2&wait_for_status=yellow").openConnection(); httpURLConnection.setRequestProperty("Authorization", "Basic " + - Base64.getEncoder().encodeToString("elastic:x-pack-test-password".getBytes(StandardCharsets.UTF_8))); - httpURLConnection.setRequestMethod("GET"); - httpURLConnection.setConnectTimeout(1000); - httpURLConnection.setReadTimeout(30000); // read needs to wait for nodes! - httpURLConnection.connect(); - if (httpURLConnection.getResponseCode() == 200) { - tmpFile.withWriter StandardCharsets.UTF_8.name(), { - it.write(httpURLConnection.getInputStream().getText(StandardCharsets.UTF_8.name())) - } - break; - } - } catch (Exception e) { - logger.debug("failed to call cluster health", e) - lastException = e - } finally { - if (httpURLConnection != null) { - httpURLConnection.disconnect(); - } - } - - // did not start, so wait a bit before trying again - Thread.sleep(500L); - } - if (tmpFile.exists() == false && lastException != null) { - logger.error("final attempt of calling cluster health failed", lastException) - } - return tmpFile.exists() -} - - -Closure changePasswordAndWaitWithAuth = { NodeInfo node, AntBuilder ant -> - File tmpFile = new File(node.cwd, 'wait.success') - - String password - if (Version.fromString(node.nodeVersion).onOrAfter('6.0.0')) { - password = "" - } else { - password = "changeme" - } - - for (int i = 0; i < 10; i++) { - HttpURLConnection httpURLConnection = null; - try { - httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_xpack/security/user/elastic/_password") - .openConnection(); - httpURLConnection.setRequestProperty("Authorization", "Basic " + - Base64.getEncoder().encodeToString("elastic:${password}".getBytes(StandardCharsets.UTF_8))); - httpURLConnection.setRequestMethod("PUT"); - httpURLConnection.setDoOutput(true); - httpURLConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); - - httpURLConnection.connect(); - OutputStream out = httpURLConnection.getOutputStream(); - out.write("{\"password\": \"x-pack-test-password\"}".getBytes(StandardCharsets.UTF_8)); - out.close() - - if (httpURLConnection.getResponseCode() == 200) { - break - } - - } catch (Exception e) { - httpURLConnection.disconnect() - if (i == 9) { - logger.error("final attempt to set password", e) - } else { - logger.debug("failed to set elastic password", e) - } - } finally { - if (httpURLConnection != null) { - httpURLConnection.disconnect(); - } - } - - // did not start, so wait a bit before trying again - Thread.sleep(500L); - } - - // wait up to twenty seconds - final long stopTime = System.currentTimeMillis() + 20000L; - Exception lastException = null; - - while (System.currentTimeMillis() < stopTime) { - lastException = null; - // we use custom wait logic here as the elastic user is not available immediately and ant.get will fail when a 401 is returned - HttpURLConnection httpURLConnection = null; - try { - // TODO this sucks having to hardcode number of nodes, but node.config.numNodes isn't necessarily accurate for rolling - httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health?wait_for_nodes=2&wait_for_status=yellow").openConnection(); - httpURLConnection.setRequestProperty("Authorization", "Basic " + - Base64.getEncoder().encodeToString("elastic:x-pack-test-password".getBytes(StandardCharsets.UTF_8))); + Base64.getEncoder().encodeToString("elastic:changeme".getBytes(StandardCharsets.UTF_8))); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setConnectTimeout(1000); httpURLConnection.setReadTimeout(30000); // read needs to wait for nodes! @@ -207,7 +119,7 @@ subprojects { numBwcNodes = 2 numNodes = 2 clusterName = 'rolling-upgrade' - waitCondition = changePasswordAndWaitWithAuth + waitCondition = waitWithAuth setting 'xpack.security.transport.ssl.enabled', 'true' setting 'xpack.ssl.keystore.path', 'testnode.jks' setting 'xpack.ssl.keystore.password', 'testnode' @@ -316,8 +228,8 @@ subprojects { dependsOn = ["v${wireCompatVersions[-1]}#bwcTest"] } } - // NORELEASE : this test must be unmuted once https://github.com/elastic/dev/issues/741 is completed -// check.dependsOn(integTest) + + check.dependsOn(integTest) dependencies { testCompile project(path: ':x-pack-elasticsearch:plugin', configuration: 'runtime') diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java index 5c520a60c79..d630e6efe5a 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java @@ -56,7 +56,7 @@ public class UpgradeClusterClientYamlTestSuiteIT extends SecurityClusterClientYa @Override protected Settings restClientSettings() { - String token = "Basic " + Base64.getEncoder().encodeToString("elastic:x-pack-test-password".getBytes(StandardCharsets.UTF_8)); + String token = "Basic " + Base64.getEncoder().encodeToString(("elastic:changeme").getBytes(StandardCharsets.UTF_8)); return Settings.builder() .put(ThreadContext.PREFIX + ".Authorization", token) // we increase the timeout here to 90 seconds to handle long waits for a green diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/WatchBackwardsCompatibilityIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/WatchBackwardsCompatibilityIT.java index f7ab8850240..8da564f3ce7 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/WatchBackwardsCompatibilityIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/WatchBackwardsCompatibilityIT.java @@ -112,7 +112,7 @@ public class WatchBackwardsCompatibilityIT extends ESRestTestCase { @Override protected Settings restClientSettings() { String token = "Basic " + Base64.getEncoder() - .encodeToString("elastic:x-pack-test-password".getBytes(StandardCharsets.UTF_8)); + .encodeToString(("elastic:changeme").getBytes(StandardCharsets.UTF_8)); return Settings.builder() .put(ThreadContext.PREFIX + ".Authorization", token) .build();