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@1a4d147216
This commit is contained in:
Tim Vernum 2018-03-12 10:48:35 +10:00 committed by GitHub
parent d31d90d378
commit 41af46688a
4 changed files with 78 additions and 35 deletions

View File

@ -274,6 +274,13 @@ public class SSLService extends AbstractComponent {
*/ */
public boolean isSSLClientAuthEnabled(Settings settings, Settings fallback) { public boolean isSSLClientAuthEnabled(Settings settings, Settings fallback) {
SSLConfiguration sslConfiguration = sslConfiguration(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(); return sslConfiguration.sslClientAuth().enabled();
} }

View File

@ -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.RealmSettings;
import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings;
import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport; import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport;
import org.elasticsearch.xpack.core.ssl.SSLConfiguration;
import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.core.ssl.SSLService;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import static org.elasticsearch.xpack.core.XPackSettings.HTTP_SSL_ENABLED; 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 { class PkiRealmBootstrapCheck implements BootstrapCheck {
private final SSLService sslService; private final SSLService sslService;
private final List<SSLConfiguration> sslConfigurations;
PkiRealmBootstrapCheck(SSLService sslService) { PkiRealmBootstrapCheck(Settings settings, SSLService sslService) {
this.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<SSLConfiguration> loadSslConfigurations(Settings settings) {
final List<SSLConfiguration> 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"))) .filter(s -> PkiRealmSettings.TYPE.equals(s.get("type")))
.anyMatch(s -> s.getAsBoolean("enabled", true)); .anyMatch(s -> s.getAsBoolean("enabled", true));
if (pkiRealmEnabled) { if (pkiRealmEnabled) {
// HTTP for (SSLConfiguration configuration : this.sslConfigurations) {
final boolean httpSsl = HTTP_SSL_ENABLED.get(settings); if (sslService.isSSLClientAuthEnabled(configuration)) {
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<String, Settings> groupedSettings = settings.getGroups("transport.profiles.");
for (Map.Entry<String, Settings> entry : groupedSettings.entrySet()) {
if (transportSSLEnabled && sslService.isSSLClientAuthEnabled(
SecurityNetty4Transport.profileSslSettings(entry.getValue()), transportSSLSettings)) {
return BootstrapCheckResult.success(); return BootstrapCheckResult.success();
} }
} }

View File

@ -283,7 +283,7 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
final List<BootstrapCheck> checks = new ArrayList<>(); final List<BootstrapCheck> checks = new ArrayList<>();
checks.addAll(Arrays.asList( checks.addAll(Arrays.asList(
new TokenSSLBootstrapCheck(), new TokenSSLBootstrapCheck(),
new PkiRealmBootstrapCheck(getSslService()), new PkiRealmBootstrapCheck(settings, getSslService()),
new TLSLicenseBootstrapCheck())); new TLSLicenseBootstrapCheck()));
checks.addAll(InternalRealms.getBootstrapChecks(settings, env)); checks.addAll(InternalRealms.getBootstrapChecks(settings, env));
this.bootstrapChecks = Collections.unmodifiableList(checks); this.bootstrapChecks = Collections.unmodifiableList(checks);

View File

@ -5,20 +5,23 @@
*/ */
package org.elasticsearch.xpack.security; package org.elasticsearch.xpack.security;
import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.bootstrap.BootstrapContext; import org.elasticsearch.bootstrap.BootstrapContext;
import org.elasticsearch.common.settings.MockSecureSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings;
import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.core.ssl.SSLService;
import org.hamcrest.Matchers;
public class PkiRealmBootstrapCheckTests extends ESTestCase { public class PkiRealmBootstrapCheckTests extends ESTestCase {
public void testPkiRealmBootstrapDefault() throws Exception { public void testPkiRealmBootstrapDefault() throws Exception {
assertFalse(new PkiRealmBootstrapCheck(new SSLService(Settings.EMPTY, final Settings settings = Settings.EMPTY;
TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build()))).check( final Environment env = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build());
new BootstrapContext(Settings.EMPTY, null)).isFailure()); assertFalse(runCheck(settings, env).isFailure());
} }
public void testBootstrapCheckWithPkiRealm() throws Exception { public void testBootstrapCheckWithPkiRealm() throws Exception {
@ -27,48 +30,48 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase {
.put("path.home", createTempDir()) .put("path.home", createTempDir())
.build(); .build();
Environment env = TestEnvironment.newEnvironment(settings); 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 // enable transport tls
settings = Settings.builder().put(settings) settings = Settings.builder().put(settings)
.put("xpack.security.transport.ssl.enabled", true) .put("xpack.security.transport.ssl.enabled", true)
.build(); .build();
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure()); assertFalse(runCheck(settings, env).isFailure());
// disable client auth default // disable client auth default
settings = Settings.builder().put(settings) settings = Settings.builder().put(settings)
.put("xpack.ssl.client_authentication", "none") .put("xpack.ssl.client_authentication", "none")
.build(); .build();
env = TestEnvironment.newEnvironment(settings); 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 // enable ssl for http
settings = Settings.builder().put(settings) settings = Settings.builder().put(settings)
.put("xpack.security.http.ssl.enabled", true) .put("xpack.security.http.ssl.enabled", true)
.build(); .build();
env = TestEnvironment.newEnvironment(settings); 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 // enable client auth for http
settings = Settings.builder().put(settings) settings = Settings.builder().put(settings)
.put("xpack.security.http.ssl.client_authentication", randomFrom("required", "optional")) .put("xpack.security.http.ssl.client_authentication", randomFrom("required", "optional"))
.build(); .build();
env = TestEnvironment.newEnvironment(settings); 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 // disable http ssl
settings = Settings.builder().put(settings) settings = Settings.builder().put(settings)
.put("xpack.security.http.ssl.enabled", false) .put("xpack.security.http.ssl.enabled", false)
.build(); .build();
env = TestEnvironment.newEnvironment(settings); 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 // set transport client auth
settings = Settings.builder().put(settings) settings = Settings.builder().put(settings)
.put("xpack.security.transport.client_authentication", randomFrom("required", "optional")) .put("xpack.security.transport.client_authentication", randomFrom("required", "optional"))
.build(); .build();
env = TestEnvironment.newEnvironment(settings); 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 // test with transport profile
settings = Settings.builder().put(settings) 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")) .put("transport.profiles.foo.xpack.security.ssl.client_authentication", randomFrom("required", "optional"))
.build(); .build();
env = TestEnvironment.newEnvironment(settings); 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 { public void testBootstrapCheckWithDisabledRealm() throws Exception {
@ -87,6 +94,26 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase {
.put("path.home", createTempDir()) .put("path.home", createTempDir())
.build(); .build();
Environment env = TestEnvironment.newEnvironment(settings); 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));
} }
} }