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@8a2a577038
This commit is contained in:
Tim Brooks 2017-07-06 10:37:48 -05:00 committed by GitHub
parent 4e03c657a3
commit d95c365e64
8 changed files with 37 additions and 196 deletions

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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))

View File

@ -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')

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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();