From 41af46688a48692744e8ebc3d8a9396c1c1f7678 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Mon, 12 Mar 2018 10:48:35 +1000 Subject: [PATCH] Make PKI BootstrapCheck work with SecureSettings (elastic/x-pack-elasticsearch#3993) SslConfiguration can depend on SecureSettings, so it must be constructed during the correct lifecycle phase. For PkiRealmBootstrapCheck, moved the construction of SslConfiguration objets into the constructor rather than the check method Original commit: elastic/x-pack-elasticsearch@1a4d147216bf45e35747761cf778be1e39f97631 --- .../xpack/core/ssl/SSLService.java | 7 +++ .../security/PkiRealmBootstrapCheck.java | 53 +++++++++++-------- .../xpack/security/Security.java | 2 +- .../security/PkiRealmBootstrapCheckTests.java | 51 +++++++++++++----- 4 files changed, 78 insertions(+), 35 deletions(-) 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)); } }