Adds support for reading PKCSelastic/x-pack-elasticsearch#12 files as SSL keystores/truststores.

Original commit: elastic/x-pack-elasticsearch@1855ad6173
This commit is contained in:
Tim Vernum 2017-07-25 17:31:37 +10:00 committed by GitHub
parent 15f5c5a632
commit 9ab6d3cbc3
11 changed files with 219 additions and 44 deletions

View File

@ -298,6 +298,10 @@ List of paths to PEM encoded certificate files that should be trusted.
The path to the Java Keystore file that contains a private key and certificate. The path to the Java Keystore file that contains a private key and certificate.
`ssl.key` and `ssl.keystore.path` may not be used at the same time. `ssl.key` and `ssl.keystore.path` may not be used at the same time.
`ssl.keystore.type`::
The format of the keystore file. Should be either `jks` to use the Java
Keystore format, or `PKCS12` to use PKCS#12 files. The default is `jks`.
`ssl.keystore.password`:: `ssl.keystore.password`::
The password to the keystore. The password to the keystore.
@ -308,6 +312,10 @@ The password for the key in the keystore. Defaults to the keystore password.
The path to the Java Keystore file that contains the certificates to trust. The path to the Java Keystore file that contains the certificates to trust.
`ssl.certificate_authorities` and `ssl.truststore.path` may not be used at the same time. `ssl.certificate_authorities` and `ssl.truststore.path` may not be used at the same time.
`ssl.truststore.type`::
The format of the keystore file. Should be either `jks` to use the Java
Keystore format, or `PKCS12` to use PKCS#12 files. The default is `jks`.
`ssl.truststore.password`:: `ssl.truststore.password`::
The password to the truststore. The password to the truststore.
@ -482,6 +490,10 @@ List of paths to PEM encoded certificate files that should be trusted.
`ssl.keystore.path`:: `ssl.keystore.path`::
The path to the Java Keystore file that contains a private key and certificate. The path to the Java Keystore file that contains a private key and certificate.
`ssl.keystore.type`::
The format of the keystore file. Should be either `jks` to use the Java
Keystore format, or `PKCS12` to use PKCS#12 files. The default is `jks`.
`ssl.keystore.password`:: `ssl.keystore.password`::
The password to the keystore. The password to the keystore.
@ -491,6 +503,10 @@ The password for the key in the keystore. Defaults to the keystore password.
`ssl.truststore.path`:: `ssl.truststore.path`::
The path to the Java Keystore file that contains the certificates to trust. The path to the Java Keystore file that contains the certificates to trust.
`ssl.truststore.type`::
The format of the truststore file. Should be either `jks` to use the Java
Keystore format, or `PKCS12` to use PKCS#12 files. The default is `jks`.
`ssl.truststore.password`:: `ssl.truststore.password`::
The password to the truststore. The password to the truststore.
@ -636,6 +652,35 @@ Path to the truststore file.
`xpack.ssl.truststore.password`:: `xpack.ssl.truststore.password`::
Password to the truststore. Password to the truststore.
[float]
===== PKCS#12 Files
When using PKCS#12 container files (`.p12` or `.pfx`), which contain the
private key, certificate, and certificates that should be trusted, use
the following settings:
`xpack.ssl.keystore.path`::
Path to the PKCS#12 file that holds the private key and certificate.
`xpack.ssl.keystore.type`::
Set this to `PKCS12`.
`xpack.ssl.keystore.password`::
Password to the PKCS#12 file.
`xpack.ssl.keystore.key_password`::
Password for the private key in the PKCS12 file.
Defaults to the same value as `xpack.ssl.keystore.password`.
`xpack.ssl.truststore.path`::
Path to the truststore file.
`xpack.ssl.truststore.type`::
Set this to `PKCS12`.
`xpack.ssl.truststore.password`::
Password to the truststore.
[[http-tls-ssl-settings]] [[http-tls-ssl-settings]]
:ssl-prefix: xpack.security.http :ssl-prefix: xpack.security.http
:component: HTTP :component: HTTP

View File

