Refactor bootstrap check results and error messages

This commit refactors the X-Pack bootstrap checks to respond to a change
in core Elasticsearch where the checks now return a single result
object.

Relates elastic/x-pack-elasticsearch#2495

Original commit: elastic/x-pack-elasticsearch@230b050529
This commit is contained in:
Jason Tedor 2017-09-13 21:30:51 -04:00 committed by GitHub
parent 9ea36ef771
commit 4f3e740ba8
10 changed files with 99 additions and 99 deletions

View File

@ -31,7 +31,7 @@ class PkiRealmBootstrapCheck implements BootstrapCheck {
* least one network communication layer.
*/
@Override
public boolean check(BootstrapContext context) {
public BootstrapCheckResult check(BootstrapContext context) {
final Settings settings = context.settings;
final boolean pkiRealmEnabled = settings.getGroups(RealmSettings.PREFIX).values().stream()
.filter(s -> PkiRealm.TYPE.equals(s.get("type")))
@ -42,34 +42,30 @@ class PkiRealmBootstrapCheck implements BootstrapCheck {
Settings httpSSLSettings = SSLService.getHttpTransportSSLSettings(settings);
final boolean httpClientAuth = sslService.isSSLClientAuthEnabled(httpSSLSettings);
if (httpSsl && httpClientAuth) {
return false;
return BootstrapCheckResult.success();
}
// Default Transport
final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl."));
final boolean clientAuthEnabled = sslService.isSSLClientAuthEnabled(transportSSLSettings);
if (clientAuthEnabled) {
return false;
return BootstrapCheckResult.success();
}
// Transport Profiles
Map<String, Settings> groupedSettings = settings.getGroups("transport.profiles.");
for (Map.Entry<String, Settings> entry : groupedSettings.entrySet()) {
if (sslService.isSSLClientAuthEnabled(SecurityNetty4Transport.profileSslSettings(entry.getValue()), transportSSLSettings)) {
return false;
return BootstrapCheckResult.success();
}
}
return true;
return BootstrapCheckResult.failure(
"a PKI realm is enabled but cannot be used as neither HTTP or Transport have SSL and client authentication enabled");
} else {
return false;
return BootstrapCheckResult.success();
}
}
@Override
public String errorMessage() {
return "A PKI realm is enabled but cannot be used as neither HTTP or Transport have SSL and client authentication enabled";
}
@Override
public boolean alwaysEnforce() {
return true;

View File

@ -11,24 +11,29 @@ import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.XPackSettings;
import java.util.Locale;
/**
* Bootstrap check to ensure that the user has enabled HTTPS when using the token service
*/
final class TokenSSLBootstrapCheck implements BootstrapCheck {
@Override
public boolean check(BootstrapContext context) {
if (NetworkModule.HTTP_ENABLED.get(context.settings)) {
return XPackSettings.HTTP_SSL_ENABLED.get(context.settings) == false && XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.get
(context.settings);
public BootstrapCheckResult check(BootstrapContext context) {
final Boolean httpEnabled = NetworkModule.HTTP_ENABLED.get(context.settings);
final Boolean httpsEnabled = XPackSettings.HTTP_SSL_ENABLED.get(context.settings);
final Boolean tokenServiceEnabled = XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.get(context.settings);
if (httpEnabled && httpsEnabled == false && tokenServiceEnabled) {
final String message = String.format(
Locale.ROOT,
"HTTPS is required in order to use the token service; "
+ "please enable HTTPS using the [%s] setting or disable the token service using the [%s] setting",
XPackSettings.HTTP_SSL_ENABLED.getKey(),
XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey());
return BootstrapCheckResult.failure(message);
} else {
return BootstrapCheckResult.success();
}
return false;
}
@Override
public String errorMessage() {
return "HTTPS is required in order to use the token service. Please enable HTTPS using the [" +
XPackSettings.HTTP_SSL_ENABLED.getKey() + "] setting or disable the token service using the [" +
XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey() + "] setting.";
}
}

View File

@ -20,30 +20,22 @@ public class RoleMappingFileBootstrapCheck implements BootstrapCheck {
private final RealmConfig realmConfig;
private final Path path;
private final SetOnce<String> error = new SetOnce<>();
public RoleMappingFileBootstrapCheck(RealmConfig config, Path path) {
RoleMappingFileBootstrapCheck(RealmConfig config, Path path) {
this.realmConfig = config;
this.path = path;
}
@Override
public boolean check(BootstrapContext context) {
public BootstrapCheckResult check(BootstrapContext context) {
try {
DnRoleMapper.parseFile(path, realmConfig.logger(getClass()), realmConfig.type(), realmConfig.name(), true);
return false;
return BootstrapCheckResult.success();
} catch (Exception e) {
error.set(e.getMessage());
return true;
return BootstrapCheckResult.failure(e.getMessage());
}
}
@Override
public String errorMessage() {
return error.get();
}
@Override
public boolean alwaysEnforce() {
return true;
@ -56,4 +48,5 @@ public class RoleMappingFileBootstrapCheck implements BootstrapCheck {
}
return null;
}
}

View File

@ -42,10 +42,15 @@ public final class SSLBootstrapCheck implements BootstrapCheck {
}
@Override
public boolean check(BootstrapContext context) {
public BootstrapCheckResult check(BootstrapContext context) {
final Settings transportSSLSettings = context.settings.getByPrefix(XPackSettings.TRANSPORT_SSL_PREFIX);
return sslService.sslConfiguration(transportSSLSettings).keyConfig() == KeyConfig.NONE
|| isDefaultCACertificateTrusted() || isDefaultPrivateKeyUsed();
if (sslService.sslConfiguration(transportSSLSettings).keyConfig() == KeyConfig.NONE
|| isDefaultCACertificateTrusted() || isDefaultPrivateKeyUsed()) {
return BootstrapCheckResult.failure(
"default SSL key and certificate do not provide security; please generate keys and certificates");
} else {
return BootstrapCheckResult.success();
}
}
/**
@ -91,8 +96,4 @@ public final class SSLBootstrapCheck implements BootstrapCheck {
.anyMatch(defaultPrivateKey::equals);
}
@Override
public String errorMessage() {
return "Default SSL key and certificate do not provide security; please generate keys and certificates";
}
}

View File

@ -23,27 +23,28 @@ final class EncryptSensitiveDataBootstrapCheck implements BootstrapCheck {
}
@Override
public boolean check(BootstrapContext context) {
return Watcher.ENCRYPT_SENSITIVE_DATA_SETTING.get(context.settings)
&& Watcher.ENCRYPTION_KEY_SETTING.exists(context.settings) == false;
}
@Override
public String errorMessage() {
final Path sysKeyPath = environment.configFile().resolve(XPackPlugin.NAME).resolve("system_key").toAbsolutePath();
if (Files.exists(sysKeyPath)) {
return "Encryption of sensitive data requires the key to be placed in the secure setting store. Run " +
"'bin/elasticsearch-keystore add-file " + Watcher.ENCRYPTION_KEY_SETTING.getKey() + " " +
environment.configFile().resolve(XPackPlugin.NAME).resolve("system_key").toAbsolutePath() +
"' to import the file.\nAfter importing, the system_key file should be removed from the " +
"filesystem.\nRepeat this on every node in the cluster.";
public BootstrapCheckResult check(BootstrapContext context) {
if (Watcher.ENCRYPT_SENSITIVE_DATA_SETTING.get(context.settings)
&& Watcher.ENCRYPTION_KEY_SETTING.exists(context.settings) == false) {
final Path systemKeyPath = environment.configFile().resolve(XPackPlugin.NAME).resolve("system_key").toAbsolutePath();
final String message;
if (Files.exists(systemKeyPath)) {
message = "Encryption of sensitive data requires the key to be placed in the secure setting store. Run " +
"'bin/elasticsearch-keystore add-file " + Watcher.ENCRYPTION_KEY_SETTING.getKey() + " " +
systemKeyPath +
"' to import the file.\nAfter importing, the system_key file should be removed from the " +
"filesystem.\nRepeat this on every node in the cluster.";
} else {
message = "Encryption of sensitive data requires a key to be placed in the secure setting store. First run the " +
"bin/x-pack/syskeygen tool to generate a key file.\nThen run 'bin/elasticsearch-keystore add-file " +
Watcher.ENCRYPTION_KEY_SETTING.getKey() + " " +
systemKeyPath + "' to import the key into" +
" the secure setting store. Finally, remove the system_key file from the filesystem.\n" +
"Repeat this on every node in the cluster";
}
return BootstrapCheckResult.failure(message);
} else {
return "Encryption of sensitive data requires a key to be placed in the secure setting store. First run the " +
"bin/x-pack/syskeygen tool to generate a key file.\nThen run 'bin/elasticsearch-keystore add-file " +
Watcher.ENCRYPTION_KEY_SETTING.getKey() + " " +
environment.configFile().resolve(XPackPlugin.NAME).resolve("system_key").toAbsolutePath() + "' to import the key into" +
" the secure setting store. Finally, remove the system_key file from the filesystem.\n" +
"Repeat this on every node in the cluster";
return BootstrapCheckResult.success();
}
}

View File

@ -17,7 +17,7 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase {
public void testPkiRealmBootstrapDefault() throws Exception {
assertFalse(new PkiRealmBootstrapCheck(new SSLService(Settings.EMPTY,
new Environment(Settings.builder().put("path.home", createTempDir()).build()))).check((new BootstrapContext(Settings
.EMPTY, null))));
.EMPTY, null))).isFailure());
}
public void testBootstrapCheckWithPkiRealm() throws Exception {
@ -26,42 +26,42 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Environment env = new Environment(settings);
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)));
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
// disable client auth default
settings = Settings.builder().put(settings)
.put("xpack.ssl.client_authentication", "none")
.build();
env = new Environment(settings);
assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)));
assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
// enable ssl for http
settings = Settings.builder().put(settings)
.put("xpack.security.http.ssl.enabled", true)
.build();
env = new Environment(settings);
assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)));
assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
// enable client auth for http
settings = Settings.builder().put(settings)
.put("xpack.security.http.ssl.client_authentication", randomFrom("required", "optional"))
.build();
env = new Environment(settings);
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)));
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
// disable http ssl
settings = Settings.builder().put(settings)
.put("xpack.security.http.ssl.enabled", false)
.build();
env = new Environment(settings);
assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)));
assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
// set transport client auth
settings = Settings.builder().put(settings)
.put("xpack.security.transport.client_authentication", randomFrom("required", "optional"))
.build();
env = new Environment(settings);
assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)));
assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
// test with transport profile
settings = Settings.builder().put(settings)
@ -69,7 +69,7 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase {
.put("transport.profiles.foo.xpack.security.ssl.client_authentication", randomFrom("required", "optional"))
.build();
env = new Environment(settings);
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)));
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
}
public void testBootstrapCheckWithDisabledRealm() throws Exception {
@ -80,6 +80,6 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Environment env = new Environment(settings);
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)));
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
}
}

View File

@ -16,29 +16,29 @@ public class TokenSSLBootsrapCheckTests extends ESTestCase {
public void testTokenSSLBootstrapCheck() {
Settings settings = Settings.EMPTY;
assertFalse(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)));
assertFalse(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
settings = Settings.builder()
.put(NetworkModule.HTTP_ENABLED.getKey(), false)
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true).build();
assertFalse(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)));
assertFalse(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
settings = Settings.builder().put(XPackSettings.HTTP_SSL_ENABLED.getKey(), true).build();
assertFalse(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)));
assertFalse(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
// XPackSettings.HTTP_SSL_ENABLED default false
settings = Settings.builder().put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true).build();
assertTrue(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)));
assertTrue(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
settings = Settings.builder()
.put(XPackSettings.HTTP_SSL_ENABLED.getKey(), false)
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true).build();
assertTrue(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)));
assertTrue(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
settings = Settings.builder()
.put(XPackSettings.HTTP_SSL_ENABLED.getKey(), false)
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true)
.put(NetworkModule.HTTP_ENABLED.getKey(), false).build();
assertFalse(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)));
assertFalse(new TokenSSLBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
}
}

View File

@ -46,7 +46,7 @@ public class RoleMappingFileBootstrapCheckTests extends ESTestCase {
final BootstrapCheck check = RoleMappingFileBootstrapCheck.create(config);
assertThat(check, notNullValue());
assertThat(check.alwaysEnforce(), equalTo(true));
assertThat(check.check(new BootstrapContext(settings, null)), equalTo(false));
assertFalse(check.check(new BootstrapContext(settings, null)).isFailure());
}
public void testBootstrapCheckOfMissingFile() {
@ -59,10 +59,11 @@ public class RoleMappingFileBootstrapCheckTests extends ESTestCase {
final BootstrapCheck check = RoleMappingFileBootstrapCheck.create(config);
assertThat(check, notNullValue());
assertThat(check.alwaysEnforce(), equalTo(true));
assertThat(check.check(new BootstrapContext(settings, null)), equalTo(true));
assertThat(check.errorMessage(), containsString("the-realm-name"));
assertThat(check.errorMessage(), containsString(fileName));
assertThat(check.errorMessage(), containsString("does not exist"));
final BootstrapCheck.BootstrapCheckResult result = check.check(new BootstrapContext(settings, null));
assertTrue(result.isFailure());
assertThat(result.getMessage(), containsString("the-realm-name"));
assertThat(result.getMessage(), containsString(fileName));
assertThat(result.getMessage(), containsString("does not exist"));
}
public void testBootstrapCheckWithInvalidYaml() throws IOException {
@ -77,10 +78,11 @@ public class RoleMappingFileBootstrapCheckTests extends ESTestCase {
final BootstrapCheck check = RoleMappingFileBootstrapCheck.create(config);
assertThat(check, notNullValue());
assertThat(check.alwaysEnforce(), equalTo(true));
assertThat(check.check(new BootstrapContext(settings, null)), equalTo(true));
assertThat(check.errorMessage(), containsString("the-realm-name"));
assertThat(check.errorMessage(), containsString(file.toString()));
assertThat(check.errorMessage(), containsString("could not read"));
final BootstrapCheck.BootstrapCheckResult result = check.check(new BootstrapContext(settings, null));
assertTrue(result.isFailure());
assertThat(result.getMessage(), containsString("the-realm-name"));
assertThat(result.getMessage(), containsString(file.toString()));
assertThat(result.getMessage(), containsString("could not read"));
}
public void testBootstrapCheckWithInvalidDn() throws IOException {
@ -95,10 +97,11 @@ public class RoleMappingFileBootstrapCheckTests extends ESTestCase {
final BootstrapCheck check = RoleMappingFileBootstrapCheck.create(config);
assertThat(check, notNullValue());
assertThat(check.alwaysEnforce(), equalTo(true));
assertThat(check.check(new BootstrapContext(settings, null)), equalTo(true));
assertThat(check.errorMessage(), containsString("the-realm-name"));
assertThat(check.errorMessage(), containsString(file.toString()));
assertThat(check.errorMessage(), containsString("invalid DN"));
assertThat(check.errorMessage(), containsString("not-a-dn"));
final BootstrapCheck.BootstrapCheckResult result = check.check(new BootstrapContext(settings, null));
assertTrue(result.isFailure());
assertThat(result.getMessage(), containsString("the-realm-name"));
assertThat(result.getMessage(), containsString(file.toString()));
assertThat(result.getMessage(), containsString("invalid DN"));
assertThat(result.getMessage(), containsString("not-a-dn"));
}
}

View File

@ -16,7 +16,7 @@ public class SSLBootstrapCheckTests extends ESTestCase {
public void testSSLBootstrapCheckWithNoKey() throws Exception {
SSLService sslService = new SSLService(Settings.EMPTY, null);
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(sslService, null);
assertTrue(bootstrapCheck.check(new BootstrapContext(Settings.EMPTY, null)));
assertTrue(bootstrapCheck.check(new BootstrapContext(Settings.EMPTY, null)).isFailure());
}
public void testSSLBootstrapCheckWithKey() throws Exception {
@ -33,7 +33,7 @@ public class SSLBootstrapCheckTests extends ESTestCase {
.build();
final Environment env = randomBoolean() ? new Environment(settings) : null;
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), env);
assertFalse(bootstrapCheck.check(new BootstrapContext(settings, null)));
assertFalse(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
}
public void testSSLBootstrapCheckWithDefaultCABeingTrusted() throws Exception {
@ -53,14 +53,14 @@ public class SSLBootstrapCheckTests extends ESTestCase {
.build();
final Environment env = randomBoolean() ? new Environment(settings) : null;
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), env);
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)));
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
settings = Settings.builder().put(settings.filter((s) -> s.contains(".certificate_authorities")))
.put("xpack.security.http.ssl.certificate_authorities",
getDataPath("/org/elasticsearch/xpack/ssl/ca.pem").toString())
.build();
bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), env);
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)));
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
}
public void testSSLBootstrapCheckWithDefaultKeyBeingUsed() throws Exception {
@ -79,7 +79,7 @@ public class SSLBootstrapCheckTests extends ESTestCase {
.build();
final Environment env = randomBoolean() ? new Environment(settings) : null;
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), env);
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)));
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
settings = Settings.builder().put(settings.filter((s) -> s.contains(".http.ssl.")))
.put("xpack.security.transport.profiles.foo.xpack.security.ssl.key",
@ -88,6 +88,6 @@ public class SSLBootstrapCheckTests extends ESTestCase {
getDataPath("/org/elasticsearch/xpack/ssl/ca.pem").toString())
.build();
bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), env);
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)));
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
}
}

View File

@ -18,7 +18,7 @@ public class EncryptSensitiveDataBootstrapCheckTests extends ESTestCase {
Settings settings = Settings.builder().put("path.home", createTempDir()).build();
Environment env = new Environment(settings);
EncryptSensitiveDataBootstrapCheck check = new EncryptSensitiveDataBootstrapCheck(env);
assertFalse(check.check(new BootstrapContext(settings, null)));
assertFalse(check.check(new BootstrapContext(settings, null)).isFailure());
assertTrue(check.alwaysEnforce());
}
@ -29,7 +29,7 @@ public class EncryptSensitiveDataBootstrapCheckTests extends ESTestCase {
.build();
Environment env = new Environment(settings);
EncryptSensitiveDataBootstrapCheck check = new EncryptSensitiveDataBootstrapCheck(env);
assertTrue(check.check(new BootstrapContext(settings, null)));
assertTrue(check.check(new BootstrapContext(settings, null)).isFailure());
}
public void testKeyInKeystore() {
@ -42,6 +42,7 @@ public class EncryptSensitiveDataBootstrapCheckTests extends ESTestCase {
.build();
Environment env = new Environment(settings);
EncryptSensitiveDataBootstrapCheck check = new EncryptSensitiveDataBootstrapCheck(env);
assertFalse(check.check(new BootstrapContext(settings, null)));
assertFalse(check.check(new BootstrapContext(settings, null)).isFailure());
}
}