diff --git a/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java b/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java index c6fd0eca9c4..c59a2889c28 100644 --- a/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java +++ b/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java @@ -274,6 +274,13 @@ public class SSLService extends AbstractComponent { */ public boolean isSSLClientAuthEnabled(Settings settings, Settings fallback) { SSLConfiguration sslConfiguration = sslConfiguration(settings, fallback); + return isSSLClientAuthEnabled(sslConfiguration); + } + + /** + * Indicates whether client authentication is enabled for a particular configuration + */ + public boolean isSSLClientAuthEnabled(SSLConfiguration sslConfiguration) { return sslConfiguration.sslClientAuth().enabled(); } diff --git a/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java b/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java index 8c8c13a78d7..dcc2308f9f0 100644 --- a/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java +++ b/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java @@ -12,8 +12,11 @@ import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import static org.elasticsearch.xpack.core.XPackSettings.HTTP_SSL_ENABLED; @@ -22,9 +25,34 @@ import static org.elasticsearch.xpack.core.security.SecurityField.setting; class PkiRealmBootstrapCheck implements BootstrapCheck { private final SSLService sslService; + private final List sslConfigurations; - PkiRealmBootstrapCheck(SSLService sslService) { + PkiRealmBootstrapCheck(Settings settings, SSLService sslService) { this.sslService = sslService; + this.sslConfigurations = loadSslConfigurations(settings); + } + + /** + * {@link SSLConfiguration} may depend on {@link org.elasticsearch.common.settings.SecureSettings} that can only be read during startup. + * We need to preload these during component configuration. + */ + private List loadSslConfigurations(Settings settings) { + final List list = new ArrayList<>(); + if (HTTP_SSL_ENABLED.get(settings)) { + list.add(sslService.sslConfiguration(SSLService.getHttpTransportSSLSettings(settings), Settings.EMPTY)); + } + + if (XPackSettings.TRANSPORT_SSL_ENABLED.get(settings)) { + final Settings transportSslSettings = settings.getByPrefix(setting("transport.ssl.")); + list.add(sslService.sslConfiguration(transportSslSettings, Settings.EMPTY)); + + settings.getGroups("transport.profiles.").values().stream() + .map(SecurityNetty4Transport::profileSslSettings) + .map(s -> sslService.sslConfiguration(s, transportSslSettings)) + .forEach(list::add); + } + + return list; } /** @@ -38,27 +66,8 @@ class PkiRealmBootstrapCheck implements BootstrapCheck { .filter(s -> PkiRealmSettings.TYPE.equals(s.get("type"))) .anyMatch(s -> s.getAsBoolean("enabled", true)); if (pkiRealmEnabled) { - // HTTP - final boolean httpSsl = HTTP_SSL_ENABLED.get(settings); - Settings httpSSLSettings = SSLService.getHttpTransportSSLSettings(settings); - final boolean httpClientAuth = sslService.isSSLClientAuthEnabled(httpSSLSettings); - if (httpSsl && httpClientAuth) { - return BootstrapCheckResult.success(); - } - - // Default Transport - final boolean transportSSLEnabled = XPackSettings.TRANSPORT_SSL_ENABLED.get(settings); - final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl.")); - final boolean clientAuthEnabled = sslService.isSSLClientAuthEnabled(transportSSLSettings); - if (transportSSLEnabled && clientAuthEnabled) { - return BootstrapCheckResult.success(); - } - - // Transport Profiles - Map groupedSettings = settings.getGroups("transport.profiles."); - for (Map.Entry entry : groupedSettings.entrySet()) { - if (transportSSLEnabled && sslService.isSSLClientAuthEnabled( - SecurityNetty4Transport.profileSslSettings(entry.getValue()), transportSSLSettings)) { + for (SSLConfiguration configuration : this.sslConfigurations) { + if (sslService.isSSLClientAuthEnabled(configuration)) { return BootstrapCheckResult.success(); } } diff --git a/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index b6a762dd4d5..a1fa7d09aca 100644 --- a/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -283,7 +283,7 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw final List checks = new ArrayList<>(); checks.addAll(Arrays.asList( new TokenSSLBootstrapCheck(), - new PkiRealmBootstrapCheck(getSslService()), + new PkiRealmBootstrapCheck(settings, getSslService()), new TLSLicenseBootstrapCheck())); checks.addAll(InternalRealms.getBootstrapChecks(settings, env)); this.bootstrapChecks = Collections.unmodifiableList(checks); diff --git a/plugin/security/src/test/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheckTests.java b/plugin/security/src/test/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheckTests.java index c3473b6af00..5610da6f75c 100644 --- a/plugin/security/src/test/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheckTests.java +++ b/plugin/security/src/test/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheckTests.java @@ -5,20 +5,23 @@ */ package org.elasticsearch.xpack.security; +import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.settings.MockSecureSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; import org.elasticsearch.xpack.core.ssl.SSLService; +import org.hamcrest.Matchers; public class PkiRealmBootstrapCheckTests extends ESTestCase { public void testPkiRealmBootstrapDefault() throws Exception { - assertFalse(new PkiRealmBootstrapCheck(new SSLService(Settings.EMPTY, - TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build()))).check( - new BootstrapContext(Settings.EMPTY, null)).isFailure()); + final Settings settings = Settings.EMPTY; + final Environment env = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build()); + assertFalse(runCheck(settings, env).isFailure()); } public void testBootstrapCheckWithPkiRealm() throws Exception { @@ -27,48 +30,48 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase { .put("path.home", createTempDir()) .build(); Environment env = TestEnvironment.newEnvironment(settings); - assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); + assertTrue(runCheck(settings, env).isFailure()); // enable transport tls settings = Settings.builder().put(settings) .put("xpack.security.transport.ssl.enabled", true) .build(); - assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); + assertFalse(runCheck(settings, env).isFailure()); // disable client auth default settings = Settings.builder().put(settings) .put("xpack.ssl.client_authentication", "none") .build(); env = TestEnvironment.newEnvironment(settings); - assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); + assertTrue(runCheck(settings, env).isFailure()); // enable ssl for http settings = Settings.builder().put(settings) .put("xpack.security.http.ssl.enabled", true) .build(); env = TestEnvironment.newEnvironment(settings); - assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); + assertTrue(runCheck(settings, env).isFailure()); // enable client auth for http settings = Settings.builder().put(settings) .put("xpack.security.http.ssl.client_authentication", randomFrom("required", "optional")) .build(); env = TestEnvironment.newEnvironment(settings); - assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); + assertFalse(runCheck(settings, env).isFailure()); // disable http ssl settings = Settings.builder().put(settings) .put("xpack.security.http.ssl.enabled", false) .build(); env = TestEnvironment.newEnvironment(settings); - assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); + assertTrue(runCheck(settings, env).isFailure()); // set transport client auth settings = Settings.builder().put(settings) .put("xpack.security.transport.client_authentication", randomFrom("required", "optional")) .build(); env = TestEnvironment.newEnvironment(settings); - assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); + assertTrue(runCheck(settings, env).isFailure()); // test with transport profile settings = Settings.builder().put(settings) @@ -76,7 +79,11 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase { .put("transport.profiles.foo.xpack.security.ssl.client_authentication", randomFrom("required", "optional")) .build(); env = TestEnvironment.newEnvironment(settings); - assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); + assertFalse(runCheck(settings, env).isFailure()); + } + + private BootstrapCheck.BootstrapCheckResult runCheck(Settings settings, Environment env) throws Exception { + return new PkiRealmBootstrapCheck(settings, new SSLService(settings, env)).check(new BootstrapContext(settings, null)); } public void testBootstrapCheckWithDisabledRealm() throws Exception { @@ -87,6 +94,26 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase { .put("path.home", createTempDir()) .build(); Environment env = TestEnvironment.newEnvironment(settings); - assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); + assertFalse(runCheck(settings, env).isFailure()); + } + + public void testBootstrapCheckWithClosedSecuredSetting() throws Exception { + final boolean expectFail = randomBoolean(); + final MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("xpack.security.http.ssl.keystore.secure_password", "testnode"); + Settings settings = Settings.builder() + .put("xpack.security.authc.realms.test_pki.type", PkiRealmSettings.TYPE) + .put("xpack.security.http.ssl.enabled", true) + .put("xpack.security.http.ssl.client_authentication", expectFail ? "none" : "optional") + .put("xpack.security.http.ssl.keystore.path", + getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks")) + .put("path.home", createTempDir()) + .setSecureSettings(secureSettings) + .build(); + + Environment env = TestEnvironment.newEnvironment(settings); + final PkiRealmBootstrapCheck check = new PkiRealmBootstrapCheck(settings, new SSLService(settings, env)); + secureSettings.close(); + assertThat(check.check(new BootstrapContext(settings, null)).isFailure(), Matchers.equalTo(expectFail)); } }