@ -93,3 +93,32 @@ Path to the truststore file.
+{ssl-prefix}.ssl.truststore.password+:: +{ssl-prefix}.ssl.truststore.password+::
Password to the truststore. Password to the truststore.
===== PKCS#12 Files
{security} can be configured to use PKCS#12 container files (`.p12` or `.pfx` files)
that contain the private key, certificate and certificates that should be trusted.
PKCS#12 files are configured in the same way as Java Keystore Files:
+{ssl-prefix}.ssl.keystore.path+::
Path to the PKCS#12 file that holds the private key and certificate.
+{ssl-prefix}.ssl.keystore.type+::
Set this to `PKCS12` to indicate that the keystore is a PKCS#12 file.
+{ssl-prefix}.ssl.keystore.password+::
Password to the PKCS#12 file.
+{ssl-prefix}.ssl.keystore.key_password+::
Password for the private key stored in the PKCS#12 file.
Defaults to the same value as +{ssl-prefix}.ssl.keystore.password+.
+{ssl-prefix}.ssl.truststore.path+::
Path to the PKCS#12 file that holds the certificates to be trusted.
+{ssl-prefix}.ssl.truststore.type+::
Set this to `PKCS12` to indicate that the truststore is a PKCS#12 file.
+{ssl-prefix}.ssl.truststore.password+::
Password to the PKCS#12 file.

View File

