From dea82a07a264e2b9a4577ecacc36ed1e6f0043b0 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Mon, 18 Sep 2017 14:21:19 +1000 Subject: [PATCH] Infer KeyStore type from pathname (elastic/x-pack-elasticsearch#2514) If the keystore type is not explicitly specified, infer it from the filename. Treats .p12, .pfx and .pkcs12 as being PKCS12, all others as jks. This will allow certgen to produce PKCSelastic/x-pack-elasticsearch#12 files by default and make it easy to use them as x-pack keystores Original commit: elastic/x-pack-elasticsearch@fc361f0d87b21824bfbd1d0a7148f5ae8bac0245 --- .../xpack/security/authc/pki/PkiRealm.java | 2 +- .../xpack/ssl/SSLConfiguration.java | 7 +- .../xpack/ssl/SSLConfigurationSettings.java | 36 ++++++--- .../ssl/SSLConfigurationSettingsTests.java | 7 +- .../xpack/ssl/SSLConfigurationTests.java | 75 +++++++++++++++++-- 5 files changed, 101 insertions(+), 26 deletions(-) diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java index c4c948ef168..733d5911c2b 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java @@ -180,7 +180,7 @@ public class PkiRealm extends Realm { } try (SecureString password = SSL_SETTINGS.truststorePassword.get(settings)) { String trustStoreAlgorithm = SSL_SETTINGS.truststoreAlgorithm.get(settings); - String trustStoreType = SSL_SETTINGS.truststoreType.get(settings); + String trustStoreType = SSLConfigurationSettings.getKeyStoreType(SSL_SETTINGS.truststoreType, settings, truststorePath); try { return CertUtils.trustManager(truststorePath, trustStoreType, password.getChars(), trustStoreAlgorithm, realmConfig.env()); } catch (Exception e) { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfiguration.java b/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfiguration.java index cdcae4e214b..1e0e85ea0e0 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfiguration.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfiguration.java @@ -15,7 +15,6 @@ import org.elasticsearch.xpack.XPackSettings; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; import java.nio.file.Path; -import java.security.KeyStore; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -205,8 +204,8 @@ public final class SSLConfiguration { } else { SecureString keyStorePassword = SETTINGS_PARSER.keystorePassword.get(settings); String keyStoreAlgorithm = SETTINGS_PARSER.keystoreAlgorithm.get(settings); - String keyStoreType = SETTINGS_PARSER.keystoreType.get(settings); - SecureString keyStoreKeyPassword = SETTINGS_PARSER.keystoreKeyPassword.get(settings);; + String keyStoreType = SSLConfigurationSettings.getKeyStoreType(SETTINGS_PARSER.keystoreType, settings, keyStorePath); + SecureString keyStoreKeyPassword = SETTINGS_PARSER.keystoreKeyPassword.get(settings); if (keyStoreKeyPassword.length() == 0) { keyStoreKeyPassword = keyStorePassword; } @@ -244,7 +243,7 @@ public final class SSLConfiguration { } else if (trustStorePath != null) { SecureString trustStorePassword = SETTINGS_PARSER.truststorePassword.get(settings); String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings); - String trustStoreType = SETTINGS_PARSER.truststoreType.get(settings); + String trustStoreType = SSLConfigurationSettings.getKeyStoreType(SETTINGS_PARSER.truststoreType, settings, trustStorePath); return new StoreTrustConfig(trustStorePath, trustStoreType, trustStorePassword, trustStoreAlgorithm); } else if (global == null && System.getProperty("javax.net.ssl.trustStore") != null) { try (SecureString truststorePassword = new SecureString(System.getProperty("javax.net.ssl.trustStorePassword", ""))) { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettings.java b/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettings.java index 03a9b244137..4bb2d530d47 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettings.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettings.java @@ -7,21 +7,20 @@ package org.elasticsearch.xpack.ssl; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; - import java.security.KeyStore; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.function.Function; import org.elasticsearch.common.settings.SecureSetting; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Setting; -import org.elasticsearch.common.settings.Settings; - import org.elasticsearch.common.settings.Setting.Property; +import org.elasticsearch.common.settings.Settings; /** * Bridges {@link SSLConfiguration} into the {@link Settings} framework, using {@link Setting} objects. @@ -33,12 +32,12 @@ public class SSLConfigurationSettings { public final Setting> keystorePath; public final Setting keystorePassword; public final Setting keystoreAlgorithm; - public final Setting keystoreType; + public final Setting> keystoreType; public final Setting keystoreKeyPassword; public final Setting> truststorePath; public final Setting truststorePassword; public final Setting truststoreAlgorithm; - public final Setting truststoreType; + public final Setting> truststoreType; public final Setting> trustRestrictionsPath; public final Setting> keyPath; public final Setting keyPassword; @@ -62,6 +61,7 @@ public class SSLConfigurationSettings { * Older versions of X-Pack only supported JKS and never looked at the JVM's configured default. */ private static final String DEFAULT_KEYSTORE_TYPE = "jks"; + private static final String PKCS12_KEYSTORE_TYPE = "PKCS12"; private static final Function>> CIPHERS_SETTING_TEMPLATE = key -> Setting.listSetting(key, Collections .emptyList(), Function.identity(), Property.NodeScope, Property.Filtered); @@ -132,14 +132,13 @@ public class SSLConfigurationSettings { public static final Setting TRUST_STORE_ALGORITHM_PROFILES = Setting.affixKeySetting("transport.profiles.", "xpack.security.ssl.truststore.algorithm", TRUST_STORE_ALGORITHM_TEMPLATE); - private static final Function> KEY_STORE_TYPE_TEMPLATE = key -> - new Setting<>(key, DEFAULT_KEYSTORE_TYPE, Function.identity(), Property.NodeScope, Property.Filtered); - public static final Setting KEY_STORE_TYPE_PROFILES = Setting.affixKeySetting("transport.profiles.", + private static final Function>> KEY_STORE_TYPE_TEMPLATE = key -> + new Setting<>(key, s -> null, Optional::ofNullable, Property.NodeScope, Property.Filtered); + public static final Setting> KEY_STORE_TYPE_PROFILES = Setting.affixKeySetting("transport.profiles.", "xpack.security.ssl.keystore.type", KEY_STORE_TYPE_TEMPLATE); - private static final Function> TRUST_STORE_TYPE_TEMPLATE = key -> - new Setting<>(key, DEFAULT_KEYSTORE_TYPE, Function.identity(), Property.NodeScope, Property.Filtered); - public static final Setting TRUST_STORE_TYPE_PROFILES = Setting.affixKeySetting("transport.profiles.", + private static final Function>> TRUST_STORE_TYPE_TEMPLATE = KEY_STORE_TYPE_TEMPLATE; + public static final Setting> TRUST_STORE_TYPE_PROFILES = Setting.affixKeySetting("transport.profiles.", "xpack.security.ssl.truststore.type", TRUST_STORE_TYPE_TEMPLATE); private static final Function>> TRUST_RESTRICTIONS_TEMPLATE = key -> new Setting<>(key, s -> null, @@ -201,7 +200,7 @@ public class SSLConfigurationSettings { keystoreAlgorithm = KEY_STORE_ALGORITHM_TEMPLATE.apply(prefix + "keystore.algorithm"); truststoreAlgorithm = TRUST_STORE_ALGORITHM_TEMPLATE.apply(prefix + "truststore.algorithm"); keystoreType = KEY_STORE_TYPE_TEMPLATE.apply(prefix + "keystore.type"); - truststoreType = KEY_STORE_TYPE_TEMPLATE.apply(prefix + "truststore.type"); + truststoreType = TRUST_STORE_TYPE_TEMPLATE.apply(prefix + "truststore.type"); trustRestrictionsPath = TRUST_RESTRICTIONS_TEMPLATE.apply(prefix + "trust_restrictions.path"); keyPath = KEY_PATH_TEMPLATE.apply(prefix + "key"); legacyKeyPassword = LEGACY_KEY_PASSWORD_TEMPLATE.apply(prefix + "key_passphrase"); @@ -218,6 +217,19 @@ public class SSLConfigurationSettings { legacyKeystorePassword, legacyKeystoreKeyPassword, legacyKeyPassword, legacyTruststorePassword); } + public static String getKeyStoreType(Setting> setting, Settings settings, String path) { + return setting.get(settings).orElseGet(() -> inferKeyStoreType(path)); + } + + private static String inferKeyStoreType(String path) { + String name = path == null ? "" : path.toLowerCase(Locale.ROOT); + if (name.endsWith(".p12") || name.endsWith(".pfx") || name.endsWith(".pkcs12")) { + return PKCS12_KEYSTORE_TYPE; + } else { + return DEFAULT_KEYSTORE_TYPE; + } + } + public List> getAllSettings() { return allSettings; } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettingsTests.java b/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettingsTests.java index 135f5e259d4..cccf9abfcd5 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettingsTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettingsTests.java @@ -75,17 +75,20 @@ public class SSLConfigurationSettingsTests extends ESTestCase { assertThat(ssl.keyPassword.exists(settings), is(false)); assertThat(ssl.keyPath.get(settings).isPresent(), is(false)); assertThat(ssl.keystoreAlgorithm.get(settings), is(KeyManagerFactory.getDefaultAlgorithm())); - assertThat(ssl.keystoreType.get(settings), is("jks")); + assertThat(ssl.keystoreType.get(settings).isPresent(), is(false)); assertThat(ssl.keystoreKeyPassword.exists(settings), is(false)); assertThat(ssl.keystorePassword.exists(settings), is(false)); assertThat(ssl.keystorePath.get(settings).isPresent(), is(false)); assertThat(ssl.supportedProtocols.get(settings).size(), is(0)); assertThat(ssl.truststoreAlgorithm.get(settings), is(TrustManagerFactory.getDefaultAlgorithm())); - assertThat(ssl.truststoreType.get(settings), is("jks")); + assertThat(ssl.truststoreType.get(settings).isPresent(), is(false)); assertThat(ssl.truststorePassword.exists(settings), is(false)); assertThat(ssl.truststorePath.get(settings).isPresent(), is(false)); assertThat(ssl.trustRestrictionsPath.get(settings).isPresent(), is(false)); assertThat(ssl.verificationMode.get(settings).isPresent(), is(false)); + + assertThat(SSLConfigurationSettings.getKeyStoreType(ssl.keystoreType, settings, null), is("jks")); + assertThat(SSLConfigurationSettings.getKeyStoreType(ssl.truststoreType, settings, null), is("jks")); } } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationTests.java b/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationTests.java index 7cef8b415cd..558a9b1544f 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationTests.java @@ -5,6 +5,12 @@ */ package org.elasticsearch.xpack.ssl; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManager; +import java.security.cert.X509Certificate; +import java.util.Arrays; + import org.elasticsearch.common.settings.MockSecureSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; @@ -12,17 +18,11 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.ssl.TrustConfig.CombiningTrustConfig; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.TrustManager; -import java.security.cert.X509Certificate; -import java.util.Arrays; - import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.isIn; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isIn; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.sameInstance; @@ -58,6 +58,7 @@ public class SSLConfigurationTests extends ESTestCase { assertThat(ksKeyInfo.keyStorePath, is(equalTo(path))); assertThat(ksKeyInfo.keyStorePassword, is(equalTo("testnode"))); + assertThat(ksKeyInfo.keyStoreType, is(equalTo("jks"))); assertThat(ksKeyInfo.keyPassword, is(equalTo(ksKeyInfo.keyStorePassword))); assertThat(ksKeyInfo.keyStoreAlgorithm, is(KeyManagerFactory.getDefaultAlgorithm())); assertThat(sslConfiguration.trustConfig(), is(instanceOf(CombiningTrustConfig.class))); @@ -123,6 +124,66 @@ public class SSLConfigurationTests extends ESTestCase { SSLConfiguration.SETTINGS_PARSER.legacyKeystorePassword, SSLConfiguration.SETTINGS_PARSER.legacyKeystoreKeyPassword}); } + public void testInferKeystoreTypeFromJksFile() { + MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("keystore.secure_password", "password"); + secureSettings.setString("keystore.secure_key_password", "keypass"); + Settings settings = Settings.builder() + .put("keystore.path", "xpack/tls/path.jks") + .setSecureSettings(secureSettings) + .build(); + SSLConfiguration sslConfiguration = new SSLConfiguration(settings); + assertThat(sslConfiguration.keyConfig(), instanceOf(StoreKeyConfig.class)); + StoreKeyConfig ksKeyInfo = (StoreKeyConfig) sslConfiguration.keyConfig(); + assertThat(ksKeyInfo.keyStoreType, is(equalTo("jks"))); + } + + public void testInferKeystoreTypeFromPkcs12File() { + final String ext = randomFrom("p12", "pfx", "pkcs12"); + MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("keystore.secure_password", "password"); + secureSettings.setString("keystore.secure_key_password", "keypass"); + Settings settings = Settings.builder() + .put("keystore.path", "xpack/tls/path." + ext) + .setSecureSettings(secureSettings) + .build(); + SSLConfiguration sslConfiguration = new SSLConfiguration(settings); + assertThat(sslConfiguration.keyConfig(), instanceOf(StoreKeyConfig.class)); + StoreKeyConfig ksKeyInfo = (StoreKeyConfig) sslConfiguration.keyConfig(); + assertThat(ksKeyInfo.keyStoreType, is(equalTo("PKCS12"))); + } + + public void testInferKeystoreTypeFromUnrecognised() { + MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("keystore.secure_password", "password"); + secureSettings.setString("keystore.secure_key_password", "keypass"); + Settings settings = Settings.builder() + .put("keystore.path", "xpack/tls/path.foo") + .setSecureSettings(secureSettings) + .build(); + SSLConfiguration sslConfiguration = new SSLConfiguration(settings); + assertThat(sslConfiguration.keyConfig(), instanceOf(StoreKeyConfig.class)); + StoreKeyConfig ksKeyInfo = (StoreKeyConfig) sslConfiguration.keyConfig(); + assertThat(ksKeyInfo.keyStoreType, is(equalTo("jks"))); + } + + public void testExplicitKeystoreType() { + final String ext = randomFrom("p12", "jks"); + final String type = randomAlphaOfLengthBetween(2, 8); + MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("keystore.secure_password", "password"); + secureSettings.setString("keystore.secure_key_password", "keypass"); + Settings settings = Settings.builder() + .put("keystore.path", "xpack/tls/path." + ext) + .put("keystore.type", type) + .setSecureSettings(secureSettings) + .build(); + SSLConfiguration sslConfiguration = new SSLConfiguration(settings); + assertThat(sslConfiguration.keyConfig(), instanceOf(StoreKeyConfig.class)); + StoreKeyConfig ksKeyInfo = (StoreKeyConfig) sslConfiguration.keyConfig(); + assertThat(ksKeyInfo.keyStoreType, is(equalTo(type))); + } + public void testThatProfileSettingsOverrideServiceSettings() { MockSecureSettings profileSecureSettings = new MockSecureSettings(); profileSecureSettings.setString("keystore.secure_password", "password");