@ -180,8 +180,9 @@ public class PkiRealm extends Realm {
} }
try (SecureString password = SSL_SETTINGS.truststorePassword.get(settings)) { try (SecureString password = SSL_SETTINGS.truststorePassword.get(settings)) {
String trustStoreAlgorithm = SSL_SETTINGS.truststoreAlgorithm.get(settings); String trustStoreAlgorithm = SSL_SETTINGS.truststoreAlgorithm.get(settings);
String trustStoreType = SSL_SETTINGS.truststoreType.get(settings);
try { try {
return CertUtils.trustManager(truststorePath, password.getChars(), trustStoreAlgorithm, realmConfig.env()); return CertUtils.trustManager(truststorePath, trustStoreType, password.getChars(), trustStoreAlgorithm, realmConfig.env());
} catch (Exception e) { } catch (Exception e) {
throw new IllegalArgumentException("failed to load specified truststore", e); throw new IllegalArgumentException("failed to load specified truststore", e);
} }

View File

@ -160,12 +160,11 @@ public class CertUtils {
* @param env the environment to use for file resolution. May be {@code null} * @param env the environment to use for file resolution. May be {@code null}
* @return a trust manager with the trust material from the store * @return a trust manager with the trust material from the store
*/ */
public static X509ExtendedTrustManager trustManager(String trustStorePath, char[] trustStorePassword, String trustStoreAlgorithm, public static X509ExtendedTrustManager trustManager(String trustStorePath, String trustStoreType, char[] trustStorePassword,
@Nullable Environment env) String trustStoreAlgorithm, @Nullable Environment env)
throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException { throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException {
try (InputStream in = Files.newInputStream(resolvePath(trustStorePath, env))) { try (InputStream in = Files.newInputStream(resolvePath(trustStorePath, env))) {
// TODO remove reliance on JKS since we can PKCS12 stores... KeyStore trustStore = KeyStore.getInstance(trustStoreType);
KeyStore trustStore = KeyStore.getInstance("jks");
assert trustStorePassword != null; assert trustStorePassword != null;
trustStore.load(in, trustStorePassword); trustStore.load(in, trustStorePassword);
return trustManager(trustStore, trustStoreAlgorithm); return trustManager(trustStore, trustStoreAlgorithm);

View File

@ -15,6 +15,7 @@ import org.elasticsearch.xpack.XPackSettings;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.KeyStore;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -186,8 +187,7 @@ public final class SSLConfiguration {
} else if (System.getProperty("javax.net.ssl.keyStore") != null) { } else if (System.getProperty("javax.net.ssl.keyStore") != null) {
// TODO: we should not support loading a keystore from sysprops... // TODO: we should not support loading a keystore from sysprops...
try (SecureString keystorePassword = new SecureString(System.getProperty("javax.net.ssl.keyStorePassword", ""))) { try (SecureString keystorePassword = new SecureString(System.getProperty("javax.net.ssl.keyStorePassword", ""))) {
return new StoreKeyConfig(System.getProperty("javax.net.ssl.keyStore"), return new StoreKeyConfig(System.getProperty("javax.net.ssl.keyStore"), "jks", keystorePassword, keystorePassword,
keystorePassword, keystorePassword,
System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()), System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()),
System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm())); System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm()));
} }
@ -205,12 +205,14 @@ public final class SSLConfiguration {
} else { } else {
SecureString keyStorePassword = SETTINGS_PARSER.keystorePassword.get(settings); SecureString keyStorePassword = SETTINGS_PARSER.keystorePassword.get(settings);
String keyStoreAlgorithm = SETTINGS_PARSER.keystoreAlgorithm.get(settings); String keyStoreAlgorithm = SETTINGS_PARSER.keystoreAlgorithm.get(settings);
String keyStoreType = SETTINGS_PARSER.keystoreType.get(settings);
SecureString keyStoreKeyPassword = SETTINGS_PARSER.keystoreKeyPassword.get(settings);; SecureString keyStoreKeyPassword = SETTINGS_PARSER.keystoreKeyPassword.get(settings);;
if (keyStoreKeyPassword.length() == 0) { if (keyStoreKeyPassword.length() == 0) {
keyStoreKeyPassword = keyStorePassword; keyStoreKeyPassword = keyStorePassword;
} }
String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings); String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings);
return new StoreKeyConfig(keyStorePath, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm, trustStoreAlgorithm); return new StoreKeyConfig(keyStorePath, keyStoreType, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm,
trustStoreAlgorithm);
} }
} }
@ -242,10 +244,11 @@ public final class SSLConfiguration {
} else if (trustStorePath != null) { } else if (trustStorePath != null) {
SecureString trustStorePassword = SETTINGS_PARSER.truststorePassword.get(settings); SecureString trustStorePassword = SETTINGS_PARSER.truststorePassword.get(settings);
String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings); String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings);
return new StoreTrustConfig(trustStorePath, trustStorePassword, trustStoreAlgorithm); String trustStoreType = SETTINGS_PARSER.truststoreType.get(settings);
return new StoreTrustConfig(trustStorePath, trustStoreType, trustStorePassword, trustStoreAlgorithm);
} else if (global == null && System.getProperty("javax.net.ssl.trustStore") != null) { } else if (global == null && System.getProperty("javax.net.ssl.trustStore") != null) {
try (SecureString truststorePassword = new SecureString(System.getProperty("javax.net.ssl.trustStorePassword", ""))) { try (SecureString truststorePassword = new SecureString(System.getProperty("javax.net.ssl.trustStorePassword", ""))) {
return new StoreTrustConfig(System.getProperty("javax.net.ssl.trustStore"), truststorePassword, return new StoreTrustConfig(System.getProperty("javax.net.ssl.trustStore"), "jks", truststorePassword,
System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm())); System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm()));
} }
} else if (global != null && keyConfig == global.keyConfig()) { } else if (global != null && keyConfig == global.keyConfig()) {

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.ssl;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import java.security.KeyStore;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -32,10 +33,12 @@ public class SSLConfigurationSettings {
public final Setting<Optional<String>> keystorePath; public final Setting<Optional<String>> keystorePath;
public final Setting<SecureString> keystorePassword; public final Setting<SecureString> keystorePassword;
public final Setting<String> keystoreAlgorithm; public final Setting<String> keystoreAlgorithm;
public final Setting<String> keystoreType;
public final Setting<SecureString> keystoreKeyPassword; public final Setting<SecureString> keystoreKeyPassword;
public final Setting<Optional<String>> truststorePath; public final Setting<Optional<String>> truststorePath;
public final Setting<SecureString> truststorePassword; public final Setting<SecureString> truststorePassword;
public final Setting<String> truststoreAlgorithm; public final Setting<String> truststoreAlgorithm;
public final Setting<String> truststoreType;
public final Setting<Optional<String>> trustRestrictionsPath; public final Setting<Optional<String>> trustRestrictionsPath;
public final Setting<Optional<String>> keyPath; public final Setting<Optional<String>> keyPath;
public final Setting<SecureString> keyPassword; public final Setting<SecureString> keyPassword;
@ -52,6 +55,12 @@ public class SSLConfigurationSettings {
private final List<Setting<?>> allSettings; private final List<Setting<?>> allSettings;
/**
* We explicitly default to "jks" here (rather than {@link KeyStore#getDefaultType()}) for backwards compatibility.
* 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 Function<String, Setting<List<String>>> CIPHERS_SETTING_TEMPLATE = key -> Setting.listSetting(key, Collections private static final Function<String, Setting<List<String>>> CIPHERS_SETTING_TEMPLATE = key -> Setting.listSetting(key, Collections
.emptyList(), Function.identity(), Property.NodeScope, Property.Filtered); .emptyList(), Function.identity(), Property.NodeScope, Property.Filtered);
public static final Setting<List<String>> CIPHERS_SETTING_PROFILES = Setting.affixKeySetting("transport.profiles.", public static final Setting<List<String>> CIPHERS_SETTING_PROFILES = Setting.affixKeySetting("transport.profiles.",
@ -121,6 +130,16 @@ public class SSLConfigurationSettings {
public static final Setting<String> TRUST_STORE_ALGORITHM_PROFILES = Setting.affixKeySetting("transport.profiles.", public static final Setting<String> TRUST_STORE_ALGORITHM_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.truststore.algorithm", TRUST_STORE_ALGORITHM_TEMPLATE); "xpack.security.ssl.truststore.algorithm", TRUST_STORE_ALGORITHM_TEMPLATE);
private static final Function<String, Setting<String>> KEY_STORE_TYPE_TEMPLATE = key ->
new Setting<>(key, DEFAULT_KEYSTORE_TYPE, Function.identity(), Property.NodeScope, Property.Filtered);
public static final Setting<String> KEY_STORE_TYPE_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.keystore.type", KEY_STORE_TYPE_TEMPLATE);
private static final Function<String, Setting<String>> TRUST_STORE_TYPE_TEMPLATE = key ->
new Setting<>(key, DEFAULT_KEYSTORE_TYPE, Function.identity(), Property.NodeScope, Property.Filtered);
public static final Setting<String> TRUST_STORE_TYPE_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.truststore.type", TRUST_STORE_TYPE_TEMPLATE);
private static final Function<String, Setting<Optional<String>>> TRUST_RESTRICTIONS_TEMPLATE = key -> new Setting<>(key, s -> null, private static final Function<String, Setting<Optional<String>>> TRUST_RESTRICTIONS_TEMPLATE = key -> new Setting<>(key, s -> null,
Optional::ofNullable, Property.NodeScope, Property.Filtered); Optional::ofNullable, Property.NodeScope, Property.Filtered);
public static final Setting<Optional<String>> TRUST_RESTRICTIONS_PROFILES = Setting.affixKeySetting("transport.profiles.", public static final Setting<Optional<String>> TRUST_RESTRICTIONS_PROFILES = Setting.affixKeySetting("transport.profiles.",
@ -179,6 +198,8 @@ public class SSLConfigurationSettings {
truststorePassword = TRUSTSTORE_PASSWORD_TEMPLATE.apply(prefix + "truststore.secure_password"); truststorePassword = TRUSTSTORE_PASSWORD_TEMPLATE.apply(prefix + "truststore.secure_password");
keystoreAlgorithm = KEY_STORE_ALGORITHM_TEMPLATE.apply(prefix + "keystore.algorithm"); keystoreAlgorithm = KEY_STORE_ALGORITHM_TEMPLATE.apply(prefix + "keystore.algorithm");
truststoreAlgorithm = TRUST_STORE_ALGORITHM_TEMPLATE.apply(prefix + "truststore.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");
trustRestrictionsPath = TRUST_RESTRICTIONS_TEMPLATE.apply(prefix + "trust_restrictions.path"); trustRestrictionsPath = TRUST_RESTRICTIONS_TEMPLATE.apply(prefix + "trust_restrictions.path");
keyPath = KEY_PATH_TEMPLATE.apply(prefix + "key"); keyPath = KEY_PATH_TEMPLATE.apply(prefix + "key");
legacyKeyPassword = LEGACY_KEY_PASSWORD_TEMPLATE.apply(prefix + "key_passphrase"); legacyKeyPassword = LEGACY_KEY_PASSWORD_TEMPLATE.apply(prefix + "key_passphrase");
@ -189,8 +210,8 @@ public class SSLConfigurationSettings {
verificationMode = VERIFICATION_MODE_SETTING_TEMPLATE.apply(prefix + "verification_mode"); verificationMode = VERIFICATION_MODE_SETTING_TEMPLATE.apply(prefix + "verification_mode");
this.allSettings = Arrays.asList(ciphers, supportedProtocols, this.allSettings = Arrays.asList(ciphers, supportedProtocols,
keystorePath, keystorePassword, keystoreAlgorithm, keystoreKeyPassword, keystorePath, keystorePassword, keystoreAlgorithm, keystoreType, keystoreKeyPassword,
truststorePath, truststorePassword, truststoreAlgorithm, trustRestrictionsPath, truststorePath, truststorePassword, truststoreAlgorithm, truststoreType, trustRestrictionsPath,
keyPath, keyPassword, cert, caPaths, clientAuth, verificationMode, keyPath, keyPassword, cert, caPaths, clientAuth, verificationMode,
legacyKeystorePassword, legacyKeystoreKeyPassword, legacyKeyPassword, legacyTruststorePassword); legacyKeystorePassword, legacyKeystoreKeyPassword, legacyKeyPassword, legacyTruststorePassword);
} }
@ -222,7 +243,8 @@ public class SSLConfigurationSettings {
return Arrays.asList(CIPHERS_SETTING_PROFILES, SUPPORTED_PROTOCOLS_PROFILES, KEYSTORE_PATH_PROFILES, return Arrays.asList(CIPHERS_SETTING_PROFILES, SUPPORTED_PROTOCOLS_PROFILES, KEYSTORE_PATH_PROFILES,
LEGACY_KEYSTORE_PASSWORD_PROFILES, KEYSTORE_PASSWORD_PROFILES, LEGACY_KEYSTORE_KEY_PASSWORD_PROFILES, LEGACY_KEYSTORE_PASSWORD_PROFILES, KEYSTORE_PASSWORD_PROFILES, LEGACY_KEYSTORE_KEY_PASSWORD_PROFILES,
KEYSTORE_KEY_PASSWORD_PROFILES, TRUST_STORE_PATH_PROFILES, LEGACY_TRUSTSTORE_PASSWORD_PROFILES, KEYSTORE_KEY_PASSWORD_PROFILES, TRUST_STORE_PATH_PROFILES, LEGACY_TRUSTSTORE_PASSWORD_PROFILES,
TRUSTSTORE_PASSWORD_PROFILES, KEY_STORE_ALGORITHM_PROFILES, TRUST_STORE_ALGORITHM_PROFILES, TRUST_RESTRICTIONS_PROFILES, TRUSTSTORE_PASSWORD_PROFILES, KEY_STORE_ALGORITHM_PROFILES, TRUST_STORE_ALGORITHM_PROFILES,
KEY_STORE_TYPE_PROFILES, TRUST_STORE_TYPE_PROFILES, TRUST_RESTRICTIONS_PROFILES,
KEY_PATH_PROFILES, LEGACY_KEY_PASSWORD_PROFILES, KEY_PASSWORD_PROFILES,CERT_PROFILES,CAPATH_SETTING_PROFILES, KEY_PATH_PROFILES, LEGACY_KEY_PASSWORD_PROFILES, KEY_PASSWORD_PROFILES,CERT_PROFILES,CAPATH_SETTING_PROFILES,
CLIENT_AUTH_SETTING_PROFILES, VERIFICATION_MODE_SETTING_PROFILES); CLIENT_AUTH_SETTING_PROFILES, VERIFICATION_MODE_SETTING_PROFILES);
} }

View File

@ -35,6 +35,7 @@ import java.util.Objects;
class StoreKeyConfig extends KeyConfig { class StoreKeyConfig extends KeyConfig {
final String keyStorePath; final String keyStorePath;
final String keyStoreType;
final SecureString keyStorePassword; final SecureString keyStorePassword;
final String keyStoreAlgorithm; final String keyStoreAlgorithm;
final SecureString keyPassword; final SecureString keyPassword;
@ -43,14 +44,16 @@ class StoreKeyConfig extends KeyConfig {
/** /**
* Creates a new configuration that can be used to load key and trust material from a {@link KeyStore} * Creates a new configuration that can be used to load key and trust material from a {@link KeyStore}
* @param keyStorePath the path to the keystore file * @param keyStorePath the path to the keystore file
* @param keyStoreType the type of the keystore file
* @param keyStorePassword the password for the keystore * @param keyStorePassword the password for the keystore
* @param keyPassword the password for the private key in the keystore * @param keyPassword the password for the private key in the keystore
* @param keyStoreAlgorithm the algorithm for the keystore * @param keyStoreAlgorithm the algorithm for the keystore
* @param trustStoreAlgorithm the algorithm to use when loading as a truststore * @param trustStoreAlgorithm the algorithm to use when loading as a truststore
*/ */
StoreKeyConfig(String keyStorePath, SecureString keyStorePassword, SecureString keyPassword, String keyStoreAlgorithm, StoreKeyConfig(String keyStorePath, String keyStoreType, SecureString keyStorePassword, SecureString keyPassword,
String trustStoreAlgorithm) { String keyStoreAlgorithm, String trustStoreAlgorithm) {
this.keyStorePath = Objects.requireNonNull(keyStorePath, "keystore path must be specified"); this.keyStorePath = Objects.requireNonNull(keyStorePath, "keystore path must be specified");
this.keyStoreType = Objects.requireNonNull(keyStoreType, "keystore type must be specified");
// since we support reloading the keystore, we must store the passphrase in memory for the life of the node, so we // since we support reloading the keystore, we must store the passphrase in memory for the life of the node, so we
// clone the password and never close it during our uses below // clone the password and never close it during our uses below
this.keyStorePassword = Objects.requireNonNull(keyStorePassword, "keystore password must be specified").clone(); this.keyStorePassword = Objects.requireNonNull(keyStorePassword, "keystore password must be specified").clone();
@ -73,7 +76,7 @@ class StoreKeyConfig extends KeyConfig {
@Override @Override
X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) { X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
try { try {
return CertUtils.trustManager(keyStorePath, keyStorePassword.getChars(), trustStoreAlgorithm, environment); return CertUtils.trustManager(keyStorePath, keyStoreType, keyStorePassword.getChars(), trustStoreAlgorithm, environment);
} catch (Exception e) { } catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e); throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
} }
@ -107,8 +110,7 @@ class StoreKeyConfig extends KeyConfig {
private KeyStore getKeyStore(@Nullable Environment environment) private KeyStore getKeyStore(@Nullable Environment environment)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
try (InputStream in = Files.newInputStream(CertUtils.resolvePath(keyStorePath, environment))) { try (InputStream in = Files.newInputStream(CertUtils.resolvePath(keyStorePath, environment))) {
// TODO remove reliance on JKS since we can use PKCS12 stores in JDK8+... KeyStore ks = KeyStore.getInstance(keyStoreType);
KeyStore ks = KeyStore.getInstance("jks");
ks.load(in, keyStorePassword.getChars()); ks.load(in, keyStorePassword.getChars());
return ks; return ks;
} }
@ -154,6 +156,7 @@ class StoreKeyConfig extends KeyConfig {
@Override @Override
public String toString() { public String toString() {
return "keyStorePath=[" + keyStorePath + return "keyStorePath=[" + keyStorePath +
"], keyStoreType=[" + keyStoreType +
"], keyStoreAlgorithm=[" + keyStoreAlgorithm + "], keyStoreAlgorithm=[" + keyStoreAlgorithm +
"], trustStoreAlgorithm=[" + trustStoreAlgorithm + "], trustStoreAlgorithm=[" + trustStoreAlgorithm +
"]"; "]";

View File

@ -22,6 +22,7 @@ import java.util.Objects;
class StoreTrustConfig extends TrustConfig { class StoreTrustConfig extends TrustConfig {
final String trustStorePath; final String trustStorePath;
final String trustStoreType;
final SecureString trustStorePassword; final SecureString trustStorePassword;
final String trustStoreAlgorithm; final String trustStoreAlgorithm;
@ -31,8 +32,9 @@ class StoreTrustConfig extends TrustConfig {
* @param trustStorePassword the password for the truststore * @param trustStorePassword the password for the truststore
* @param trustStoreAlgorithm the algorithm to use for reading the truststore * @param trustStoreAlgorithm the algorithm to use for reading the truststore
*/ */
StoreTrustConfig(String trustStorePath, SecureString trustStorePassword, String trustStoreAlgorithm) { StoreTrustConfig(String trustStorePath, String trustStoreType, SecureString trustStorePassword, String trustStoreAlgorithm) {
this.trustStorePath = trustStorePath; this.trustStorePath = trustStorePath;
this.trustStoreType = trustStoreType;
// since we support reloading the truststore, we must store the passphrase in memory for the life of the node, so we // since we support reloading the truststore, we must store the passphrase in memory for the life of the node, so we
// clone the password and never close it during our uses below // clone the password and never close it during our uses below
this.trustStorePassword = Objects.requireNonNull(trustStorePassword, "truststore password must be specified").clone(); this.trustStorePassword = Objects.requireNonNull(trustStorePassword, "truststore password must be specified").clone();
@ -42,7 +44,7 @@ class StoreTrustConfig extends TrustConfig {
@Override @Override
X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) { X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
try { try {
return CertUtils.trustManager(trustStorePath, trustStorePassword.getChars(), trustStoreAlgorithm, environment); return CertUtils.trustManager(trustStorePath, trustStoreType, trustStorePassword.getChars(), trustStoreAlgorithm, environment);
} catch (Exception e) { } catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e); throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
} }

View File

@ -75,13 +75,16 @@ public class SSLConfigurationSettingsTests extends ESTestCase {
assertThat(ssl.keyPassword.exists(settings), is(false)); assertThat(ssl.keyPassword.exists(settings), is(false));
assertThat(ssl.keyPath.get(settings).isPresent(), is(false)); assertThat(ssl.keyPath.get(settings).isPresent(), is(false));
assertThat(ssl.keystoreAlgorithm.get(settings), is(KeyManagerFactory.getDefaultAlgorithm())); assertThat(ssl.keystoreAlgorithm.get(settings), is(KeyManagerFactory.getDefaultAlgorithm()));
assertThat(ssl.keystoreType.get(settings), is("jks"));
assertThat(ssl.keystoreKeyPassword.exists(settings), is(false)); assertThat(ssl.keystoreKeyPassword.exists(settings), is(false));
assertThat(ssl.keystorePassword.exists(settings), is(false)); assertThat(ssl.keystorePassword.exists(settings), is(false));
assertThat(ssl.keystorePath.get(settings).isPresent(), is(false)); assertThat(ssl.keystorePath.get(settings).isPresent(), is(false));
assertThat(ssl.supportedProtocols.get(settings).size(), is(0)); assertThat(ssl.supportedProtocols.get(settings).size(), is(0));
assertThat(ssl.truststoreAlgorithm.get(settings), is(TrustManagerFactory.getDefaultAlgorithm())); assertThat(ssl.truststoreAlgorithm.get(settings), is(TrustManagerFactory.getDefaultAlgorithm()));
assertThat(ssl.truststoreType.get(settings), is("jks"));
assertThat(ssl.truststorePassword.exists(settings), is(false)); assertThat(ssl.truststorePassword.exists(settings), is(false));
assertThat(ssl.truststorePath.get(settings).isPresent(), 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(ssl.verificationMode.get(settings).isPresent(), is(false));
} }

View File

@ -5,29 +5,6 @@
*/ */
package org.elasticsearch.xpack.ssl; package org.elasticsearch.xpack.ssl;
import org.elasticsearch.client.http.HttpHost;
import org.elasticsearch.client.http.HttpResponse;
import org.elasticsearch.client.http.client.methods.HttpGet;
import org.elasticsearch.client.http.concurrent.FutureCallback;
import org.elasticsearch.client.http.conn.ssl.DefaultHostnameVerifier;
import org.elasticsearch.client.http.conn.ssl.NoopHostnameVerifier;
import org.elasticsearch.client.http.impl.client.CloseableHttpClient;
import org.elasticsearch.client.http.impl.client.HttpClients;
import org.elasticsearch.client.http.impl.nio.client.CloseableHttpAsyncClient;
import org.elasticsearch.client.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.CheckedRunnable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.MockSecureSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.XPackSettings;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
@ -45,6 +22,29 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.client.http.HttpHost;
import org.elasticsearch.client.http.HttpResponse;
import org.elasticsearch.client.http.client.methods.HttpGet;
import org.elasticsearch.client.http.concurrent.FutureCallback;
import org.elasticsearch.client.http.conn.ssl.DefaultHostnameVerifier;
import org.elasticsearch.client.http.conn.ssl.NoopHostnameVerifier;
import org.elasticsearch.client.http.impl.client.CloseableHttpClient;
import org.elasticsearch.client.http.impl.client.HttpClients;
import org.elasticsearch.client.http.impl.nio.client.CloseableHttpAsyncClient;
import org.elasticsearch.client.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.elasticsearch.common.CheckedRunnable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.MockSecureSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.XPackSettings;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
@ -64,12 +64,22 @@ import static org.mockito.Mockito.when;
public class SSLServiceTests extends ESTestCase { public class SSLServiceTests extends ESTestCase {
private Path testnodeStore; private Path testnodeStore;
private String testnodeStoreType;
private Path testclientStore; private Path testclientStore;
private Environment env; private Environment env;
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
// Randomise the keystore type (jks/PKCS#12)
if (randomBoolean()) {
testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"); testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks");
// The default is to use JKS. Randomly test with explicit and with the default value.
testnodeStoreType = randomBoolean() ? "jks" : null;
} else {
testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.p12");
testnodeStoreType = "PKCS12";
}
logger.info("Using [{}] key/truststore [{}]", testnodeStoreType, testnodeStore);
testclientStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks"); testclientStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks");
env = new Environment(Settings.builder().put("path.home", createTempDir()).build()); env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
} }
@ -81,6 +91,7 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("transport.profiles.foo.xpack.security.ssl.truststore.secure_password", "testclient"); secureSettings.setString("transport.profiles.foo.xpack.security.ssl.truststore.secure_password", "testclient");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.truststore.path", testnodeStore) .put("xpack.ssl.truststore.path", testnodeStore)
.put("xpack.ssl.truststore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.put("transport.profiles.foo.xpack.security.ssl.truststore.path", testClientStore) .put("transport.profiles.foo.xpack.security.ssl.truststore.path", testClientStore)
.build(); .build();
@ -105,6 +116,7 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.build(); .build();
SSLService sslService = new SSLService(settings, env); SSLService sslService = new SSLService(settings, env);
@ -150,6 +162,7 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.build(); .build();
SSLService sslService = new SSLService(settings, env); SSLService sslService = new SSLService(settings, env);
@ -181,6 +194,7 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.build(); .build();
SSLService sslService = new SSLService(settings, env); SSLService sslService = new SSLService(settings, env);
@ -193,6 +207,7 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.ssl.truststore.secure_password", "testnode"); secureSettings.setString("xpack.ssl.truststore.secure_password", "testnode");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.truststore.path", testnodeStore) .put("xpack.ssl.truststore.path", testnodeStore)
.put("xpack.ssl.truststore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.build(); .build();
SSLService sslService = new SSLService(settings, env); SSLService sslService = new SSLService(settings, env);
@ -201,8 +216,10 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.security.transport.ssl.keystore.secure_password", "testnode"); secureSettings.setString("xpack.security.transport.ssl.keystore.secure_password", "testnode");
settings = Settings.builder() settings = Settings.builder()
.put("xpack.ssl.truststore.path", testnodeStore) .put("xpack.ssl.truststore.path", testnodeStore)
.put("xpack.ssl.truststore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.put("xpack.security.transport.ssl.keystore.path", testnodeStore) .put("xpack.security.transport.ssl.keystore.path", testnodeStore)
.put("xpack.security.transport.ssl.keystore.type", testnodeStoreType)
.build(); .build();
sslService = new SSLService(settings, env); sslService = new SSLService(settings, env);
assertFalse(sslService.isConfigurationValidForServerUsage(sslService.sslConfiguration(Settings.EMPTY))); assertFalse(sslService.isConfigurationValidForServerUsage(sslService.sslConfiguration(Settings.EMPTY)));
@ -268,8 +285,10 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.put("xpack.ssl.truststore.path", testnodeStore) .put("xpack.ssl.truststore.path", testnodeStore)
.put("xpack.ssl.truststore.type", testnodeStoreType)
.build(); .build();
ElasticsearchException e = ElasticsearchException e =
expectThrows(ElasticsearchException.class, () -> new SSLService(settings, env)); expectThrows(ElasticsearchException.class, () -> new SSLService(settings, env));
@ -279,6 +298,7 @@ public class SSLServiceTests extends ESTestCase {
public void testThatKeystorePasswordIsRequired() throws Exception { public void testThatKeystorePasswordIsRequired() throws Exception {
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.build(); .build();
ElasticsearchException e = ElasticsearchException e =
expectThrows(ElasticsearchException.class, () -> new SSLService(settings, env)); expectThrows(ElasticsearchException.class, () -> new SSLService(settings, env));
@ -293,6 +313,7 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.putArray("xpack.ssl.ciphers", ciphers.toArray(new String[ciphers.size()])) .putArray("xpack.ssl.ciphers", ciphers.toArray(new String[ciphers.size()]))
.build(); .build();
@ -309,6 +330,7 @@ public class SSLServiceTests extends ESTestCase {
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.putArray("xpack.ssl.cipher_suites", new String[]{"foo", "bar"}) .putArray("xpack.ssl.cipher_suites", new String[]{"foo", "bar"})
.build(); .build();
@ -322,6 +344,7 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.build(); .build();
SSLService sslService = new SSLService(settings, env); SSLService sslService = new SSLService(settings, env);
@ -335,6 +358,7 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.build(); .build();
SSLService sslService = new SSLService(settings, env); SSLService sslService = new SSLService(settings, env);
@ -359,6 +383,7 @@ public class SSLServiceTests extends ESTestCase {
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore) .put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings) .setSecureSettings(secureSettings)
.build(); .build();
SSLService sslService = new SSLService(settings, env); SSLService sslService = new SSLService(settings, env);

View File

@ -0,0 +1,43 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ssl;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import java.security.PrivateKey;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
public class StoreKeyConfigTests extends ESTestCase {
public void testCreateKeyManagerUsingJKS() throws Exception {
tryReadPrivateKeyFromKeyStore("jks", ".jks");
}
public void testCreateKeyManagerUsingPKCS12() throws Exception {
tryReadPrivateKeyFromKeyStore("PKCS12", ".p12");
}
private void tryReadPrivateKeyFromKeyStore(String type, String extension) {
final Settings settings = Settings.builder().put("path.home", createTempDir()).build();
final String path = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode" + extension).toString();
final SecureString keyStorePassword = new SecureString("testnode".toCharArray());
final StoreKeyConfig keyConfig = new StoreKeyConfig(path, type, keyStorePassword, keyStorePassword,
KeyManagerFactory.getDefaultAlgorithm(), TrustManagerFactory.getDefaultAlgorithm());
final X509ExtendedKeyManager keyManager = keyConfig.createKeyManager(new Environment(settings));
final PrivateKey key = keyManager.getPrivateKey("testnode");
assertThat(key, notNullValue());
assertThat(key.getAlgorithm(), equalTo("RSA"));
assertThat(key.getFormat(), equalTo("PKCS#8"));
}
}