security: transport always uses TLS (elastic/elasticsearch#4738)

This commit brings back support an auto-generated certificate and private key for
transport traffic. The auto-generated certificate and key can only be used in development
mode; when moving to production a key and certificate must be provided.

For the edge case of a user not wanting to encrypt their traffic, the user can set
the cipher_suites setting to `TLS_RSA_WITH_NULL_SHA256` or a like cipher, but a key/cert
is still required.

Closes elastic/elasticsearch#4332

Original commit: elastic/x-pack-elasticsearch@b7a1e629f5
This commit is contained in:
Jay Modi 2017-02-07 11:39:31 -05:00 committed by GitHub
parent 8820902f0c
commit bd04b30acd
67 changed files with 1263 additions and 680 deletions

View File

@ -5,10 +5,12 @@
*/
package org.elasticsearch.xpack;
import org.bouncycastle.operator.OperatorCreationException;
import org.elasticsearch.SpecialPermission;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
@ -89,15 +91,21 @@ import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.SecurityFeatureSet;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.ssl.SSLBootstrapCheck;
import org.elasticsearch.xpack.ssl.SSLConfigurationReloader;
import org.elasticsearch.xpack.ssl.SSLService;
import org.elasticsearch.xpack.watcher.Watcher;
import org.elasticsearch.xpack.watcher.WatcherFeatureSet;
import javax.security.auth.DestroyFailedException;
import java.io.IOException;
import java.nio.file.Path;
import java.security.AccessController;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.time.Clock;
import java.util.ArrayList;
import java.util.Collection;
@ -176,7 +184,8 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
protected Watcher watcher;
protected Graph graph;
public XPackPlugin(Settings settings) throws IOException {
public XPackPlugin(Settings settings) throws IOException, CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException,
KeyStoreException, DestroyFailedException, OperatorCreationException {
this.settings = settings;
this.transportClientMode = transportClientMode(settings);
this.env = transportClientMode ? null : new Environment(settings);
@ -498,4 +507,9 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
public UnaryOperator<RestHandler> getRestHandlerWrapper(ThreadContext threadContext) {
return security.getRestHandlerWrapper(threadContext);
}
@Override
public List<BootstrapCheck> getBootstrapChecks() {
return security.getBootstrapChecks();
}
}

View File

@ -5,7 +5,9 @@
*/
package org.elasticsearch.xpack;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.ssl.SSLClientAuth;
@ -45,8 +47,20 @@ public class XPackSettings {
/** Setting for enabling or disabling document/field level security. Defaults to true. */
public static final Setting<Boolean> DLS_FLS_ENABLED = enabledSetting(XPackPlugin.SECURITY + ".dls_fls", true);
/** Setting for enabling or disabling transport ssl. Defaults to false. */
public static final Setting<Boolean> TRANSPORT_SSL_ENABLED = enabledSetting(XPackPlugin.SECURITY + ".transport.ssl", false);
/**
* Legacy setting for enabling or disabling transport ssl. Defaults to true. This is just here to make upgrading easier since the
* user needs to set this setting in 5.x to upgrade
*/
private static final Setting<Boolean> TRANSPORT_SSL_ENABLED =
new Setting<>(XPackPlugin.featureSettingPrefix(XPackPlugin.SECURITY) + ".transport.ssl.enabled", (s) -> Boolean.toString(true),
(s) -> {
final boolean parsed = Booleans.parseBoolean(s);
if (parsed == false) {
throw new IllegalArgumentException("transport ssl cannot be disabled. Remove setting [" +
XPackPlugin.featureSettingPrefix(XPackPlugin.SECURITY) + ".transport.ssl.enabled]");
}
return true;
}, Property.NodeScope, Property.Deprecated);
/** Setting for enabling or disabling http ssl. Defaults to false. */
public static final Setting<Boolean> HTTP_SSL_ENABLED = enabledSetting(XPackPlugin.SECURITY + ".http.ssl", false);
@ -86,6 +100,7 @@ public class XPackSettings {
ALL_SETTINGS.addAll(GLOBAL_SSL.getAllSettings());
ALL_SETTINGS.addAll(HTTP_SSL.getAllSettings());
ALL_SETTINGS.addAll(TRANSPORT_SSL.getAllSettings());
ALL_SETTINGS.add(TRANSPORT_SSL_ENABLED);
}
/**

View File

@ -11,6 +11,7 @@ import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.service.ClusterService;
@ -125,6 +126,7 @@ import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4HttpServerTransport;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4Transport;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.ssl.SSLBootstrapCheck;
import org.elasticsearch.xpack.ssl.SSLService;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@ -448,6 +450,14 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
return settingsFilter;
}
public List<BootstrapCheck> getBootstrapChecks() {
if (enabled) {
return Collections.singletonList(new SSLBootstrapCheck(sslService, settings, env));
} else {
return Collections.emptyList();
}
}
public void onIndexModule(IndexModule module) {
if (enabled == false) {
return;

View File

@ -7,7 +7,6 @@ package org.elasticsearch.xpack.security;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.elasticsearch.common.Nullable;
@ -28,7 +27,6 @@ import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import static org.elasticsearch.xpack.XPackSettings.HTTP_SSL_ENABLED;
import static org.elasticsearch.xpack.XPackSettings.TRANSPORT_SSL_ENABLED;
/**
* Indicates whether the features of Security are currently in use
@ -106,10 +104,7 @@ public class SecurityFeatureSet implements XPackFeatureSet {
}
static Map<String, Object> sslUsage(Settings settings) {
Map<String, Object> map = new HashMap<>(2);
map.put("http", Collections.singletonMap("enabled", HTTP_SSL_ENABLED.get(settings)));
map.put("transport", Collections.singletonMap("enabled", TRANSPORT_SSL_ENABLED.get(settings)));
return map;
return Collections.singletonMap("http", Collections.singletonMap("enabled", HTTP_SSL_ENABLED.get(settings)));
}
static Map<String, Object> auditUsage(@Nullable AuditTrailService auditTrailService) {

View File

@ -22,7 +22,7 @@ import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.common.xcontent.XContentUtils;
import java.io.IOException;
import java.util.Arrays;
import java.nio.CharBuffer;
/**
* Request to change a user's password.
@ -44,15 +44,17 @@ public class ChangePasswordRequestBuilder
return this;
}
/**
* Sets the password. Note: the char[] passed to this method will be cleared.
*/
public ChangePasswordRequestBuilder password(char[] password) {
Validation.Error error = Validation.Users.validatePassword(password);
if (error != null) {
ValidationException validationException = new ValidationException();
validationException.addValidationError(error.toString());
throw validationException;
}
try (SecuredString securedString = new SecuredString(password)) {
Validation.Error error = Validation.Users.validatePassword(password);
if (error != null) {
ValidationException validationException = new ValidationException();
validationException.addValidationError(error.toString());
throw validationException;
}
request.passwordHash(Hasher.BCRYPT.hash(securedString));
}
return this;
@ -82,10 +84,10 @@ public class ChangePasswordRequestBuilder
} else if (User.Fields.PASSWORD.match(currentFieldName)) {
if (token == XContentParser.Token.VALUE_STRING) {
String password = parser.text();
char[] passwordChars = password.toCharArray();
final char[] passwordChars = password.toCharArray();
password(passwordChars);
password = null;
Arrays.fill(passwordChars, (char) 0);
assert CharBuffer.wrap(passwordChars).chars().noneMatch((i) -> (char) i != (char) 0) : "expected password to " +
"clear the char[] but it did not!";
} else {
throw new ElasticsearchParseException(
"expected field [{}] to be of type string, but found [{}] instead", currentFieldName, token);

View File

@ -42,6 +42,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.node.Node;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportMessage;
@ -777,7 +778,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
return queueConsumer.peek();
}
private static Client initializeRemoteClient(Settings settings, Logger logger) {
Client initializeRemoteClient(Settings settings, Logger logger) {
Settings clientSettings = REMOTE_CLIENT_SETTINGS.get(settings);
String[] hosts = clientSettings.getAsArray("hosts");
if (hosts.length == 0) {
@ -807,7 +808,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
final Settings theClientSetting = clientSettings.filter((s) -> s.startsWith("hosts") == false); // hosts is not a valid setting
final TransportClient transportClient = new TransportClient(Settings.builder()
.put("node.name", DEFAULT_CLIENT_NAME + "-" + Node.NODE_NAME_SETTING.get(settings))
.put(theClientSetting).build(), Settings.EMPTY, Collections.singletonList(XPackPlugin.class), null) {};
.put(theClientSetting).build(), Settings.EMPTY, remoteTransportClientPlugins(), null) {};
for (Tuple<String, Integer> pair : hostPortPairs) {
try {
transportClient.addTransportAddress(new TransportAddress(InetAddress.getByName(pair.v1()), pair.v2()));
@ -965,6 +966,11 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
}
}
// method for testing to allow different plugins such as mock transport...
List<Class<? extends Plugin>> remoteTransportClientPlugins() {
return Collections.singletonList(XPackPlugin.class);
}
public static void registerSettings(List<Setting<?>> settings) {
settings.add(INDEX_SETTINGS);
settings.add(EXCLUDE_EVENT_SETTINGS);

View File

@ -41,7 +41,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.elasticsearch.xpack.XPackSettings.HTTP_SSL_ENABLED;
import static org.elasticsearch.xpack.XPackSettings.TRANSPORT_SSL_ENABLED;
import static org.elasticsearch.xpack.security.Security.setting;
public class PkiRealm extends Realm {
@ -199,6 +198,7 @@ public class PkiRealm extends Realm {
* @param config this realm's configuration
* @param sslService the SSLService to use for ssl configurations
*/
// TODO move this to a Bootstrap check!
static void checkSSLEnabled(RealmConfig config, SSLService sslService) {
Settings settings = config.globalSettings();
@ -211,10 +211,9 @@ public class PkiRealm extends Realm {
}
// Default Transport
final boolean ssl = TRANSPORT_SSL_ENABLED.get(settings);
final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl."));
final boolean clientAuthEnabled = sslService.isSSLClientAuthEnabled(transportSSLSettings);
if (ssl && clientAuthEnabled) {
if (clientAuthEnabled) {
return;
}
@ -222,9 +221,7 @@ public class PkiRealm extends Realm {
Map<String, Settings> groupedSettings = settings.getGroups("transport.profiles.");
for (Map.Entry<String, Settings> entry : groupedSettings.entrySet()) {
Settings profileSettings = entry.getValue().getByPrefix(Security.settingPrefix());
if (SecurityNetty4Transport.PROFILE_SSL_SETTING.get(profileSettings)
&& sslService.isSSLClientAuthEnabled(
SecurityNetty4Transport.profileSslSettings(profileSettings), transportSSLSettings)) {
if (sslService.isSSLClientAuthEnabled(SecurityNetty4Transport.profileSslSettings(profileSettings), transportSSLSettings)) {
return;
}
}

View File

@ -9,6 +9,7 @@ import org.elasticsearch.ElasticsearchException;
import java.nio.CharBuffer;
import java.util.Arrays;
import java.util.Objects;
/**
* This is not a string but a CharSequence that can be cleared of its memory. Important for handling passwords.
@ -26,11 +27,11 @@ public class SecuredString implements CharSequence, AutoCloseable {
private boolean cleared = false;
/**
* Note: the passed in chars are duplicated
* Creates a new SecuredString from the chars. These chars will be cleared when the SecuredString is closed so they should not be
* used directly outside of using the SecuredString
*/
public SecuredString(char[] chars) {
this.chars = new char[chars.length];
System.arraycopy(chars, 0, this.chars, 0, chars.length);
this.chars = Objects.requireNonNull(chars, "chars must not be null!");
}
/**
@ -94,6 +95,7 @@ public class SecuredString implements CharSequence, AutoCloseable {
* @return A copy of the internal characters. May be used for caching.
*/
public char[] copyChars() {
throwIfCleared();
return Arrays.copyOf(chars, chars.length);
}

View File

@ -156,6 +156,10 @@ public class SecurityClient {
client.execute(PutUserAction.INSTANCE, request, listener);
}
/**
* Populates the {@link ChangePasswordRequest} with the username and password. Note: the passed in char[] will be cleared by this
* method.
*/
public ChangePasswordRequestBuilder prepareChangePassword(String username, char[] password) {
return new ChangePasswordRequestBuilder(client).username(username).password(password);
}

View File

@ -44,7 +44,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import static org.elasticsearch.xpack.XPackSettings.TRANSPORT_SSL_ENABLED;
import static org.elasticsearch.xpack.security.Security.setting;
public class SecurityServerTransportInterceptor extends AbstractComponent implements TransportInterceptor {
@ -142,10 +141,8 @@ public class SecurityServerTransportInterceptor extends AbstractComponent implem
final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl."));
for (Map.Entry<String, Settings> entry : profileSettingsMap.entrySet()) {
Settings profileSettings = entry.getValue();
final boolean profileSsl = SecurityNetty4Transport.PROFILE_SSL_SETTING.get(profileSettings);
final Settings profileSslSettings = SecurityNetty4Transport.profileSslSettings(profileSettings);
final boolean clientAuth = sslService.isSSLClientAuthEnabled(profileSslSettings, transportSSLSettings);
final boolean extractClientCert = profileSsl && clientAuth;
final boolean extractClientCert = sslService.isSSLClientAuthEnabled(profileSslSettings, transportSSLSettings);
String type = entry.getValue().get(SETTING_NAME, "node");
switch (type) {
case "client":
@ -161,9 +158,7 @@ public class SecurityServerTransportInterceptor extends AbstractComponent implem
}
if (!profileFilters.containsKey(TransportSettings.DEFAULT_PROFILE)) {
final boolean profileSsl = TRANSPORT_SSL_ENABLED.get(settings);
final boolean clientAuth = sslService.isSSLClientAuthEnabled(transportSSLSettings);
final boolean extractClientCert = profileSsl && clientAuth;
final boolean extractClientCert = sslService.isSSLClientAuthEnabled(transportSSLSettings);
profileFilters.put(TransportSettings.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService,
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled, securityContext));
}

View File

@ -92,7 +92,7 @@ public class SecurityNetty4HttpServerTransport extends Netty4HttpServerTransport
HttpSslChannelHandler(Netty4HttpServerTransport transport) {
super(transport, detailedErrorsEnabled, threadPool.getThreadContext());
this.sslSettings = SSLService.getHttpTransportSSLSettings(settings);
if (ssl && sslService.isConfigurationValidForServerUsage(sslSettings, Settings.EMPTY) == false) {
if (ssl && sslService.isConfigurationValidForServerUsage(sslSettings, false) == false) {
throw new IllegalArgumentException("a key must be provided to run as a server. the key should be configured using the " +
"[xpack.security.http.ssl.key] or [xpack.security.http.ssl.keystore.path] setting");
}

View File

@ -15,12 +15,10 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.network.NetworkService;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportSettings;
import org.elasticsearch.transport.netty4.Netty4Transport;
import org.elasticsearch.xpack.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
@ -31,7 +29,6 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress;
import static org.elasticsearch.xpack.security.Security.setting;
import static org.elasticsearch.xpack.XPackSettings.TRANSPORT_SSL_ENABLED;
/**
@ -39,12 +36,9 @@ import static org.elasticsearch.xpack.XPackSettings.TRANSPORT_SSL_ENABLED;
*/
public class SecurityNetty4Transport extends Netty4Transport {
public static final Setting<Boolean> PROFILE_SSL_SETTING = Setting.boolSetting(setting("ssl.enabled"), false);
private final SSLService sslService;
@Nullable private final IPFilter authenticator;
private final Settings transportSSLSettings;
private final boolean ssl;
@Inject
public SecurityNetty4Transport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays,
@ -52,7 +46,6 @@ public class SecurityNetty4Transport extends Netty4Transport {
@Nullable IPFilter authenticator, SSLService sslService) {
super(settings, threadPool, networkService, bigArrays, namedWriteableRegistry, circuitBreakerService);
this.authenticator = authenticator;
this.ssl = TRANSPORT_SSL_ENABLED.get(settings);
this.sslService = sslService;
this.transportSSLSettings = settings.getByPrefix(setting("transport.ssl."));
}
@ -77,32 +70,22 @@ public class SecurityNetty4Transport extends Netty4Transport {
class SecurityServerChannelInitializer extends ServerChannelInitializer {
private final boolean sslEnabled;
private final Settings securityProfileSettings;
SecurityServerChannelInitializer(String name, Settings profileSettings) {
super(name, profileSettings);
this.sslEnabled = PROFILE_SSL_SETTING.exists(profileSettings) ? PROFILE_SSL_SETTING.get(profileSettings) : ssl;
this.securityProfileSettings = profileSettings.getByPrefix(setting("ssl."));
if (sslEnabled && sslService.isConfigurationValidForServerUsage(securityProfileSettings, transportSSLSettings) == false) {
if (TransportSettings.DEFAULT_PROFILE.equals(name)) {
throw new IllegalArgumentException("a key must be provided to run as a server. the key should be configured using the "
+ "[xpack.security.transport.ssl.key] or [xpack.security.transport.ssl.keystore.path] setting");
}
throw new IllegalArgumentException("a key must be provided to run as a server. the key should be configured using the "
+ "[transport.profiles." + name + ".xpack.security.ssl.key] or [transport.profiles." + name
+ ".xpack.security.ssl.keystore.path] setting");
}
assert sslService.isConfigurationValidForServerUsage(securityProfileSettings, true) :
"the ssl configuration is not valid for server use but we are running as a server. this should have been caught by" +
" the key config validation";
}
@Override
protected void initChannel(Channel ch) throws Exception {
super.initChannel(ch);
if (sslEnabled) {
SSLEngine serverEngine = sslService.createSSLEngine(securityProfileSettings, transportSSLSettings);
serverEngine.setUseClientMode(false);
ch.pipeline().addFirst(new SslHandler(serverEngine));
}
SSLEngine serverEngine = sslService.createSSLEngine(securityProfileSettings, transportSSLSettings);
serverEngine.setUseClientMode(false);
ch.pipeline().addFirst(new SslHandler(serverEngine));
if (authenticator != null) {
ch.pipeline().addFirst(new IpFilterRemoteAddressFilter(authenticator, name));
}
@ -121,9 +104,7 @@ public class SecurityNetty4Transport extends Netty4Transport {
@Override
protected void initChannel(Channel ch) throws Exception {
super.initChannel(ch);
if (ssl) {
ch.pipeline().addFirst(new ClientSslHandlerInitializer(transportSSLSettings, sslService, hostnameVerificationEnabled));
}
ch.pipeline().addFirst(new ClientSslHandlerInitializer(transportSSLSettings, sslService, hostnameVerificationEnabled));
}
}

View File

@ -14,7 +14,9 @@ import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.Time;
import org.bouncycastle.cert.CertIOException;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
@ -27,6 +29,7 @@ import org.bouncycastle.openssl.X509TrustedCertificateBlock;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
@ -48,19 +51,26 @@ import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.security.auth.x500.X500Principal;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@ -96,7 +106,8 @@ public class CertUtils {
/**
* Returns a {@link X509ExtendedKeyManager} that is built from the provided private key and certificate chain
*/
static X509ExtendedKeyManager keyManager(Certificate[] certificateChain, PrivateKey privateKey, char[] password) throws Exception {
static X509ExtendedKeyManager keyManager(Certificate[] certificateChain, PrivateKey privateKey, char[] password)
throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException {
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(null, null);
// password must be non-null for keystore...
@ -107,7 +118,8 @@ public class CertUtils {
/**
* Returns a {@link X509ExtendedKeyManager} that is built from the provided keystore
*/
static X509ExtendedKeyManager keyManager(KeyStore keyStore, char[] password, String algorithm) throws Exception {
static X509ExtendedKeyManager keyManager(KeyStore keyStore, char[] password, String algorithm)
throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(keyStore, password);
KeyManager[] keyManagers = kmf.getKeyManagers();
@ -123,9 +135,9 @@ public class CertUtils {
* Creates a {@link X509ExtendedTrustManager} based on the provided certificates
* @param certificates the certificates to trust
* @return a trust manager that trusts the provided certificates
* @throws Exception if there is an error loading the certificates or trust manager
*/
public static X509ExtendedTrustManager trustManager(Certificate[] certificates) throws Exception {
public static X509ExtendedTrustManager trustManager(Certificate[] certificates)
throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException {
KeyStore store = KeyStore.getInstance("jks");
store.load(null, null);
int counter = 0;
@ -143,23 +155,24 @@ public class CertUtils {
* @param trustStoreAlgorithm the algorithm to use for the truststore
* @param env the environment to use for file resolution. May be {@code null}
* @return a trust manager with the trust material from the store
* @throws Exception if an error occurs when loading the truststore or the trust manager
*/
public static X509ExtendedTrustManager trustManager(String trustStorePath, String trustStorePassword, String trustStoreAlgorithm,
@Nullable Environment env) throws Exception {
@Nullable Environment env)
throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException {
try (InputStream in = Files.newInputStream(resolvePath(trustStorePath, env))) {
// TODO remove reliance on JKS since we can PKCS12 stores...
KeyStore trustStore = KeyStore.getInstance("jks");
assert trustStorePassword != null;
trustStore.load(in, trustStorePassword.toCharArray());
return CertUtils.trustManager(trustStore, trustStoreAlgorithm);
return trustManager(trustStore, trustStoreAlgorithm);
}
}
/**
* Creates a {@link X509ExtendedTrustManager} based on the trust material in the provided {@link KeyStore}
*/
static X509ExtendedTrustManager trustManager(KeyStore keyStore, String algorithm) throws Exception {
static X509ExtendedTrustManager trustManager(KeyStore keyStore, String algorithm)
throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init(keyStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
@ -176,9 +189,9 @@ public class CertUtils {
* @param certPaths the paths to the PEM encoded certificates
* @param environment the environment to resolve files against. May be {@code null}
* @return an array of {@link Certificate} objects
* @throws Exception if an error occurs reading a file or parsing a certificate
*/
public static Certificate[] readCertificates(List<String> certPaths, @Nullable Environment environment) throws Exception {
public static Certificate[] readCertificates(List<String> certPaths, @Nullable Environment environment)
throws CertificateException, IOException {
List<Certificate> certificates = new ArrayList<>(certPaths.size());
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
for (String path : certPaths) {
@ -192,7 +205,8 @@ public class CertUtils {
/**
* Reads the certificates from the provided reader
*/
static void readCertificates(Reader reader, List<Certificate> certificates, CertificateFactory certFactory) throws Exception {
static void readCertificates(Reader reader, List<Certificate> certificates, CertificateFactory certFactory)
throws IOException, CertificateException {
try (PEMParser pemParser = new PEMParser(reader)) {
Object parsed = pemParser.readObject();
@ -220,40 +234,31 @@ public class CertUtils {
/**
* Reads the private key from the reader and optionally uses the password supplier to retrieve a password if the key is encrypted
*/
static PrivateKey readPrivateKey(Reader reader, Supplier<char[]> passwordSupplier) throws Exception {
static PrivateKey readPrivateKey(Reader reader, Supplier<char[]> passwordSupplier) throws IOException {
try (PEMParser parser = new PEMParser(reader)) {
Object parsed;
List<Object> list = new ArrayList<>(1);
do {
parsed = parser.readObject();
if (parsed != null) {
list.add(parsed);
}
} while (parsed != null);
if (list.size() != 1) {
throw new IllegalStateException("key file contained [" + list.size() + "] entries, expected one");
final Object parsed = parser.readObject();
if (parser.readObject() != null) {
throw new IllegalStateException("key file contained more that one entry");
}
PrivateKeyInfo privateKeyInfo;
Object parsedObject = list.get(0);
if (parsedObject instanceof PEMEncryptedKeyPair) {
if (parsed instanceof PEMEncryptedKeyPair) {
char[] keyPassword = passwordSupplier.get();
if (keyPassword == null) {
throw new IllegalArgumentException("cannot read encrypted key without a password");
}
// we have an encrypted key pair so we need to decrypt it
PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) parsedObject;
PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) parsed;
privateKeyInfo = encryptedKeyPair
.decryptKeyPair(new JcePEMDecryptorProviderBuilder().setProvider(BC_PROV).build(keyPassword))
.getPrivateKeyInfo();
} else if (parsedObject instanceof PEMKeyPair) {
privateKeyInfo = ((PEMKeyPair) parsedObject).getPrivateKeyInfo();
} else if (parsedObject instanceof PrivateKeyInfo) {
privateKeyInfo = (PrivateKeyInfo) parsedObject;
} else if (parsed instanceof PEMKeyPair) {
privateKeyInfo = ((PEMKeyPair) parsed).getPrivateKeyInfo();
} else if (parsed instanceof PrivateKeyInfo) {
privateKeyInfo = (PrivateKeyInfo) parsed;
} else {
throw new IllegalArgumentException("parsed an unsupported object [" +
parsedObject.getClass().getSimpleName() + "]");
parsed.getClass().getSimpleName() + "]");
}
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
@ -264,7 +269,8 @@ public class CertUtils {
/**
* Generates a CA certificate
*/
static X509Certificate generateCACertificate(X500Principal x500Principal, KeyPair keyPair, int days) throws Exception {
static X509Certificate generateCACertificate(X500Principal x500Principal, KeyPair keyPair, int days)
throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException {
return generateSignedCertificate(x500Principal, null, keyPair, null, null, true, days);
}
@ -272,7 +278,8 @@ public class CertUtils {
* Generates a signed certificate using the provided CA private key and information from the CA certificate
*/
public static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair,
X509Certificate caCert, PrivateKey caPrivKey, int days) throws Exception {
X509Certificate caCert, PrivateKey caPrivKey, int days)
throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException {
return generateSignedCertificate(principal, subjectAltNames, keyPair, caCert, caPrivKey, false, days);
}
@ -286,11 +293,10 @@ public class CertUtils {
* @param caPrivKey the CA private key. If {@code null}, this results in a self signed certificate
* @param isCa whether or not the generated certificate is a CA
* @return a signed {@link X509Certificate}
* @throws Exception if an error occurs during the certificate creation
*/
private static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair,
X509Certificate caCert, PrivateKey caPrivKey, boolean isCa, int days)
throws Exception {
throws NoSuchAlgorithmException, CertificateException, CertIOException, OperatorCreationException {
final DateTime notBefore = new DateTime(DateTimeZone.UTC);
if (days < 1) {
throw new IllegalArgumentException("the certificate must be valid for at least one day");
@ -338,9 +344,9 @@ public class CertUtils {
* @param sanList the subject alternative names that should be added to the certificate as an X509v3 extension. May be
* {@code null}
* @return a certificate signing request
* @throws Exception if an error occurs generating or signing the CSR
*/
static PKCS10CertificationRequest generateCSR(KeyPair keyPair, X500Principal principal, GeneralNames sanList) throws Exception {
static PKCS10CertificationRequest generateCSR(KeyPair keyPair, X500Principal principal, GeneralNames sanList)
throws IOException, OperatorCreationException {
JcaPKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(principal, keyPair.getPublic());
if (sanList != null) {
ExtensionsGenerator extGen = new ExtensionsGenerator();
@ -364,7 +370,7 @@ public class CertUtils {
/**
* Generates a RSA key pair with the provided key size (in bits)
*/
static KeyPair generateKeyPair(int keysize) throws Exception {
static KeyPair generateKeyPair(int keysize) throws NoSuchAlgorithmException {
// generate a private key
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(keysize);
@ -374,7 +380,7 @@ public class CertUtils {
/**
* Converts the {@link InetAddress} objects into a {@link GeneralNames} object that is used to represent subject alternative names.
*/
static GeneralNames getSubjectAlternativeNames(boolean resolveName, Set<InetAddress> addresses) throws Exception {
static GeneralNames getSubjectAlternativeNames(boolean resolveName, Set<InetAddress> addresses) throws SocketException {
Set<GeneralName> generalNameList = new HashSet<>();
for (InetAddress address : addresses) {
if (address.isAnyLocalAddress()) {

View File

@ -13,6 +13,7 @@ import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -61,68 +62,10 @@ class DefaultJDKTrustConfig extends TrustConfig {
* @return a {@link TrustConfig} that represents a combination of both trust configurations
*/
static TrustConfig merge(TrustConfig trustConfig) {
return new CombiningTrustConfig(trustConfig);
}
/**
* A trust configuration that is a combination of a trust configuration with the default JDK trust configuration. This trust
* configuration returns a trust manager verifies certificates against both the default JDK trusted configurations and the specific
* {@link TrustConfig} provided.
*/
static class CombiningTrustConfig extends TrustConfig {
private final TrustConfig trustConfig;
private CombiningTrustConfig(TrustConfig trustConfig) {
this.trustConfig = trustConfig;
}
@Override
X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
X509ExtendedTrustManager trustManager = trustConfig.createTrustManager(environment);
X509ExtendedTrustManager defaultTrustManager = INSTANCE.createTrustManager(environment);
if (trustManager == null) {
return defaultTrustManager;
}
X509Certificate[] firstIssuers = trustManager.getAcceptedIssuers();
X509Certificate[] secondIssuers = defaultTrustManager.getAcceptedIssuers();
X509Certificate[] acceptedIssuers = new X509Certificate[firstIssuers.length + secondIssuers.length];
System.arraycopy(firstIssuers, 0, acceptedIssuers, 0, firstIssuers.length);
System.arraycopy(secondIssuers, 0, acceptedIssuers, firstIssuers.length, secondIssuers.length);
try {
return CertUtils.trustManager(acceptedIssuers);
} catch (Exception e) {
throw new ElasticsearchException("failed to create trust manager", e);
}
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return trustConfig.filesToMonitor(environment);
}
@Override
public String toString() {
return "Combining Trust Config{first=[" + trustConfig.toString() + "], second=[" + INSTANCE.toString() + "]}";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CombiningTrustConfig)) {
return false;
}
CombiningTrustConfig that = (CombiningTrustConfig) o;
return trustConfig.equals(that.trustConfig);
}
@Override
public int hashCode() {
return trustConfig.hashCode();
if (trustConfig == null) {
return INSTANCE;
} else {
return new CombiningTrustConfig(Arrays.asList(INSTANCE, trustConfig));
}
}
}

View File

@ -0,0 +1,211 @@
/*
* 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 org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.operator.OperatorCreationException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.hash.MessageDigests;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.node.Node;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.security.auth.DestroyFailedException;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.security.KeyPair;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* Represents a {@link KeyConfig} that is automatically generated on node startup if necessary. This helps with the default user experience
* so that the user does not need to have any knowledge about SSL setup to start a node
*/
final class GeneratedKeyConfig extends KeyConfig {
// these values have been generated using openssl
// For private key: openssl pkcs8 -in private.pem -inform PEM -nocrypt -topk8 -outform DER | openssl dgst -sha256 -hex
// For certificate: openssl x509 -in ca.pem -noout -fingerprint -sha256
private static final String PRIVATE_KEY_SHA256 = "eec5bdb422c17c75d3850ffc64a724e52a99ec64366677da2fe4e782d7426e9f";
private static final String CA_CERT_FINGERPRINT_SHA256 = "A147166C71EB8B61DADFC5B19ECAC8443BE2DB32A56FC1A73BC1623250738598";
private final X509ExtendedKeyManager keyManager;
private final X509ExtendedTrustManager trustManager;
GeneratedKeyConfig(Settings settings) throws NoSuchAlgorithmException, IOException, CertificateException, OperatorCreationException,
UnrecoverableKeyException, KeyStoreException {
final KeyPair keyPair = CertUtils.generateKeyPair(2048);
final X500Principal principal = new X500Principal("CN=" + Node.NODE_NAME_SETTING.get(settings));
final Certificate caCert = readCACert();
final PrivateKey privateKey = readPrivateKey();
final GeneralNames generalNames = CertUtils.getSubjectAlternativeNames(false, getLocalAddresses());
X509Certificate certificate =
CertUtils.generateSignedCertificate(principal, generalNames, keyPair, (X509Certificate) caCert, privateKey, 365);
try {
privateKey.destroy();
} catch (DestroyFailedException e) {
// best effort attempt. This is known to fail for RSA keys on the oracle JDK but maybe they'll fix it in ten years or so...
}
keyManager = CertUtils.keyManager(new Certificate[] { certificate, caCert }, keyPair.getPrivate(), new char[0]);
trustManager = CertUtils.trustManager(new Certificate[] { caCert });
}
@Override
X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
return trustManager;
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
// no files to watch
return Collections.emptyList();
}
@Override
public String toString() {
return "Generated Key Config. DO NOT USE IN PRODUCTION";
}
@Override
public boolean equals(Object o) {
return this == o;
}
@Override
public int hashCode() {
return Objects.hash(keyManager, trustManager);
}
@Override
X509ExtendedKeyManager createKeyManager(@Nullable Environment environment) {
return keyManager;
}
@Override
List<PrivateKey> privateKeys(@Nullable Environment environment) {
try {
return Collections.singletonList(readPrivateKey());
} catch (IOException e) {
throw new UncheckedIOException("failed to read key", e);
}
}
/**
* Enumerates all of the loopback and link local addresses so these can be used as SubjectAlternativeNames inside the certificate for
* a good out of the box experience with TLS
*/
private Set<InetAddress> getLocalAddresses() throws SocketException {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
Set<InetAddress> inetAddresses = new HashSet<>();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface intf = networkInterfaces.nextElement();
if (intf.isUp()) {
if (intf.isLoopback()) {
inetAddresses.addAll(Collections.list(intf.getInetAddresses()));
} else {
Enumeration<InetAddress> inetAddressEnumeration = intf.getInetAddresses();
while (inetAddressEnumeration.hasMoreElements()) {
InetAddress inetAddress = inetAddressEnumeration.nextElement();
if (inetAddress.isLoopbackAddress() || inetAddress.isLinkLocalAddress()) {
inetAddresses.add(inetAddress);
}
}
}
}
}
return inetAddresses;
}
/**
* Reads the bundled CA private key. This key is used for signing a automatically generated certificate that allows development nodes
* to talk to each other on the same machine.
*
* This private key is the same for every distribution and is only here for a nice out of the box experience. Once in production mode
* this key should not be used!
*/
static PrivateKey readPrivateKey() throws IOException {
try (InputStream inputStream = GeneratedKeyConfig.class.getResourceAsStream("private.pem");
Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
PrivateKey privateKey = CertUtils.readPrivateKey(reader, () -> null);
MessageDigest md = MessageDigests.sha256();
final byte[] privateKeyBytes = privateKey.getEncoded();
try {
final byte[] digest = md.digest(privateKeyBytes);
final byte[] expected = hexStringToByteArray(PRIVATE_KEY_SHA256);
if (Arrays.equals(digest, expected) == false) {
throw new IllegalStateException("private key hash does not match the expected value!");
}
} finally {
Arrays.fill(privateKeyBytes, (byte) 0);
}
return privateKey;
}
}
/**
* Reads the bundled CA certificate
*/
static Certificate readCACert() throws IOException, CertificateException {
try (InputStream inputStream = GeneratedKeyConfig.class.getResourceAsStream("ca.pem");
Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
List<Certificate> certificateList = new ArrayList<>(1);
CertUtils.readCertificates(reader, certificateList, certificateFactory);
if (certificateList.size() != 1) {
throw new IllegalStateException("expected [1] default CA certificate but found [" + certificateList.size() + "]");
}
Certificate certificate = certificateList.get(0);
final byte[] encoded = MessageDigests.sha256().digest(certificate.getEncoded());
final byte[] expected = hexStringToByteArray(CA_CERT_FINGERPRINT_SHA256);
if (Arrays.equals(encoded, expected) == false) {
throw new IllegalStateException("CA certificate fingerprint does not match!");
}
return certificateList.get(0);
}
}
private static byte[] hexStringToByteArray(String hexString) {
if (hexString.length() % 2 != 0) {
throw new IllegalArgumentException("String must be an even length");
}
final int numBytes = hexString.length() / 2;
final byte[] data = new byte[numBytes];
for(int i = 0; i < numBytes; i++) {
final int index = i * 2;
final int index2 = index + 1;
data[i] = (byte) ((Character.digit(hexString.charAt(index), 16) << 4) + Character.digit(hexString.charAt(index2), 16));
}
return data;
}
}

View File

@ -11,6 +11,7 @@ import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.security.PrivateKey;
import java.util.Collections;
import java.util.List;
@ -46,7 +47,14 @@ abstract class KeyConfig extends TrustConfig {
public int hashCode() {
return System.identityHashCode(this);
}
@Override
List<PrivateKey> privateKeys(@Nullable Environment environment) {
return Collections.emptyList();
}
};
abstract X509ExtendedKeyManager createKeyManager(@Nullable Environment environment);
abstract List<PrivateKey> privateKeys(@Nullable Environment environment);
}

View File

@ -8,15 +8,22 @@ package org.elasticsearch.xpack.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -46,30 +53,40 @@ class PEMKeyConfig extends KeyConfig {
@Override
X509ExtendedKeyManager createKeyManager(@Nullable Environment environment) {
// password must be non-null for keystore...
char[] password = keyPassword == null ? new char[0] : keyPassword.toCharArray();
try {
PrivateKey privateKey = readPrivateKey(CertUtils.resolvePath(keyPath, environment));
if (privateKey == null) {
throw new IllegalArgumentException("private key [" + keyPath + "] could not be loaded");
}
Certificate[] certificateChain = CertUtils.readCertificates(Collections.singletonList(certPath), environment);
// password must be non-null for keystore...
return CertUtils.keyManager(certificateChain, privateKey, password);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a KeyManagerFactory", e);
} finally {
if (password != null) {
Arrays.fill(password, (char) 0);
try (SecuredString securedKeyPasswordChars = new SecuredString(keyPassword == null ? new char[0] : keyPassword.toCharArray())) {
return CertUtils.keyManager(certificateChain, privateKey, securedKeyPasswordChars.internalChars());
}
} catch (IOException | UnrecoverableKeyException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
throw new ElasticsearchException("failed to initialize a KeyManagerFactory", e);
}
}
private PrivateKey readPrivateKey(Path keyPath) throws Exception {
char[] password = keyPassword == null ? null : keyPassword.toCharArray();
try (Reader reader = Files.newBufferedReader(keyPath, StandardCharsets.UTF_8)) {
return CertUtils.readPrivateKey(reader, () -> password);
} finally {
if (password != null) {
Arrays.fill(password, (char) 0);
}
@Override
List<PrivateKey> privateKeys(@Nullable Environment environment) {
try {
return Collections.singletonList(readPrivateKey(CertUtils.resolvePath(keyPath, environment)));
} catch (IOException e) {
throw new UncheckedIOException("failed to read key", e);
}
}
private PrivateKey readPrivateKey(Path keyPath) throws IOException {
try (Reader reader = Files.newBufferedReader(keyPath, StandardCharsets.UTF_8);
SecuredString securedString = new SecuredString(keyPassword == null ? new char[0] : keyPassword.toCharArray())) {
return CertUtils.readPrivateKey(reader, () -> {
if (keyPassword == null) {
return null;
} else {
return securedString.internalChars();
}
});
}
}

View File

@ -0,0 +1,99 @@
/*
* 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 org.elasticsearch.ElasticsearchException;
import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.XPackSettings;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Stream;
/**
* Bootstrap check to ensure that we only use the generated key config in non-production situations. This class is currently public because
* {@link org.elasticsearch.xpack.security.Security} is in a different package and we use package private accessors of the
* {@link SSLService} to get the configuration for the node to node transport
*/
public final class SSLBootstrapCheck implements BootstrapCheck {
private final SSLService sslService;
private final Settings settings;
private final Environment environment;
public SSLBootstrapCheck(SSLService sslService, Settings settings, @Nullable Environment environment) {
this.sslService = sslService;
this.settings = settings;
this.environment = environment;
}
@Override
public boolean check() {
final Settings transportSSLSettings = settings.getByPrefix(XPackSettings.TRANSPORT_SSL_PREFIX);
return sslService.sslConfiguration(transportSSLSettings).keyConfig() == KeyConfig.NONE
|| isDefaultCACertificateTrusted() || isDefaultPrivateKeyUsed();
}
/**
* Looks at all of the trusted certificates to ensure the default CA is not being trusted. We cannot let this happen in production mode
*/
private boolean isDefaultCACertificateTrusted() {
final PublicKey publicKey;
try {
publicKey = GeneratedKeyConfig.readCACert().getPublicKey();
} catch (IOException | CertificateException e) {
throw new ElasticsearchException("failed to check default CA", e);
}
return sslService.getLoadedSSLConfigurations().stream()
.flatMap(config -> Stream.of(config.keyConfig().createTrustManager(environment),
config.trustConfig().createTrustManager(environment)))
.filter(Objects::nonNull)
.flatMap((tm) -> Arrays.stream(tm.getAcceptedIssuers()))
.anyMatch((cert) -> {
try {
cert.verify(publicKey);
return true;
} catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException
| SignatureException e) {
// just ignore these
return false;
}
});
}
/**
* Looks at all of the private keys and if there is a key that is equal to the default CA key then we should bail out
*/
private boolean isDefaultPrivateKeyUsed() {
final PrivateKey defaultPrivateKey;
try {
defaultPrivateKey = GeneratedKeyConfig.readPrivateKey();
} catch (IOException e) {
throw new UncheckedIOException("failed to read key", e);
}
return sslService.getLoadedSSLConfigurations().stream()
.flatMap(sslConfiguration -> sslConfiguration.keyConfig().privateKeys(environment).stream())
.anyMatch(defaultPrivateKey::equals);
}
@Override
public String errorMessage() {
return "Default SSL key and certificate do not provide security; please generate keys and certificates";
}
}

View File

@ -7,7 +7,10 @@ package org.elasticsearch.xpack.ssl;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.lucene.util.SetOnce;
import org.bouncycastle.operator.OperatorCreationException;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.CheckedSupplier;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
@ -27,13 +30,17 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.security.auth.DestroyFailedException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.file.Path;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@ -56,26 +63,36 @@ public class SSLService extends AbstractComponent {
private final Map<SSLConfiguration, SSLContextHolder> sslContexts;
private final SSLConfiguration globalSSLConfiguration;
private final SetOnce<SSLConfiguration> transportSSLConfiguration = new SetOnce<>();
private final Environment env;
/**
* Create a new SSLService that parses the settings for the ssl contexts that need to be created, creates them, and then caches them
* for use later
*/
public SSLService(Settings settings, Environment environment) {
public SSLService(Settings settings, Environment environment) throws CertificateException, UnrecoverableKeyException,
NoSuchAlgorithmException, IOException, DestroyFailedException, KeyStoreException, OperatorCreationException {
super(settings);
this.env = environment;
this.globalSSLConfiguration = new SSLConfiguration(settings.getByPrefix(XPackSettings.GLOBAL_SSL_PREFIX));
this.sslContexts = loadSSLConfigurations();
}
private SSLService(Settings settings, Environment environment, SSLConfiguration globalSSLConfiguration,
Map<SSLConfiguration, SSLContextHolder> sslContexts) {
super(settings);
this.env = environment;
this.globalSSLConfiguration = globalSSLConfiguration;
this.sslContexts = sslContexts;
}
/**
* Creates a new SSLService that supports dynamic creation of SSLContext instances. Instances created by this service will not be
* cached and will not be monitored for reloading. This dynamic server does have access to the cached and monitored instances that
* have been created during initialization
*/
public SSLService createDynamicSSLService() {
return new SSLService(settings, env) {
return new SSLService(settings, env, globalSSLConfiguration, sslContexts) {
@Override
Map<SSLConfiguration, SSLContextHolder> loadSSLConfigurations() {
@ -87,11 +104,12 @@ public class SSLService extends AbstractComponent {
* Returns the existing {@link SSLContextHolder} for the configuration
* @throws IllegalArgumentException if not found
*/
@Override
SSLContextHolder sslContextHolder(SSLConfiguration sslConfiguration) {
SSLContextHolder holder = SSLService.this.sslContexts.get(sslConfiguration);
SSLContextHolder holder = sslContexts.get(sslConfiguration);
if (holder == null) {
// normally we'd throw here but let's create a new one that is not cached and will not be monitored for changes!
holder = SSLService.this.createSslContext(sslConfiguration);
holder = createSslContext(sslConfiguration);
}
return holder;
}
@ -218,13 +236,16 @@ public class SSLService extends AbstractComponent {
/**
* Returns whether the provided settings results in a valid configuration that can be used for server connections
* @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix
* @param fallback the settings that should be used for the fallback of the SSLConfiguration. Using {@link Settings#EMPTY}
* results in a fallback to the global configuration
* @param useTransportFallback if {@code true} this will use the transport configuration for fallback, otherwise the global
* configuration will be used
*/
public boolean isConfigurationValidForServerUsage(Settings settings, Settings fallback) {
SSLConfiguration sslConfiguration = sslConfiguration(settings, fallback);
return sslConfiguration.keyConfig() != KeyConfig.NONE;
public boolean isConfigurationValidForServerUsage(Settings settings, boolean useTransportFallback) {
SSLConfiguration fallback = useTransportFallback ? transportSSLConfiguration.get() : globalSSLConfiguration;
SSLConfiguration sslConfiguration = new SSLConfiguration(settings, fallback);
return sslConfiguration.keyConfig() != KeyConfig.NONE
|| (useTransportFallback && transportSSLConfiguration.get().keyConfig() == KeyConfig.NONE);
}
/**
* Indicates whether client authentication is enabled for a particular configuration
* @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix. The global configuration
@ -368,7 +389,18 @@ public class SSLService extends AbstractComponent {
new ReloadableTrustManager(sslConfiguration.trustConfig().createTrustManager(env), sslConfiguration.trustConfig());
ReloadableX509KeyManager keyManager =
new ReloadableX509KeyManager(sslConfiguration.keyConfig().createKeyManager(env), sslConfiguration.keyConfig());
return createSslContext(keyManager, trustManager, sslConfiguration);
}
/**
* Creates an {@link SSLContext} based on the provided configuration and trust/key managers
* @param sslConfiguration the configuration to use for context creation
* @param keyManager the key manager to use
* @param trustManager the trust manager to use
* @return the created SSLContext
*/
private SSLContextHolder createSslContext(ReloadableX509KeyManager keyManager, ReloadableTrustManager trustManager,
SSLConfiguration sslConfiguration) {
// Initialize sslContext
try {
SSLContext sslContext = SSLContext.getInstance(sslContextAlgorithm(sslConfiguration.supportedProtocols()));
@ -386,32 +418,82 @@ public class SSLService extends AbstractComponent {
/**
* Parses the settings to load all SSLConfiguration objects that will be used.
*/
Map<SSLConfiguration, SSLContextHolder> loadSSLConfigurations() {
Map<SSLConfiguration, SSLContextHolder> loadSSLConfigurations() throws CertificateException,
UnrecoverableKeyException, NoSuchAlgorithmException, IOException, DestroyFailedException, KeyStoreException,
OperatorCreationException {
Map<SSLConfiguration, SSLContextHolder> sslConfigurations = new HashMap<>();
sslConfigurations.put(globalSSLConfiguration, createSslContext(globalSSLConfiguration));
final Settings transportSSLSettings = settings.getByPrefix(XPackSettings.TRANSPORT_SSL_PREFIX);
List<Settings> sslSettingsList = new ArrayList<>();
sslSettingsList.add(transportSSLSettings);
sslSettingsList.add(getHttpTransportSSLSettings(settings));
sslSettingsList.add(settings.getByPrefix("xpack.http.ssl."));
sslSettingsList.addAll(getRealmsSSLSettings(settings));
sslSettingsList.addAll(getMonitoringExporterSettings(settings));
for (Settings sslSettings : sslSettingsList) {
SSLConfiguration sslConfiguration = new SSLConfiguration(sslSettings, globalSSLConfiguration);
if (sslConfigurations.containsKey(sslConfiguration) == false) {
sslConfigurations.put(sslConfiguration, createSslContext(sslConfiguration));
}
}
sslSettingsList.forEach((sslSettings) ->
sslConfigurations.computeIfAbsent(new SSLConfiguration(sslSettings, globalSSLConfiguration), this::createSslContext));
// transport profiles are special since they fallback is to the transport settings which in turn falls back to global.
SSLConfiguration transportSSLConfiguration = new SSLConfiguration(transportSSLSettings, globalSSLConfiguration);
for (Settings profileSettings : getTransportProfileSSLSettings(settings)) {
SSLConfiguration sslConfiguration = new SSLConfiguration(profileSettings, transportSSLConfiguration);
if (sslConfigurations.containsKey(sslConfiguration) == false) {
sslConfigurations.put(sslConfiguration, createSslContext(sslConfiguration));
}
// transport is special because we want to use a auto-generated key when there isn't one
final SSLConfiguration transportSSLConfiguration = new SSLConfiguration(transportSSLSettings, globalSSLConfiguration);
this.transportSSLConfiguration.set(transportSSLConfiguration);
List<Settings> profileSettings = getTransportProfileSSLSettings(settings);
// if no key is provided for transport we can auto-generate a key with a signed certificate for development use only. There is a
// bootstrap check that prevents this configuration from being use in production (SSLBootstrapCheck)
if (transportSSLConfiguration.keyConfig() == KeyConfig.NONE) {
// lazily generate key to avoid slowing down startup where we do not need it
final GeneratedKeyConfig generatedKeyConfig = new GeneratedKeyConfig(settings);
final TrustConfig trustConfig =
new TrustConfig.CombiningTrustConfig(Arrays.asList(transportSSLConfiguration.trustConfig(), new TrustConfig() {
@Override
X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
return generatedKeyConfig.createTrustManager(environment);
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return Collections.emptyList();
}
@Override
public String toString() {
return "Generated Trust Config. DO NOT USE IN PRODUCTION";
}
@Override
public boolean equals(Object o) {
return this == o;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
}));
X509ExtendedTrustManager extendedTrustManager = trustConfig.createTrustManager(env);
ReloadableTrustManager trustManager = new ReloadableTrustManager(extendedTrustManager, trustConfig);
ReloadableX509KeyManager keyManager =
new ReloadableX509KeyManager(generatedKeyConfig.createKeyManager(env), generatedKeyConfig);
sslConfigurations.put(transportSSLConfiguration, createSslContext(keyManager, trustManager, transportSSLConfiguration));
profileSettings.forEach((profileSetting) -> {
SSLConfiguration configuration = new SSLConfiguration(profileSetting, transportSSLConfiguration);
if (configuration.keyConfig() == KeyConfig.NONE) {
sslConfigurations.compute(configuration, (conf, holder) -> {
if (holder != null && holder.keyManager == keyManager && holder.trustManager == trustManager) {
return holder;
} else {
return createSslContext(keyManager, trustManager, configuration);
}
});
} else {
sslConfigurations.computeIfAbsent(configuration, this::createSslContext);
}
});
} else {
sslConfigurations.computeIfAbsent(transportSSLConfiguration, this::createSslContext);
profileSettings.forEach((profileSetting) ->
sslConfigurations.computeIfAbsent(new SSLConfiguration(profileSetting, transportSSLConfiguration), this::createSslContext));
}
return Collections.unmodifiableMap(sslConfigurations);
}

View File

@ -8,14 +8,24 @@ package org.elasticsearch.xpack.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
@ -49,13 +59,13 @@ class StoreKeyConfig extends KeyConfig {
@Override
X509ExtendedKeyManager createKeyManager(@Nullable Environment 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("jks");
assert keyStorePassword != null;
ks.load(in, keyStorePassword.toCharArray());
return CertUtils.keyManager(ks, keyPassword.toCharArray(), keyStoreAlgorithm);
} catch (Exception e) {
try {
KeyStore ks = getKeyStore(environment);
checkKeyStore(ks);
try (SecuredString keyPasswordSecuredString = new SecuredString(keyPassword.toCharArray())) {
return CertUtils.keyManager(ks, keyPasswordSecuredString.internalChars(), keyStoreAlgorithm);
}
} catch (IOException | CertificateException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) {
throw new ElasticsearchException("failed to initialize a KeyManagerFactory", e);
}
}
@ -74,6 +84,54 @@ class StoreKeyConfig extends KeyConfig {
return Collections.singletonList(CertUtils.resolvePath(keyStorePath, environment));
}
@Override
List<PrivateKey> privateKeys(@Nullable Environment environment) {
try {
KeyStore keyStore = getKeyStore(environment);
try (SecuredString keyPasswordSecuredString = new SecuredString(keyPassword.toCharArray())) {
List<PrivateKey> privateKeys = new ArrayList<>();
for (Enumeration<String> e = keyStore.aliases(); e.hasMoreElements(); ) {
final String alias = e.nextElement();
if (keyStore.isKeyEntry(alias)) {
Key key = keyStore.getKey(alias, keyPasswordSecuredString.internalChars());
if (key instanceof PrivateKey) {
privateKeys.add((PrivateKey) key);
}
}
}
return privateKeys;
}
} catch (Exception e) {
throw new ElasticsearchException("failed to list keys", e);
}
}
private KeyStore getKeyStore(@Nullable Environment environment)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
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("jks");
if (keyStorePassword == null) {
throw new IllegalArgumentException("keystore password may not be null");
}
try (SecuredString keyStorePasswordSecuredString = new SecuredString(keyStorePassword.toCharArray())) {
ks.load(in, keyStorePasswordSecuredString.internalChars());
}
return ks;
}
}
private void checkKeyStore(KeyStore keyStore) throws KeyStoreException {
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if (keyStore.isKeyEntry(alias)) {
return;
}
}
throw new IllegalArgumentException("the keystore [" + keyStorePath + "] does not contain a private key entry");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -5,12 +5,18 @@
*/
package org.elasticsearch.xpack.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* The configuration of trust material for SSL usage
@ -43,4 +49,63 @@ abstract class TrustConfig {
* {@inheritDoc}. Declared as abstract to force implementors to provide a custom implementation
*/
public abstract int hashCode();
/**
* A trust configuration that is a combination of a trust configuration with the default JDK trust configuration. This trust
* configuration returns a trust manager verifies certificates against both the default JDK trusted configurations and the specific
* {@link TrustConfig} provided.
*/
static class CombiningTrustConfig extends TrustConfig {
private final List<TrustConfig> trustConfigs;
CombiningTrustConfig(List<TrustConfig> trustConfig) {
this.trustConfigs = Collections.unmodifiableList(trustConfig);
}
@Override
X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
Optional<TrustConfig> matchAll = trustConfigs.stream().filter(TrustAllConfig.INSTANCE::equals).findAny();
if (matchAll.isPresent()) {
return matchAll.get().createTrustManager(environment);
}
try {
return CertUtils.trustManager(trustConfigs.stream()
.flatMap((tc) -> Arrays.stream(tc.createTrustManager(environment).getAcceptedIssuers()))
.collect(Collectors.toList())
.toArray(new X509Certificate[0]));
} catch (Exception e) {
throw new ElasticsearchException("failed to create trust manager", e);
}
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return trustConfigs.stream().flatMap((tc) -> tc.filesToMonitor(environment).stream()).collect(Collectors.toList());
}
@Override
public String toString() {
return "Combining Trust Config{" + trustConfigs.stream().map(TrustConfig::toString).collect(Collectors.joining(", ")) + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CombiningTrustConfig)) {
return false;
}
CombiningTrustConfig that = (CombiningTrustConfig) o;
return trustConfigs.equals(that.trustConfigs);
}
@Override
public int hashCode() {
return trustConfigs.hashCode();
}
}
}

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDWDCCAkCgAwIBAgIJANRlkT/I8aROMA0GCSqGSIb3DQEBCwUAMCYxJDAiBgNV
BAMTG3hwYWNrIHB1YmxpYyBkZXZlbG9wbWVudCBjYTAeFw0xNzAxMDUxNDUyMDNa
Fw00NDA1MjMxNDUyMDNaMCYxJDAiBgNVBAMTG3hwYWNrIHB1YmxpYyBkZXZlbG9w
bWVudCBjYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALBfQEQYZmPW
cAw939i8RRsa27+qxd32ysJu9aKgSEiIDFKU0JwFh6pog1l8frICM4jF0TqILGHv
+QbQYsD2e3jYp0cj8dy2+YN6jgTXMf1N8yh6GYXEzRrEKYhqVTHLpZgbhxEFxsws
gZiEMHiVxn6h5i4uWDmkp6zt4kHlKgvjtIEzZ1xiXWcS7jJvVPb8r0xUFPDu8Qij
BhjxkbkXprzjGEtt4bKqZ8/R+pr+eUuvmApMSMB38dZxDRXxyavbmbJcGDJX+ZKN
4OcECH55B/EtxhPxpfFXmX+y5Lh597vkhgitw8Qhayaa8gF16tt4rUgYude9kGSi
m3hs6Q9mWM8CAwEAAaOBiDCBhTAdBgNVHQ4EFgQUM6+ZLgmnj1FXHEPejFcpiRR+
ANIwVgYDVR0jBE8wTYAUM6+ZLgmnj1FXHEPejFcpiRR+ANKhKqQoMCYxJDAiBgNV
BAMTG3hwYWNrIHB1YmxpYyBkZXZlbG9wbWVudCBjYYIJANRlkT/I8aROMAwGA1Ud
EwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBABqgr2p+Ivb3myF56BuiJYYz55Wa
ncm4Aqdw6p/A5qkl3pSXu2zbgSfyFvux7Q1+lowIvw4fAOTBcQQpQkYWJmObkCLg
HMiKbBreFVqPOqScjTBk6t1g/mOdJXfOognc6QRwfunEBqevNVDT2w3sGlNHooEM
3XUPBgyuznE1Olqt7U0tMGsENyBgZv51bUg7ZZCLrV2sdgqc3XYZUqBnttvbBDyU
tozgDMoCXLvVHcpWcKsA+ONd0szbSAu1uF0ZfqgaoSslM1ph9ydPbXEvnD5AFO6Y
VBTW3v4cnluhrxO6TwRqNo43L5ENqZhtX9gVtzQ54exQsuoKzZ8NO5X1uIA=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsF9ARBhmY9ZwDD3f2LxFGxrbv6rF3fbKwm71oqBISIgMUpTQ
nAWHqmiDWXx+sgIziMXROogsYe/5BtBiwPZ7eNinRyPx3Lb5g3qOBNcx/U3zKHoZ
hcTNGsQpiGpVMculmBuHEQXGzCyBmIQweJXGfqHmLi5YOaSnrO3iQeUqC+O0gTNn
XGJdZxLuMm9U9vyvTFQU8O7xCKMGGPGRuRemvOMYS23hsqpnz9H6mv55S6+YCkxI
wHfx1nENFfHJq9uZslwYMlf5ko3g5wQIfnkH8S3GE/Gl8VeZf7LkuHn3u+SGCK3D
xCFrJpryAXXq23itSBi5172QZKKbeGzpD2ZYzwIDAQABAoIBADRpKbzSj2Ktr4BD
xsguMk76rUCIq+Ho25npxT69aJ19KERGCrPChO0jv5yQ/UlClDPZrPI60w2LdTIM
LLxwwoJHx3XBfbb7/KuQeLGBjU5bop1tozX4JIcGsdzi1ExG2v+XdoydbdTwiNZc
udark1/AFpm0le0TO+yMiEbSpasAUetmwmBLl0ld1qOoEFNM4ueLtM0/JE4kQHJC
a6a0fS1D+TQsPCdziW80X2hpwCIbg4CF3LqR521SfwIzRscbaXzCzeBNCShJE8Nm
Qun91Szze80aaFBBIwMKbppEx5iYCCKeTyO3yswRuZ44+iBe/piB3F/qRKnjBwNS
LeL9NOECgYEA4xMUueF8HN23QeC/KZ/6LALwyNBtT7JP7YbW6dvo+3F0KSPxbDL1
nMmeOTV8suAlGslsE+GuvPU6M9fUCxpbVnbYH5hEuh0v25WRekFv14H/yEpVF26o
OHeilUIzpRTUOndgkmN8cXNp2xkzs2Yp7F2RSlog2kXQOYgC91YmvjECgYEAxtbC
OzxUUjebeqnolo8wYur42BievUDqyD9pCnaix+2F59SdLDgirJIOEtv31jjpLaIh
nO8akxMCPNwhEgVzelI2k+jJ+Kermi3+tEAnlBBDf/tMEGNav2XE3MnYkDt2jdza
fganfhKQwAufyq2lUHC/Slh+xcLPepTef6zFxv8CgYB6ZEJ7ninDdU3dWEIxMWUq
a7tUweLpXfbu1Arqqfl97bzqn9D0vNLd215I/6di0qWtNnvmi3Ifrx3b660C/wXU
KOJ8xRnmJu0wsgFjn/mkcxFm54nNw3swVGtxf+lORVfO26FVxgHBNLANxBu1yo82
M4ioRsQGYjLFj6XpoqnnQQKBgE8RpYlCs1FCdZxwpmIArMAZKj1chPtDLlnVBWM4
zABuzpni7WFhLUCsj9YmDMbuOKOB3pX2av3jSDeFXc05x7LzsGpe3rn3iwCzm554
CIUTdpQVDSlTKQoFYSRfS7QHQVymX2hQIxi6Lz9/H9rL9Hopa5gX2smvbywSuOvS
e49nAoGAM7TQ9iFBsygXxbxh2EL47nw/LBxbDm86TazpSKrHd9pV6Z/Xv870QEf7
cZJ9T/KRGkxlK8L6B7uzeckpk4uMWuDRiymnbg2pqk94oELKkh0iLnlGSMf3IPO8
qIRFQsQfA3PaU6SG/izaB1lquBRtIj5kAW2ZXI4O9l5V39Y5/n4=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsF9ARBhmY9ZwDD3f2LxF
Gxrbv6rF3fbKwm71oqBISIgMUpTQnAWHqmiDWXx+sgIziMXROogsYe/5BtBiwPZ7
eNinRyPx3Lb5g3qOBNcx/U3zKHoZhcTNGsQpiGpVMculmBuHEQXGzCyBmIQweJXG
fqHmLi5YOaSnrO3iQeUqC+O0gTNnXGJdZxLuMm9U9vyvTFQU8O7xCKMGGPGRuRem
vOMYS23hsqpnz9H6mv55S6+YCkxIwHfx1nENFfHJq9uZslwYMlf5ko3g5wQIfnkH
8S3GE/Gl8VeZf7LkuHn3u+SGCK3DxCFrJpryAXXq23itSBi5172QZKKbeGzpD2ZY
zwIDAQAB
-----END PUBLIC KEY-----

View File

@ -179,10 +179,6 @@ public class ClearRealmsCacheTests extends SecurityIntegTestCase {
.put(NetworkModule.HTTP_ENABLED.getKey(), true)
.build();
}
@Override
public boolean sslTransportEnabled() {
return false;
}
@Override
protected String configRoles() {

View File

@ -54,13 +54,11 @@ public abstract class AbstractAdLdapRealmTestCase extends SecurityIntegTestCase
protected static RealmConfig realmConfig;
protected static boolean useGlobalSSL;
protected static boolean sslEnabled;
@BeforeClass
public static void setupRealm() {
realmConfig = randomFrom(RealmConfig.values());
useGlobalSSL = randomBoolean();
sslEnabled = randomBoolean();
ESLoggerFactory.getLogger("test").info("running test with realm configuration [{}], with direct group to role mapping [{}]. " +
"Settings [{}]", realmConfig, realmConfig.mapGroupsAsRoles, realmConfig.settings.getAsMap());
}
@ -74,20 +72,34 @@ public abstract class AbstractAdLdapRealmTestCase extends SecurityIntegTestCase
protected Settings nodeSettings(int nodeOrdinal) {
Path nodeFiles = createTempDir();
Path store = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks");
Settings.Builder builder = Settings.builder()
.put(super.nodeSettings(nodeOrdinal))
.put(realmConfig.buildSettings(store, "testnode"))
Settings.Builder builder = Settings.builder();
if (useGlobalSSL) {
builder.put(super.nodeSettings(nodeOrdinal).filter((s) -> s.startsWith("xpack.ssl.") == false))
.put(sslSettingsForStore(store, "testnode"));
} else {
builder.put(super.nodeSettings(nodeOrdinal));
}
builder.put(realmConfig.buildSettings(store, "testnode"))
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".files.role_mapping", writeFile(nodeFiles, "role_mapping.yml",
configRoleMappings()));
if (sslEnabled == false && useGlobalSSL) {
builder.put(sslSettingsForStore(store, "testnode"));
}
return builder.build();
}
@Override
protected boolean sslTransportEnabled() {
return sslEnabled;
protected Settings transportClientSettings() {
if (useGlobalSSL) {
Path store = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks");
return Settings.builder()
.put(super.transportClientSettings().filter((s) -> s.startsWith("xpack.ssl.") == false))
.put(sslSettingsForStore(store, "testnode"))
.build();
} else {
return super.transportClientSettings();
}
}
@Override
protected boolean useGeneratedSSLConfig() {
return useGlobalSSL == false;
}
protected String configRoleMappings() {

View File

@ -145,12 +145,12 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
case SUITE:
if (customSecuritySettingsSource == null) {
customSecuritySettingsSource =
new CustomSecuritySettingsSource(sslTransportEnabled(), createTempDir(), currentClusterScope);
new CustomSecuritySettingsSource(useGeneratedSSLConfig(), createTempDir(), currentClusterScope);
}
break;
case TEST:
customSecuritySettingsSource =
new CustomSecuritySettingsSource(sslTransportEnabled(), createTempDir(), currentClusterScope);
new CustomSecuritySettingsSource(useGeneratedSSLConfig(), createTempDir(), currentClusterScope);
break;
}
}
@ -291,11 +291,9 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
}
/**
* Allows to control whether ssl is enabled or not on the transport layer when the
* {@link org.elasticsearch.test.ESIntegTestCase.ClusterScope} is set to
* {@link org.elasticsearch.test.ESIntegTestCase.Scope#SUITE} or {@link org.elasticsearch.test.ESIntegTestCase.Scope#TEST}
* Allows to control whether ssl key information is auto generated or not on the transport layer
*/
protected boolean sslTransportEnabled() {
protected boolean useGeneratedSSLConfig() {
return randomBoolean();
}
@ -309,8 +307,8 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
private class CustomSecuritySettingsSource extends SecuritySettingsSource {
private CustomSecuritySettingsSource(boolean sslTransportEnabled, Path configDir, Scope scope) {
super(maxNumberOfNodes(), sslTransportEnabled, configDir, scope);
private CustomSecuritySettingsSource(boolean useGeneratedSSLConfig, Path configDir, Scope scope) {
super(maxNumberOfNodes(), useGeneratedSSLConfig, configDir, scope);
}
@Override

View File

@ -74,36 +74,36 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
private final Path parentFolder;
private final String subfolderPrefix;
private final byte[] systemKey;
private final boolean sslTransportEnabled;
private final boolean useGeneratedSSLConfig;
private final boolean hostnameVerificationEnabled;
/**
* Creates a new {@link org.elasticsearch.test.NodeConfigurationSource} for the security configuration.
*
* @param numOfNodes the number of nodes for proper unicast configuration (can be more than actually available)
* @param sslTransportEnabled whether ssl should be enabled on the transport layer or not
* @param useGeneratedSSLConfig whether ssl key/cert should be auto-generated
* @param parentFolder the parent folder that will contain all of the configuration files that need to be created
* @param scope the scope of the test that is requiring an instance of SecuritySettingsSource
*/
public SecuritySettingsSource(int numOfNodes, boolean sslTransportEnabled, Path parentFolder, Scope scope) {
this(numOfNodes, sslTransportEnabled, generateKey(), parentFolder, scope);
public SecuritySettingsSource(int numOfNodes, boolean useGeneratedSSLConfig, Path parentFolder, Scope scope) {
this(numOfNodes, useGeneratedSSLConfig, generateKey(), parentFolder, scope);
}
/**
* Creates a new {@link org.elasticsearch.test.NodeConfigurationSource} for the security configuration.
*
* @param numOfNodes the number of nodes for proper unicast configuration (can be more than actually available)
* @param sslTransportEnabled whether ssl should be enabled on the transport layer or not
* @param useGeneratedSSLConfig whether ssl key/cert should be auto-generated
* @param systemKey the system key that all of the nodes will use to sign messages
* @param parentFolder the parent folder that will contain all of the configuration files that need to be created
* @param scope the scope of the test that is requiring an instance of SecuritySettingsSource
*/
public SecuritySettingsSource(int numOfNodes, boolean sslTransportEnabled, byte[] systemKey, Path parentFolder, Scope scope) {
public SecuritySettingsSource(int numOfNodes, boolean useGeneratedSSLConfig, byte[] systemKey, Path parentFolder, Scope scope) {
super(numOfNodes, DEFAULT_SETTINGS);
this.systemKey = systemKey;
this.parentFolder = parentFolder;
this.subfolderPrefix = scope.name();
this.sslTransportEnabled = sslTransportEnabled;
this.useGeneratedSSLConfig = useGeneratedSSLConfig;
this.hostnameVerificationEnabled = randomBoolean();
}
@ -211,10 +211,10 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt",
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/openldap.crt",
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"),
sslTransportEnabled, hostnameVerificationEnabled, false);
useGeneratedSSLConfig, hostnameVerificationEnabled, false);
}
return getSSLSettingsForStore("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks", "testnode",
sslTransportEnabled, hostnameVerificationEnabled, false);
useGeneratedSSLConfig, hostnameVerificationEnabled, false);
}
public Settings getClientSSLSettings() {
@ -223,11 +223,11 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt",
Arrays.asList("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt",
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"),
sslTransportEnabled, hostnameVerificationEnabled, true);
useGeneratedSSLConfig, hostnameVerificationEnabled, true);
}
return getSSLSettingsForStore("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks", "testclient",
sslTransportEnabled, hostnameVerificationEnabled, true);
useGeneratedSSLConfig, hostnameVerificationEnabled, true);
}
/**
@ -238,26 +238,26 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
* @return the configuration settings
*/
public static Settings getSSLSettingsForStore(String resourcePathToStore, String password) {
return getSSLSettingsForStore(resourcePathToStore, password, true, true, true);
return getSSLSettingsForStore(resourcePathToStore, password, false, true, true);
}
private static Settings getSSLSettingsForStore(String resourcePathToStore, String password, boolean sslTransportEnabled,
private static Settings getSSLSettingsForStore(String resourcePathToStore, String password, boolean useGeneratedSSLConfig,
boolean hostnameVerificationEnabled, boolean transportClient) {
Path store = resolveResourcePath(resourcePathToStore);
Settings.Builder builder = Settings.builder().put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), sslTransportEnabled);
Settings.Builder builder = Settings.builder();
if (transportClient == false) {
builder.put("xpack.security.http.ssl.enabled", false);
}
if (sslTransportEnabled) {
builder.put("xpack.ssl.verification_mode", hostnameVerificationEnabled ? "full" : "certificate");
if (useGeneratedSSLConfig == false) {
builder.put("xpack.ssl.keystore.path", store)
.put("xpack.ssl.keystore.password", password)
.put("xpack.ssl.verification_mode", hostnameVerificationEnabled ? "full" : "certificate");
.put("xpack.ssl.keystore.password", password);
}
if (sslTransportEnabled && randomBoolean()) {
if (useGeneratedSSLConfig == false && randomBoolean()) {
builder.put("xpack.ssl.truststore.path", store)
.put("xpack.ssl.truststore.password", password);
}
@ -265,20 +265,19 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
}
private static Settings getSSLSettingsForPEMFiles(String keyPath, String password, String certificatePath,
List<String> trustedCertificates, boolean sslTransportEnabled,
List<String> trustedCertificates, boolean useGeneratedSSLConfig,
boolean hostnameVerificationEnabled, boolean transportClient) {
Settings.Builder builder = Settings.builder();
builder.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), sslTransportEnabled);
if (transportClient == false) {
builder.put("xpack.security.http.ssl.enabled", false);
}
if (sslTransportEnabled) {
builder.put("xpack.ssl.verification_mode", hostnameVerificationEnabled ? "full" : "certificate");
if (useGeneratedSSLConfig == false) {
builder.put("xpack.ssl.key", resolveResourcePath(keyPath))
.put("xpack.ssl.key_passphrase", password)
.put("xpack.ssl.certificate", resolveResourcePath(certificatePath))
.put("xpack.ssl.verification_mode", hostnameVerificationEnabled ? "full" : "certificate");
.put("xpack.ssl.certificate", resolveResourcePath(certificatePath));
if (trustedCertificates.isEmpty() == false) {
builder.put("xpack.ssl.certificate_authorities",

View File

@ -18,7 +18,6 @@ import org.hamcrest.Matcher;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -32,7 +31,7 @@ public class SettingsFilterTests extends ESTestCase {
private Settings.Builder configuredSettingsBuilder = Settings.builder();
private Map<String, Matcher> settingsMatcherMap = new HashMap<>();
public void testFiltering() throws IOException {
public void testFiltering() throws Exception {
configureUnfilteredSetting("xpack.security.authc.realms.file.type", "file");
// ldap realm filtering

View File

@ -5,17 +5,24 @@
*/
package org.elasticsearch.xpack;
import org.bouncycastle.operator.OperatorCreationException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.support.clock.ClockMock;
import org.elasticsearch.xpack.watcher.test.TimeWarpedWatcher;
import javax.security.auth.DestroyFailedException;
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.time.Clock;
public class TimeWarpedXPackPlugin extends XPackPlugin {
private final ClockMock clock = new ClockMock();
public TimeWarpedXPackPlugin(Settings settings) throws IOException {
public TimeWarpedXPackPlugin(Settings settings) throws IOException, CertificateException, UnrecoverableKeyException,
NoSuchAlgorithmException, KeyStoreException, DestroyFailedException, OperatorCreationException {
super(settings);
watcher = new TimeWarpedWatcher(settings);
}

View File

@ -9,7 +9,6 @@ import com.carrotsearch.randomizedtesting.generators.RandomStrings;
import org.apache.http.client.ClientProtocolException;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;

View File

@ -104,8 +104,6 @@ public class SecurityFeatureSetTests extends ESTestCase {
final boolean httpSSLEnabled = randomBoolean();
settings.put("xpack.security.http.ssl.enabled", httpSSLEnabled);
final boolean transportSSLEnabled = randomBoolean();
settings.put("xpack.security.transport.ssl.enabled", transportSSLEnabled);
final boolean auditingEnabled = randomBoolean();
final String[] auditOutputs = randomFrom(new String[] {"logfile"}, new String[] {"index"}, new String[] {"logfile", "index"});
when(auditTrail.usageStats())
@ -171,9 +169,8 @@ public class SecurityFeatureSetTests extends ESTestCase {
assertThat(source.getValue("realms"), is(notNullValue()));
}
// check SSL
// check http SSL
assertThat(source.getValue("ssl.http.enabled"), is(httpSSLEnabled));
assertThat(source.getValue("ssl.transport.enabled"), is(transportSSLEnabled));
// auditing
assertThat(source.getValue("audit.enabled"), is(auditingEnabled));

View File

@ -58,14 +58,14 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
private static final String SECOND_CLUSTER_NODE_PREFIX = "node_cluster2_";
private static InternalTestCluster cluster2;
private static boolean useSSL;
private static boolean useGeneratedSSL;
private Node tribeNode;
private Client tribeClient;
@BeforeClass
public static void setupSSL() {
useSSL = randomBoolean();
useGeneratedSSL = randomBoolean();
}
@Override
@ -73,7 +73,7 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
super.setUp();
if (cluster2 == null) {
SecuritySettingsSource cluster2SettingsSource =
new SecuritySettingsSource(defaultMaxNumberOfNodes(), useSSL, systemKey(), createTempDir(), Scope.SUITE);
new SecuritySettingsSource(defaultMaxNumberOfNodes(), useGeneratedSSL, systemKey(), createTempDir(), Scope.SUITE);
cluster2 = new InternalTestCluster(randomLong(), createTempDir(), true, true, 1, 2,
UUIDs.randomBase64UUID(random()), cluster2SettingsSource, 0, false, SECOND_CLUSTER_NODE_PREFIX, getMockPlugins(),
getClientWrapper());
@ -82,6 +82,11 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
}
}
@Override
public boolean useGeneratedSSLConfig() {
return useGeneratedSSL;
}
@AfterClass
public static void tearDownSecondCluster() {
if (cluster2 != null) {
@ -123,18 +128,14 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
}
}
@Override
public boolean sslTransportEnabled() {
return useSSL;
}
@Override
protected boolean ignoreExternalCluster() {
return true;
}
private void setupTribeNode(Settings settings) throws NodeValidationException, InterruptedException {
SecuritySettingsSource cluster2SettingsSource = new SecuritySettingsSource(1, useSSL, systemKey(), createTempDir(), Scope.TEST);
SecuritySettingsSource cluster2SettingsSource =
new SecuritySettingsSource(1, useGeneratedSSL, systemKey(), createTempDir(), Scope.TEST);
Map<String,String> asMap = new HashMap<>(cluster2SettingsSource.nodeSettings(0).getAsMap());
asMap.remove(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey());
Settings.Builder tribe1Defaults = Settings.builder();

View File

@ -37,6 +37,7 @@ import org.elasticsearch.transport.MockTcpTransportPlugin;
import org.elasticsearch.transport.TransportInfo;
import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail.Message;
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
@ -56,6 +57,7 @@ import org.junit.BeforeClass;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@ -132,10 +134,11 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
// Setup a second test cluster with randomization for number of nodes, security enabled, and SSL
final int numNodes = randomIntBetween(1, 2);
final boolean useSecurity = randomBoolean();
final boolean useSSL = useSecurity && randomBoolean();
logger.info("--> remote indexing enabled. security enabled: [{}], SSL enabled: [{}], nodes: [{}]", useSecurity, useSSL, numNodes);
final boolean useGeneratedSSL = useSecurity && randomBoolean();
logger.info("--> remote indexing enabled. security enabled: [{}], SSL enabled: [{}], nodes: [{}]", useSecurity, useGeneratedSSL,
numNodes);
SecuritySettingsSource cluster2SettingsSource =
new SecuritySettingsSource(numNodes, useSSL, systemKey(), createTempDir(), Scope.SUITE) {
new SecuritySettingsSource(numNodes, useGeneratedSSL, systemKey(), createTempDir(), Scope.SUITE) {
@Override
public Settings nodeSettings(int nodeOrdinal) {
Settings.Builder builder = Settings.builder()
@ -146,6 +149,21 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
}
return builder.build();
}
@Override
public Settings transportClientSettings() {
if (useSecurity) {
return super.transportClientSettings();
} else {
Settings.Builder builder = Settings.builder()
.put(XPackSettings.SECURITY_ENABLED.getKey(), false)
.put(super.transportClientSettings());
if (builder.get(NetworkModule.TRANSPORT_TYPE_KEY) == null) {
builder.put(NetworkModule.TRANSPORT_TYPE_KEY, MockTcpTransportPlugin.MOCK_TCP_TRANSPORT_NAME);
}
return builder.build();
}
}
};
@ -163,17 +181,19 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
TransportAddress inet = info.address().publishAddress();
Settings.Builder builder = Settings.builder()
.put(XPackSettings.SECURITY_ENABLED.getKey(), useSecurity)
.put("xpack.security.audit.index.client." + XPackSettings.SECURITY_ENABLED.getKey(), useSecurity)
.put(remoteSettings(NetworkAddress.format(inet.address().getAddress()), inet.address().getPort(), cluster2Name))
.put("xpack.security.audit.index.client.xpack.security.user", SecuritySettingsSource.DEFAULT_USER_NAME + ":" +
SecuritySettingsSource.DEFAULT_PASSWORD);
if (useSSL) {
if (useGeneratedSSL == false) {
for (Map.Entry<String, String> entry : cluster2SettingsSource.getClientSSLSettings().getAsMap().entrySet()) {
builder.put("xpack.security.audit.index.client." + entry.getKey(), entry.getValue());
}
} else {
builder.put("xpack.security.audit.index.client.xpack.ssl.client_authentication", "none");
}
if (useSecurity == false && builder.get(NetworkModule.TRANSPORT_TYPE_KEY) == null) {
builder.put("xpack.security.audit.index.client." + NetworkModule.TRANSPORT_TYPE_KEY,
MockTcpTransportPlugin.MOCK_TCP_TRANSPORT_NAME);
}
remoteSettings = builder.build();
}
@ -283,6 +303,11 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
enqueuedMessage.set(message);
super.enqueue(message, type);
}
@Override
List<Class<? extends Plugin>> remoteTransportClientPlugins() {
return Arrays.asList(XPackPlugin.class, MockTcpTransportPlugin.class);
}
};
auditor.start(true);
}

View File

@ -34,7 +34,6 @@ import java.util.stream.StreamSupport;
import static org.elasticsearch.test.InternalTestCluster.clusterName;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout;
import static org.hamcrest.Matchers.is;
/**
* This test checks to ensure that the IndexAuditTrail starts properly when indexing to a remote cluster. The cluster
@ -50,13 +49,13 @@ public class RemoteIndexAuditTrailStartingTests extends SecurityIntegTestCase {
private InternalTestCluster remoteCluster;
private final boolean useSSL = randomBoolean();
private final boolean useGeneratedSSL = randomBoolean();
private final boolean localAudit = randomBoolean();
private final String outputs = randomFrom("index", "logfile", "index,logfile");
@Override
public boolean sslTransportEnabled() {
return useSSL;
public boolean useGeneratedSSLConfig() {
return useGeneratedSSL;
}
@Override
@ -90,7 +89,7 @@ public class RemoteIndexAuditTrailStartingTests extends SecurityIntegTestCase {
// Setup a second test cluster with a single node, security enabled, and SSL
final int numNodes = 1;
SecuritySettingsSource cluster2SettingsSource =
new SecuritySettingsSource(numNodes, useSSL, systemKey(), createTempDir(), Scope.TEST) {
new SecuritySettingsSource(numNodes, useGeneratedSSL, systemKey(), createTempDir(), Scope.TEST) {
@Override
public Settings nodeSettings(int nodeOrdinal) {
Settings.Builder builder = Settings.builder()

View File

@ -48,11 +48,6 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
.build();
}
@Override
public boolean sslTransportEnabled() {
return false;
}
@Override
public String configRoles() {
return ROLES + super.configRoles();
@ -72,6 +67,11 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
+ "transport_client:" + TRANSPORT_CLIENT_USER;
}
@Override
public boolean useGeneratedSSLConfig() {
return true;
}
public void testUserImpersonation() throws Exception {
try (TransportClient client = getTransportClient(Settings.builder()
.put(Security.USER_SETTING.getKey(), TRANSPORT_CLIENT_USER + ":" + SecuritySettingsSource.DEFAULT_PASSWORD).build())) {
@ -222,7 +222,6 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
Settings settings = Settings.builder()
.put(extraSettings)
.put("cluster.name", clusterName)
.put("xpack.security.transport.ssl.enabled", false)
.build();
return new TestXPackTransportClient(settings)

View File

@ -49,8 +49,9 @@ public class ESNativeMigrateToolTests extends NativeRealmIntegTestCase {
}
@Override
protected boolean sslTransportEnabled() {
return useSSL;
protected boolean useGeneratedSSLConfig() {
// don't use autogenerated when we expect a different cert
return useSSL == false;
}
private Environment nodeEnvironment() throws Exception {

View File

@ -55,7 +55,9 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
assertThat(response.getClusterName(), is(cluster().getClusterName()));
}
ChangePasswordResponse response = securityClient().prepareChangePassword(username, newPassword).get();
ChangePasswordResponse response = securityClient()
.prepareChangePassword(username, Arrays.copyOf(newPassword, newPassword.length))
.get();
assertThat(response, notNullValue());
ElasticsearchSecurityException elasticsearchSecurityException = expectThrows(ElasticsearchSecurityException.class, () -> client()

View File

@ -30,7 +30,7 @@ public class AbstractActiveDirectoryIntegTests extends ESTestCase {
@Before
public void initializeSslSocketFactory() throws Exception {
useGlobalSSL = randomBoolean();
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
Path truststore = getDataPath("../ldap/support/ldaptrust.jks");
/*
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
@ -38,19 +38,19 @@ public class AbstractActiveDirectoryIntegTests extends ESTestCase {
*/
Settings.Builder builder = Settings.builder().put("path.home", createTempDir());
if (useGlobalSSL) {
builder.put("xpack.ssl.keystore.path", keystore)
.put("xpack.ssl.keystore.password", "changeit");
builder.put("xpack.ssl.truststore.path", truststore)
.put("xpack.ssl.truststore.password", "changeit");
// fake realm to load config with certificate verification mode
builder.put("xpack.security.authc.realms.bar.ssl.keystore.path", keystore);
builder.put("xpack.security.authc.realms.bar.ssl.keystore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", truststore);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.verification_mode", VerificationMode.CERTIFICATE);
} else {
// fake realms so ssl will get loaded
builder.put("xpack.security.authc.realms.foo.ssl.truststore.path", keystore);
builder.put("xpack.security.authc.realms.foo.ssl.truststore.path", truststore);
builder.put("xpack.security.authc.realms.foo.ssl.truststore.password", "changeit");
builder.put("xpack.security.authc.realms.foo.ssl.verification_mode", VerificationMode.FULL);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", keystore);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", truststore);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.verification_mode", VerificationMode.CERTIFICATE);
}

View File

@ -43,24 +43,24 @@ public abstract class GroupsResolverTestCase extends ESTestCase {
@Before
public void setUpLdapConnection() throws Exception {
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
Path truststore = getDataPath("../ldap/support/ldaptrust.jks");
boolean useGlobalSSL = randomBoolean();
Settings.Builder builder = Settings.builder().put("path.home", createTempDir());
if (useGlobalSSL) {
builder.put("xpack.ssl.keystore.path", keystore)
.put("xpack.ssl.keystore.password", "changeit");
builder.put("xpack.ssl.truststore.path", truststore)
.put("xpack.ssl.truststore.password", "changeit");
// fake realm to load config with certificate verification mode
builder.put("xpack.security.authc.realms.bar.ssl.keystore.path", keystore);
builder.put("xpack.security.authc.realms.bar.ssl.keystore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", truststore);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.verification_mode", VerificationMode.CERTIFICATE);
} else {
// fake realms so ssl will get loaded
builder.put("xpack.security.authc.realms.foo.ssl.keystore.path", keystore);
builder.put("xpack.security.authc.realms.foo.ssl.keystore.password", "changeit");
builder.put("xpack.security.authc.realms.foo.ssl.truststore.path", truststore);
builder.put("xpack.security.authc.realms.foo.ssl.truststore.password", "changeit");
builder.put("xpack.security.authc.realms.foo.ssl.verification_mode", VerificationMode.FULL);
builder.put("xpack.security.authc.realms.bar.ssl.keystore.path", keystore);
builder.put("xpack.security.authc.realms.bar.ssl.keystore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", truststore);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.verification_mode", VerificationMode.CERTIFICATE);
}
Settings settings = builder.build();
@ -78,8 +78,8 @@ public abstract class GroupsResolverTestCase extends ESTestCase {
if (useGlobalSSL) {
connectionSettings = Settings.EMPTY;
} else {
connectionSettings = Settings.builder().put("keystore.path", keystore)
.put("keystore.password", "changeit").build();
connectionSettings = Settings.builder().put("truststore.path", truststore)
.put("truststore.password", "changeit").build();
}
ldapConnection = LdapUtils.privilegedConnect(() -> new LDAPConnection(sslService.sslSocketFactory(connectionSettings), options,
ldapurl.getHost(), ldapurl.getPort(), bindDN(), bindPassword()));

View File

@ -37,7 +37,7 @@ public class LdapSessionFactoryTests extends LdapTestCase {
private SSLService sslService;
@Before
public void setup() {
public void setup() throws Exception {
globalSettings = Settings.builder().put("path.home", createTempDir()).build();
sslService = new SSLService(globalSettings, new Environment(globalSettings));
}

View File

@ -57,8 +57,8 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
*/
globalSettings = Settings.builder()
.put("path.home", createTempDir())
.put("xpack.ssl.keystore.path", keystore)
.put("xpack.ssl.keystore.password", "changeit")
.put("xpack.ssl.truststore.path", keystore)
.put("xpack.ssl.truststore.password", "changeit")
.build();
sslService = new SSLService(globalSettings, env);
}

View File

@ -44,7 +44,7 @@ public class OpenLdapTests extends ESTestCase {
@Before
public void initializeSslSocketFactory() throws Exception {
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
Path truststore = getDataPath("../ldap/support/ldaptrust.jks");
/*
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
@ -53,19 +53,19 @@ public class OpenLdapTests extends ESTestCase {
useGlobalSSL = randomBoolean();
Settings.Builder builder = Settings.builder().put("path.home", createTempDir());
if (useGlobalSSL) {
builder.put("xpack.ssl.keystore.path", keystore)
.put("xpack.ssl.keystore.password", "changeit");
builder.put("xpack.ssl.truststore.path", truststore)
.put("xpack.ssl.truststore.password", "changeit");
// fake realm to load config with certificate verification mode
builder.put("xpack.security.authc.realms.bar.ssl.keystore.path", keystore);
builder.put("xpack.security.authc.realms.bar.ssl.keystore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", truststore);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.verification_mode", VerificationMode.CERTIFICATE);
} else {
// fake realms so ssl will get loaded
builder.put("xpack.security.authc.realms.foo.ssl.truststore.path", keystore);
builder.put("xpack.security.authc.realms.foo.ssl.truststore.path", truststore);
builder.put("xpack.security.authc.realms.foo.ssl.truststore.password", "changeit");
builder.put("xpack.security.authc.realms.foo.ssl.verification_mode", VerificationMode.FULL);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", keystore);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", truststore);
builder.put("xpack.security.authc.realms.bar.ssl.truststore.password", "changeit");
builder.put("xpack.security.authc.realms.bar.ssl.verification_mode", VerificationMode.CERTIFICATE);
}

View File

@ -25,7 +25,7 @@ import static org.hamcrest.Matchers.is;
public class SessionFactoryTests extends ESTestCase {
public void testConnectionFactoryReturnsCorrectLDAPConnectionOptionsWithDefaultSettings() {
public void testConnectionFactoryReturnsCorrectLDAPConnectionOptionsWithDefaultSettings() throws Exception {
final Environment environment = new Environment(Settings.builder().put("path.home", createTempDir()).build());
RealmConfig realmConfig = new RealmConfig("conn settings", Settings.EMPTY, environment.settings(), environment);
LDAPConnectionOptions options = SessionFactory.connectionOptions(realmConfig, new SSLService(environment.settings(), environment),
@ -37,7 +37,7 @@ public class SessionFactoryTests extends ESTestCase {
assertThat(options.getSSLSocketVerifier(), is(instanceOf(HostNameSSLSocketVerifier.class)));
}
public void testConnectionFactoryReturnsCorrectLDAPConnectionOptions() {
public void testConnectionFactoryReturnsCorrectLDAPConnectionOptions() throws Exception {
Settings settings = Settings.builder()
.put(SessionFactory.TIMEOUT_TCP_CONNECTION_SETTING, "10ms")
.put(SessionFactory.HOSTNAME_VERIFICATION_SETTING, "false")

View File

@ -69,8 +69,8 @@ public class PkiAuthenticationTests extends SecurityIntegTestCase {
}
@Override
protected boolean sslTransportEnabled() {
return true;
protected boolean useGeneratedSSLConfig() {
return false;
}
public void testTransportClientCanAuthenticateViaPki() {

View File

@ -10,15 +10,10 @@ import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.TestXPackTransportClient;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.ssl.SSLClientAuth;
@ -27,15 +22,11 @@ import org.junit.BeforeClass;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.InputStream;
import java.net.InetAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.SecureRandom;
import static org.elasticsearch.test.SecuritySettingsSource.DEFAULT_PASSWORD;
import static org.elasticsearch.test.SecuritySettingsSource.DEFAULT_USER_NAME;
import static org.elasticsearch.test.SecuritySettingsSource.getSSLSettingsForStore;
import static org.hamcrest.Matchers.is;
public class PkiOptionalClientAuthTests extends SecurityIntegTestCase {
@ -71,8 +62,8 @@ public class PkiOptionalClientAuthTests extends SecurityIntegTestCase {
}
@Override
protected boolean sslTransportEnabled() {
return true;
protected boolean useGeneratedSSLConfig() {
return false;
}
public void testRestClientWithoutClientCertificate() throws Exception {
@ -93,26 +84,6 @@ public class PkiOptionalClientAuthTests extends SecurityIntegTestCase {
}
}
public void testTransportClientWithoutClientCertificate() {
Transport transport = internalCluster().getDataNodeInstance(Transport.class);
int port = randomFrom(transport.profileBoundAddresses().get("want_client_auth").boundAddresses()).address().getPort();
Settings sslSettingsForStore = getSSLSettingsForStore
("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks", "truststore-testnode-only");
Settings settings = Settings.builder()
.put(sslSettingsForStore)
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD)
.put("cluster.name", internalCluster().getClusterName())
.put("xpack.ssl.client_authentication", SSLClientAuth.REQUIRED)
.build();
try (TransportClient client = new TestXPackTransportClient(settings)) {
client.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), port));
assertGreenClusterState(client);
}
}
private SSLContext getSSLContext() throws Exception {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
Path truststore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks");

View File

@ -44,13 +44,12 @@ public class PkiRealmTests extends ESTestCase {
private SSLService sslService;
@Before
public void setup() {
public void setup() throws Exception {
Path testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks");
globalSettings = Settings.builder()
.put("path.home", createTempDir())
.put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.password", "testnode")
.put("xpack.security.transport.ssl.enabled", true)
.build();
sslService = new SSLService(globalSettings, new Environment(globalSettings));
}
@ -217,7 +216,7 @@ public class PkiRealmTests extends ESTestCase {
assertThat(e.getMessage(), containsString("has SSL with client authentication enabled"));
}
public void testHttpClientAuthOnly() {
public void testHttpClientAuthOnly() throws Exception {
Settings settings = Settings.builder()
.put(globalSettings)
.put("xpack.ssl.client_authentication", "none")
@ -228,18 +227,6 @@ public class PkiRealmTests extends ESTestCase {
new SSLService(settings, new Environment(settings)));
}
public void testNoSSLThrowsException() throws Exception {
Settings settings = Settings.builder()
.put(globalSettings)
.put("xpack.security.transport.ssl.enabled", false)
.build();
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> new PkiRealm(new RealmConfig("", Settings.EMPTY, settings), mock(DnRoleMapper.class),
new SSLService(settings, new Environment(settings))));
assertThat(e.getMessage(), containsString("has SSL with client authentication enabled"));
}
static X509Certificate readCert(Path path) throws Exception {
try (InputStream in = Files.newInputStream(path)) {
CertificateFactory factory = CertificateFactory.getInstance("X.509");

View File

@ -42,9 +42,10 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase
randomClientPort = randomIntBetween(49000, 65500); // ephemeral port
}
// don't use it here to simplify the settings we need
@Override
protected boolean sslTransportEnabled() {
return true;
public boolean useGeneratedSSLConfig() {
return false;
}
@Override
@ -60,11 +61,9 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase
throw new RuntimeException(e);
}
if (sslTransportEnabled()) {
settingsBuilder.put("transport.profiles.client.xpack.security.truststore.path", store) // settings for client truststore
.put("transport.profiles.client.xpack.security.truststore.password", "testnode")
.put("xpack.ssl.client_authentication", SSLClientAuth.REQUIRED);
}
settingsBuilder.put("transport.profiles.client.xpack.security.truststore.path", store) // settings for client truststore
.put("transport.profiles.client.xpack.security.truststore.password", "testnode")
.put("xpack.ssl.client_authentication", SSLClientAuth.REQUIRED);
return settingsBuilder
.put(super.nodeSettings(nodeOrdinal))
@ -94,7 +93,6 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase
.put("network.host", "localhost")
.put("cluster.name", internalCluster().getClusterName())
.put("discovery.zen.ping.unicast.hosts", unicastHost)
.put("xpack.security.transport.ssl.enabled", sslTransportEnabled())
.put("xpack.security.audit.enabled", false)
.put("path.home", home)
.put(NetworkModule.HTTP_ENABLED.getKey(), false)
@ -123,7 +121,6 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase
.put(Security.USER_SETTING.getKey(), "test_user:changeme")
.put("cluster.name", internalCluster().getClusterName())
.put("discovery.zen.ping.unicast.hosts", "localhost:" + randomClientPort)
.put("xpack.security.transport.ssl.enabled", sslTransportEnabled())
.put("xpack.security.audit.enabled", false)
.put(NetworkModule.HTTP_ENABLED.getKey(), false)
.put("discovery.initial_state_timeout", "0s")

View File

@ -88,8 +88,8 @@ public class DNSOnlyHostnameVerificationTests extends SecurityIntegTestCase {
}
@Override
public boolean sslTransportEnabled() {
return true;
public boolean useGeneratedSSLConfig() {
return false;
}
@Override
@ -97,7 +97,6 @@ public class DNSOnlyHostnameVerificationTests extends SecurityIntegTestCase {
Settings defaultSettings = super.nodeSettings(nodeOrdinal);
Settings.Builder builder = Settings.builder()
.put(defaultSettings.filter((s) -> s.startsWith("xpack.ssl.") == false))
.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), true)
.put("transport.host", hostName);
String confPath = Environment.PATH_CONF_SETTING.get(defaultSettings);
Path path = PathUtils.get(confPath).resolve("keystore.jks");
@ -126,8 +125,7 @@ public class DNSOnlyHostnameVerificationTests extends SecurityIntegTestCase {
public Settings transportClientSettings() {
Settings defaultSettings = super.transportClientSettings();
Settings.Builder builder = Settings.builder()
.put(defaultSettings.filter((s) -> s.startsWith("xpack.ssl.") == false))
.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), true);
.put(defaultSettings.filter((s) -> s.startsWith("xpack.ssl.") == false));
Path path = createTempDir().resolve("keystore.jks");
try (OutputStream os = Files.newOutputStream(path)) {
keystore.store(os, "changeme".toCharArray());

View File

@ -22,19 +22,15 @@ public class IPHostnameVerificationTests extends SecurityIntegTestCase {
Path keystore;
@Override
protected boolean sslTransportEnabled() {
return true;
protected boolean useGeneratedSSLConfig() {
return false;
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
Settings settings = super.nodeSettings(nodeOrdinal);
Settings.Builder builder = Settings.builder();
for (Entry<String, String> entry : settings.getAsMap().entrySet()) {
if (entry.getKey().startsWith("xpack.ssl.") == false) {
builder.put(entry.getKey(), entry.getValue());
}
}
Settings.Builder builder = Settings.builder()
.put(settings.filter((s) -> s.startsWith("xpack.ssl.") == false));
settings = builder.build();
// The default Unicast test behavior is to use 'localhost' with the port number. For this test we need to use IP

View File

@ -17,7 +17,6 @@ import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.netty4.Netty4MockUtil;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.ssl.SSLClientAuth;
import org.elasticsearch.xpack.ssl.SSLService;
import org.junit.Before;
@ -26,10 +25,8 @@ import javax.net.ssl.SSLEngine;
import java.nio.file.Path;
import java.util.Locale;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Mockito.mock;
public class SecurityNetty4TransportTests extends ESTestCase {
@ -49,14 +46,13 @@ public class SecurityNetty4TransportTests extends ESTestCase {
sslService = new SSLService(settings, env);
}
private SecurityNetty4Transport createTransport(boolean sslEnabled) {
return createTransport(sslEnabled, Settings.EMPTY);
private SecurityNetty4Transport createTransport() {
return createTransport(Settings.EMPTY);
}
private SecurityNetty4Transport createTransport(boolean sslEnabled, Settings additionalSettings) {
private SecurityNetty4Transport createTransport(Settings additionalSettings) {
final Settings settings =
Settings.builder()
.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), sslEnabled)
.put(additionalSettings)
.build();
return new SecurityNetty4Transport(
@ -70,26 +66,8 @@ public class SecurityNetty4TransportTests extends ESTestCase {
sslService);
}
public void testThatSSLCanBeDisabledByProfile() throws Exception {
SecurityNetty4Transport transport = createTransport(true);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client",
Settings.builder().put("xpack.security.ssl.enabled", false).build());
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class), nullValue());
}
public void testThatSSLCanBeEnabledByProfile() throws Exception {
SecurityNetty4Transport transport = createTransport(false);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client",
Settings.builder().put("xpack.security.ssl.enabled", true).build());
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class), notNullValue());
}
public void testThatProfileTakesDefaultSSLSetting() throws Exception {
SecurityNetty4Transport transport = createTransport(true);
SecurityNetty4Transport transport = createTransport();
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client", Settings.EMPTY);
final EmbeddedChannel ch = new EmbeddedChannel(handler);
@ -97,7 +75,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
}
public void testDefaultClientAuth() throws Exception {
SecurityNetty4Transport transport = createTransport(true);
SecurityNetty4Transport transport = createTransport();
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client", Settings.EMPTY);
final EmbeddedChannel ch = new EmbeddedChannel(handler);
@ -112,7 +90,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
.put("xpack.ssl.client_authentication", value)
.build();
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(true, settings);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client", Settings.EMPTY);
final EmbeddedChannel ch = new EmbeddedChannel(handler);
@ -127,7 +105,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
.put("xpack.ssl.client_authentication", value)
.build();
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(true, settings);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client", Settings.EMPTY);
final EmbeddedChannel ch = new EmbeddedChannel(handler);
@ -142,7 +120,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
.put("xpack.ssl.client_authentication", value)
.build();
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(true, settings);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client", Settings.EMPTY);
final EmbeddedChannel ch = new EmbeddedChannel(handler);
@ -154,11 +132,10 @@ public class SecurityNetty4TransportTests extends ESTestCase {
String value = randomFrom(SSLClientAuth.REQUIRED.name(), SSLClientAuth.REQUIRED.name().toLowerCase(Locale.ROOT));
Settings settings = Settings.builder()
.put(env.settings())
.put("xpack.security.transport.ssl.enabled", true)
.put("transport.profiles.client.xpack.security.ssl.client_authentication", value)
.build();
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(true, settings);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client",
Settings.builder().put("xpack.security.ssl.client_authentication", value).build());
@ -171,11 +148,10 @@ public class SecurityNetty4TransportTests extends ESTestCase {
String value = randomFrom(SSLClientAuth.NONE.name(), SSLClientAuth.NONE.name().toLowerCase(Locale.ROOT));
Settings settings = Settings.builder()
.put(env.settings())
.put("xpack.security.transport.ssl.enabled", true)
.put("transport.profiles.client.xpack.security.ssl.client_authentication", value)
.build();
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(true, settings);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client",
Settings.builder().put("xpack.security.ssl.client_authentication", value).build());
@ -188,11 +164,10 @@ public class SecurityNetty4TransportTests extends ESTestCase {
String value = randomFrom(SSLClientAuth.OPTIONAL.name(), SSLClientAuth.OPTIONAL.name().toLowerCase(Locale.ROOT));
Settings settings = Settings.builder()
.put(env.settings())
.put("xpack.security.transport.ssl.enabled", true)
.put("transport.profiles.client.xpack.security.ssl.client_authentication", value)
.build();
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(true, settings);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
final ChannelHandler handler = transport.getServerChannelInitializer("client",
Settings.builder().put("xpack.security.ssl.client_authentication", value).build());
@ -201,56 +176,20 @@ public class SecurityNetty4TransportTests extends ESTestCase {
assertThat(ch.pipeline().get(SslHandler.class).engine().getWantClientAuth(), is(true));
}
public void testThatExceptionIsThrownWhenConfiguredWithoutSslKey() throws Exception {
Settings settings = Settings.builder()
.put("xpack.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.ssl.truststore.password", "testnode")
.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), true)
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = new SecurityNetty4Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), mock(NamedWriteableRegistry.class), mock(CircuitBreakerService.class), null, sslService);
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> transport.getServerChannelInitializer(randomAsciiOfLength(6), Settings.EMPTY));
assertThat(e.getMessage(), containsString("key must be provided"));
}
public void testNoExceptionWhenConfiguredWithoutSslKeySSLDisabled() throws Exception {
Settings settings = Settings.builder()
.put("xpack.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.ssl.truststore.password", "testnode")
.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), false)
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = new SecurityNetty4Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), mock(NamedWriteableRegistry.class), mock(CircuitBreakerService.class), null, sslService);
assertNotNull(transport.getServerChannelInitializer(randomAsciiOfLength(6), Settings.EMPTY));
}
public void testTransportSSLOverridesGlobalSSL() throws Exception {
final boolean useGlobalKeystoreWithoutKey = randomBoolean();
Settings.Builder builder = Settings.builder()
.put("xpack.security.transport.ssl.keystore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.transport.ssl.keystore.password", "testnode")
.put("xpack.security.transport.ssl.client_authentication", "none")
.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), true)
.put("xpack.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
.put("xpack.ssl.truststore.password", "truststore-testnode-only")
.put("path.home", createTempDir());
if (useGlobalKeystoreWithoutKey) {
builder.put("xpack.ssl.keystore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
.put("xpack.ssl.keystore.password", "truststore-testnode-only");
}
Settings settings = builder.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(true, settings);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
final ChannelHandler handler = transport.getServerChannelInitializer("default", Settings.EMPTY);
final EmbeddedChannel ch = new EmbeddedChannel(handler);

View File

@ -26,8 +26,8 @@ import static org.hamcrest.Matchers.containsString;
public class SslHostnameVerificationTests extends SecurityIntegTestCase {
@Override
protected boolean sslTransportEnabled() {
return true;
protected boolean useGeneratedSSLConfig() {
return false;
}
@Override

View File

@ -52,8 +52,8 @@ public class SslIntegrationTests extends SecurityIntegTestCase {
}
@Override
protected boolean sslTransportEnabled() {
return true;
protected boolean useGeneratedSSLConfig() {
return false;
}
// no SSL exception as this is the exception is returned when connecting

View File

@ -30,13 +30,11 @@ import static org.hamcrest.Matchers.containsString;
public class SslMultiPortTests extends SecurityIntegTestCase {
private static int randomClientPort;
private static int randomNonSslPort;
private static int randomNoClientAuthPort;
@BeforeClass
public static void getRandomPort() {
randomClientPort = randomIntBetween(49000, 65500); // ephemeral port
randomNonSslPort = randomIntBetween(49000, 65500);
randomNoClientAuthPort = randomIntBetween(49000, 65500);
}
@ -46,13 +44,11 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
* <li>default: testnode keystore. Requires client auth</li>
* <li>client: testnode-client-profile keystore that only trusts the testclient cert. Requires client auth</li>
* <li>no_client_auth: testnode keystore. Does not require client auth</li>
* <li>no_ssl: plaintext transport profile</li>
* </ul>
*/
@Override
protected Settings nodeSettings(int nodeOrdinal) {
String randomClientPortRange = randomClientPort + "-" + (randomClientPort+100);
String randomNonSslPortRange = randomNonSslPort + "-" + (randomNonSslPort+100);
String randomNoClientAuthPortRange = randomNoClientAuthPort + "-" + (randomNoClientAuthPort+100);
Path store;
@ -71,9 +67,6 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
.put("transport.profiles.client.bind_host", "localhost")
.put("transport.profiles.client.xpack.security.ssl.truststore.path", store.toAbsolutePath())
.put("transport.profiles.client.xpack.security.ssl.truststore.password", "testnode-client-profile")
.put("transport.profiles.no_ssl.port", randomNonSslPortRange)
.put("transport.profiles.no_ssl.bind_host", "localhost")
.put("transport.profiles.no_ssl.xpack.security.ssl.enabled", "false")
.put("transport.profiles.no_client_auth.port", randomNoClientAuthPortRange)
.put("transport.profiles.no_client_auth.bind_host", "localhost")
.put("transport.profiles.no_client_auth.xpack.security.ssl.client_authentication", SSLClientAuth.NONE)
@ -81,23 +74,13 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
}
@Override
protected boolean sslTransportEnabled() {
return true;
protected boolean useGeneratedSSLConfig() {
return false;
}
private TransportClient createTransportClient(Settings additionalSettings) {
Settings clientSettings = transportClientSettings();
if (additionalSettings.getByPrefix("xpack.ssl.").isEmpty() == false) {
Settings.Builder builder = Settings.builder();
for (Entry<String, String> entry : clientSettings.getAsMap().entrySet()) {
if (entry.getKey().startsWith("xpack.ssl.") == false) {
builder.put(entry.getKey(), entry.getValue());
}
}
clientSettings = builder.build();
}
Settings settings = Settings.builder().put(clientSettings)
Settings settings = Settings.builder()
.put(transportClientSettings().filter(s -> s.startsWith("xpack.ssl") == false))
.put("node.name", "programmatic_transport_client")
.put("cluster.name", internalCluster().getClusterName())
.put(additionalSettings)
@ -121,7 +104,11 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
* disabling the client auth requirement
*/
public void testThatStandardTransportClientCanConnectToNoClientAuthProfile() throws Exception {
try(TransportClient transportClient = createTransportClient(Settings.EMPTY)) {
try(TransportClient transportClient = new TestXPackTransportClient(Settings.builder()
.put(transportClientSettings())
.put("node.name", "programmatic_transport_client")
.put("cluster.name", internalCluster().getClusterName())
.build())) {
transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(),
getProfilePort("no_client_auth")));
assertGreenClusterState(transportClient);
@ -145,21 +132,6 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
}
}
/**
* Uses a transport client with the same settings as the internal cluster transport client to test connection to the
* no_ssl profile. The internal transport client is not used here since we are connecting to a different
* profile. The no_ssl profile is plain text and the standard transport client uses SSL, so a connection will never work
*/
public void testThatStandardTransportClientCannotConnectToNoSslProfile() throws Exception {
try (TransportClient transportClient = createTransportClient(Settings.EMPTY)) {
transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("no_ssl")));
assertGreenClusterState(transportClient);
fail("Expected NoNodeAvailableException");
} catch (NoNodeAvailableException e) {
assertThat(e.getMessage(), containsString("None of the configured nodes are available: [{#transport#-"));
}
}
/**
* Uses a transport client with a custom keystore; this keystore testclient-client-profile.jks trusts the testnode
* certificate and had its own self signed certificate. This test connects to the client profile, which is only
@ -209,38 +181,6 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
}
}
/**
* Uses a transport client with a custom keystore; this keystore testclient-client-profile.jks trusts the testnode
* certificate and had its own self signed certificate. This test connects to the no_ssl profile, which does not
* use SSL so the connection will never work
*/
public void testThatProfileTransportClientCannotConnectToNoSslProfile() throws Exception {
Settings settings = getSSLSettingsForStore(
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient-client-profile.jks", "testclient-client-profile");
try (TransportClient transportClient = createTransportClient(settings)) {
transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("no_ssl")));
transportClient.admin().cluster().prepareHealth().get();
fail("Expected NoNodeAvailableException");
} catch (NoNodeAvailableException e) {
assertThat(e.getMessage(), containsString("None of the configured nodes are available: [{#transport#-"));
}
}
/**
* Uses a transport client with SSL disabled. This test connects to the no_ssl profile, which should always succeed
*/
public void testThatTransportClientCanConnectToNoSslProfile() throws Exception {
Settings settings = Settings.builder()
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD)
.put("xpack.security.transport.ssl.enabled", false)
.put("cluster.name", internalCluster().getClusterName())
.build();
try (TransportClient transportClient = new TestXPackTransportClient(settings)) {
transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("no_ssl")));
assertGreenClusterState(transportClient);
}
}
/**
* Uses a transport client with SSL disabled. This test connects to the default profile, which should always fail
* as a non-ssl transport client cannot connect to a ssl profile
@ -305,7 +245,6 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
Settings settings = Settings.builder()
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD)
.put("cluster.name", internalCluster().getClusterName())
.put("xpack.security.transport.ssl.enabled", true)
.put("xpack.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
.put("xpack.ssl.truststore.password", "truststore-testnode-only")
@ -365,29 +304,6 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
}
}
/**
* Uses a transport client with a custom truststore; this truststore truststore-testnode-only only trusts the testnode
* certificate and contains no other certification. This test connects to the no_ssl profile, which does not use
* SSL so the connection should never succeed
*/
public void testThatTransportClientWithOnlyTruststoreCannotConnectToNoSslProfile() throws Exception {
Settings settings = Settings.builder()
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD)
.put("cluster.name", internalCluster().getClusterName())
.put("xpack.security.transport.ssl.enabled", true)
.put("xpack.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
.put("xpack.ssl.truststore.password", "truststore-testnode-only")
.build();
try (TransportClient transportClient = new TestXPackTransportClient(settings)) {
transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("no_ssl")));
assertGreenClusterState(transportClient);
fail("Expected NoNodeAvailableException");
} catch (NoNodeAvailableException e) {
assertThat(e.getMessage(), containsString("None of the configured nodes are available: [{#transport#-"));
}
}
/**
* Uses a transport client with the default JDK truststore; this truststore only trusts the known good public
* certificate authorities. This test connects to the default profile, which uses a self-signed certificate that

View File

@ -0,0 +1,49 @@
/*
* 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.security.transport.ssl;
import org.elasticsearch.action.DocWriteResponse.Result;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.SecurityIntegTestCase;
/**
* An extremely simple test that shows SSL will work with a cipher that does not perform encryption
*/
public class SslNullCipherTests extends SecurityIntegTestCase {
@Override
public boolean useGeneratedSSLConfig() {
return true;
}
@Override
public Settings nodeSettings(int nodeOrdinal) {
Settings settings = super.nodeSettings(nodeOrdinal);
Settings.Builder builder = Settings.builder()
.put(settings.filter((s) -> s.startsWith("xpack.ssl") == false));
builder.put("xpack.security.transport.ssl.cipher_suites", "TLS_RSA_WITH_NULL_SHA256");
return builder.build();
}
@Override
public Settings transportClientSettings() {
Settings settings = super.transportClientSettings();
Settings.Builder builder = Settings.builder()
.put(settings.filter((s) -> s.startsWith("xpack.ssl") == false));
builder.put("xpack.security.transport.ssl.cipher_suites", "TLS_RSA_WITH_NULL_SHA256");
return builder.build();
}
public void testClusterIsFormed() {
ensureGreen();
Client client = internalCluster().transportClient();
IndexResponse response = client.prepareIndex("index", "type").setSource("foo", "bar").get();
assertEquals(Result.CREATED, response.getResult());
}
}

View File

@ -0,0 +1,45 @@
/*
* 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 org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.Node;
import org.elasticsearch.test.ESTestCase;
import javax.net.ssl.X509ExtendedKeyManager;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
public class GeneratedKeyConfigTests extends ESTestCase {
public void testGenerating() throws Exception {
Settings settings = Settings.builder().put(Node.NODE_NAME_SETTING.getKey(), randomAsciiOfLengthBetween(1, 8)).build();
GeneratedKeyConfig keyConfig = new GeneratedKeyConfig(settings);
assertThat(keyConfig.filesToMonitor(null), is(empty()));
X509ExtendedKeyManager keyManager = keyConfig.createKeyManager(null);
assertNotNull(keyManager);
assertNotNull(keyConfig.createTrustManager(null));
String[] aliases = keyManager.getServerAliases("RSA", null);
assertEquals(1, aliases.length);
PrivateKey privateKey = keyManager.getPrivateKey(aliases[0]);
assertNotNull(privateKey);
assertThat(privateKey, instanceOf(RSAPrivateKey.class));
X509Certificate[] certificates = keyManager.getCertificateChain(aliases[0]);
assertEquals(2, certificates.length);
assertEquals(GeneratedKeyConfig.readCACert(), certificates[1]);
X509Certificate generatedCertificate = certificates[0];
assertEquals("CN=" + Node.NODE_NAME_SETTING.get(settings), generatedCertificate.getSubjectX500Principal().getName());
assertEquals(certificates[1].getSubjectX500Principal(), generatedCertificate.getIssuerX500Principal());
}
}

View File

@ -0,0 +1,85 @@
/*
* 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 org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase;
public class SSLBootstrapCheckTests extends ESTestCase {
public void testSSLBootstrapCheckWithNoKey() throws Exception {
SSLService sslService = new SSLService(Settings.EMPTY, null);
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(sslService, Settings.EMPTY, null);
assertTrue(bootstrapCheck.check());
}
public void testSSLBootstrapCheckWithKey() throws Exception {
final String keyPrefix = randomBoolean() ? "security.transport." : "";
Settings settings = Settings.builder()
.put("path.home", createTempDir())
.put("xpack." + keyPrefix + "ssl.key",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.pem"))
.put("xpack." + keyPrefix + "ssl.key_passphrase", "testclient")
.put("xpack." + keyPrefix + "ssl.certificate",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"))
.build();
final Environment env = randomBoolean() ? new Environment(settings) : null;
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), settings, env);
assertFalse(bootstrapCheck.check());
}
public void testSSLBootstrapCheckWithDefaultCABeingTrusted() throws Exception {
final String keyPrefix = randomBoolean() ? "security.transport." : "";
Settings settings = Settings.builder()
.put("path.home", createTempDir())
.put("xpack." + keyPrefix + "ssl.key",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.pem"))
.put("xpack." + keyPrefix + "ssl.key_passphrase", "testclient")
.put("xpack." + keyPrefix + "ssl.certificate",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"))
.putArray("xpack." + keyPrefix + "ssl.certificate_authorities",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt").toString(),
getDataPath("/org/elasticsearch/xpack/ssl/ca.pem").toString())
.build();
final Environment env = randomBoolean() ? new Environment(settings) : null;
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), settings, env);
assertTrue(bootstrapCheck.check());
settings = Settings.builder().put(settings.filter((s) -> s.contains(".certificate_authorities")))
.put("xpack.security.http.ssl.certificate_authorities",
getDataPath("/org/elasticsearch/xpack/ssl/ca.pem").toString())
.build();
bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), settings, env);
assertTrue(bootstrapCheck.check());
}
public void testSSLBootstrapCheckWithDefaultKeyBeingUsed() throws Exception {
final String keyPrefix = randomBoolean() ? "security.transport." : "";
Settings settings = Settings.builder()
.put("path.home", createTempDir())
.put("xpack." + keyPrefix + "ssl.key",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.pem"))
.put("xpack." + keyPrefix + "ssl.key_passphrase", "testclient")
.put("xpack." + keyPrefix + "ssl.certificate",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"))
.put("xpack.security.http.ssl.key", getDataPath("/org/elasticsearch/xpack/ssl/private.pem").toString())
.put("xpack.security.http.ssl.certificate", getDataPath("/org/elasticsearch/xpack/ssl/ca.pem").toString())
.build();
final Environment env = randomBoolean() ? new Environment(settings) : null;
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), settings, env);
assertTrue(bootstrapCheck.check());
settings = Settings.builder().put(settings.filter((s) -> s.contains(".http.ssl.")))
.put("xpack.security.transport.profiles.foo.xpack.security.ssl.key",
getDataPath("/org/elasticsearch/xpack/ssl/private.pem").toString())
.put("xpack.security.transport.profiles.foo.xpack.security.ssl.certificate",
getDataPath("/org/elasticsearch/xpack/ssl/ca.pem").toString())
.build();
bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), settings, env);
assertTrue(bootstrapCheck.check());
}
}

View File

@ -55,8 +55,8 @@ public class SSLClientAuthTests extends SecurityIntegTestCase {
}
@Override
protected boolean sslTransportEnabled() {
return true;
protected boolean useGeneratedSSLConfig() {
return false;
}
public void testThatHttpFailsWithoutSslClientAuth() throws IOException {
@ -90,7 +90,6 @@ public class SSLClientAuthTests extends SecurityIntegTestCase {
}
Settings settings = Settings.builder()
.put("xpack.security.transport.ssl.enabled", true)
.put("xpack.ssl.client_authentication", SSLClientAuth.NONE)
.put("xpack.ssl.keystore.path", store)
.put("xpack.ssl.keystore.password", "testclient-client-profile")

View File

@ -19,6 +19,7 @@ import org.junit.Before;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
@ -34,8 +35,9 @@ import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.function.BiFunction;
import java.util.function.BiConsumer;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
@ -81,21 +83,19 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
.build();
final Environment env = randomBoolean() ? null : new Environment(settings);
final BiFunction<X509ExtendedKeyManager, SSLConfiguration, Void> keyManagerPreChecks = (keyManager, config) -> {
final BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPreChecks = (keyManager, config) -> {
// key manager checks
String[] aliases = keyManager.getServerAliases("RSA", null);
assertNotNull(aliases);
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("testnode"));
return null;
};
final SetOnce<Integer> trustedCount = new SetOnce<>();
final BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPreChecks = (trustManager, config) -> {
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPreChecks = (trustManager, config) -> {
// trust manager checks
Certificate[] certificates = trustManager.getAcceptedIssuers();
trustedCount.set(certificates.length);
return null;
};
final Runnable modifier = () -> {
@ -103,6 +103,10 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
// modify it
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(null, null);
final KeyPair keyPair = CertUtils.generateKeyPair(512);
X509Certificate cert = CertUtils.generateSignedCertificate(new X500Principal("CN=testReloadingKeyStore"), null, keyPair,
null, null, 365);
keyStore.setKeyEntry("key", keyPair.getPrivate(), "testnode".toCharArray(), new X509Certificate[] { cert });
Path updated = tempDir.resolve("updated.jks");
try (OutputStream out = Files.newOutputStream(updated)) {
keyStore.store(out, "testnode".toCharArray());
@ -113,11 +117,16 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
}
};
final BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPostChecks = (updatedTrustManager, config) -> {
assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(5));
return null;
final BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPostChecks = (updatedKeyManager, config) -> {
String[] aliases = updatedKeyManager.getServerAliases("RSA", null);
assertNotNull(aliases);
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("key"));
};
validateSSLConfigurationIsReloaded(settings, env, keyManagerPreChecks, trustManagerPreChecks, modifier, (k, c) -> null,
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks = (updatedTrustManager, config) -> {
assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(4));
};
validateSSLConfigurationIsReloaded(settings, env, keyManagerPreChecks, trustManagerPreChecks, modifier, keyManagerPostChecks,
trustManagerPostChecks);
}
@ -144,14 +153,13 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
new Environment(Settings.builder().put("path.home", createTempDir()).build());
final SetOnce<PrivateKey> privateKey = new SetOnce<>();
final BiFunction<X509ExtendedKeyManager, SSLConfiguration, Void> keyManagerPreChecks = (keyManager, config) -> {
final BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPreChecks = (keyManager, config) -> {
String[] aliases = keyManager.getServerAliases("RSA", null);
assertNotNull(aliases);
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("key"));
privateKey.set(keyManager.getPrivateKey("key"));
assertNotNull(privateKey.get());
return null;
};
final KeyPair keyPair = CertUtils.generateKeyPair(randomFrom(1024, 2048));
@ -180,14 +188,13 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
}
};
final BiFunction<X509ExtendedKeyManager, SSLConfiguration, Void> keyManagerPostChecks = (keyManager, config) -> {
final BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPostChecks = (keyManager, config) -> {
String[] aliases = keyManager.getServerAliases("RSA", null);
assertNotNull(aliases);
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("key"));
assertThat(keyManager.getPrivateKey(aliases[0]), not(equalTo(privateKey)));
assertThat(keyManager.getPrivateKey(aliases[0]), is(equalTo(keyPair.getPrivate())));
return null;
};
validateKeyConfigurationIsReloaded(settings, env, keyManagerPreChecks, modifier, keyManagerPostChecks);
}
@ -205,13 +212,15 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Environment env = randomBoolean() ? null : new Environment(settings);
final X500Principal expectedPrincipal = new X500Principal("CN=xpack public development ca");
final SetOnce<Integer> trustedCount = new SetOnce<>();
final BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPreChecks = (trustManager, config) -> {
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPreChecks = (trustManager, config) -> {
// trust manager checks
Certificate[] certificates = trustManager.getAcceptedIssuers();
trustedCount.set(certificates.length);
return null;
assertTrue(Arrays.stream(trustManager.getAcceptedIssuers())
.anyMatch((cert) -> expectedPrincipal.equals(cert.getSubjectX500Principal())));
};
@ -229,9 +238,10 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
}
};
final BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPostChecks = (updatedTrustManager, config) -> {
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks = (updatedTrustManager, config) -> {
assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(5));
return null;
assertTrue(Arrays.stream(updatedTrustManager.getAcceptedIssuers())
.anyMatch((cert) -> expectedPrincipal.equals(cert.getSubjectX500Principal())));
};
validateTrustConfigurationIsReloaded(settings, env, trustManagerPreChecks, modifier, trustManagerPostChecks);
@ -250,13 +260,15 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Environment env = randomBoolean() ? null : new Environment(settings);
final X500Principal expectedPrincipal = new X500Principal("CN=xpack public development ca");
final BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPreChecks = (trustManager, config) -> {
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPreChecks = (trustManager, config) -> {
// trust manager checks
Certificate[] certificates = trustManager.getAcceptedIssuers();
assertThat(certificates.length, is(1));
assertThat(certificates.length, is(2));
assertThat(((X509Certificate)certificates[0]).getSubjectX500Principal().getName(), containsString("Test Client"));
return null;
assertTrue(Arrays.stream(trustManager.getAcceptedIssuers())
.anyMatch((cert) -> expectedPrincipal.equals(cert.getSubjectX500Principal())));
};
final Runnable modifier = () -> {
@ -270,11 +282,12 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
}
};
final BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPostChecks = (updatedTrustManager, config) -> {
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks = (updatedTrustManager, config) -> {
Certificate[] updatedCerts = updatedTrustManager.getAcceptedIssuers();
assertThat(updatedCerts.length, is(1));
assertThat(updatedCerts.length, is(2));
assertThat(((X509Certificate)updatedCerts[0]).getSubjectX500Principal().getName(), containsString("Test Node"));
return null;
assertTrue(Arrays.stream(updatedTrustManager.getAcceptedIssuers())
.anyMatch((cert) -> expectedPrincipal.equals(cert.getSubjectX500Principal())));
};
validateTrustConfigurationIsReloaded(settings, env, trustManagerPreChecks, modifier, trustManagerPostChecks);
@ -425,9 +438,9 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
* Validates the trust configuration aspect of the SSLConfiguration is reloaded
*/
private void validateTrustConfigurationIsReloaded(Settings settings, Environment env,
BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPreChecks,
BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPreChecks,
Runnable modificationFunction,
BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPostChecks)
BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks)
throws Exception {
validateSSLConfigurationIsReloaded(settings, env, false, true, null, trustManagerPreChecks, modificationFunction, null,
trustManagerPostChecks);
@ -437,10 +450,10 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
* Validates the trust configuration aspect of the SSLConfiguration is reloaded
*/
private void validateKeyConfigurationIsReloaded(Settings settings, Environment env,
BiFunction<X509ExtendedKeyManager, SSLConfiguration, Void> keyManagerPreChecks,
Runnable modificationFunction,
BiFunction<X509ExtendedKeyManager, SSLConfiguration, Void> keyManagerPostChecks)
throws Exception {
BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPreChecks,
Runnable modificationFunction,
BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPostChecks)
throws Exception {
validateSSLConfigurationIsReloaded(settings, env, true, false, keyManagerPreChecks, null, modificationFunction,
keyManagerPostChecks, null);
}
@ -449,22 +462,22 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
* Validates that both the key and trust configuration aspects of the SSLConfiguration are reloaded
*/
private void validateSSLConfigurationIsReloaded(Settings settings, Environment env,
BiFunction<X509ExtendedKeyManager, SSLConfiguration, Void> keyManagerPreChecks,
BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPreChecks,
BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPreChecks,
BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPreChecks,
Runnable modificationFunction,
BiFunction<X509ExtendedKeyManager, SSLConfiguration, Void> keyManagerPostChecks,
BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPostChecks)
BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPostChecks,
BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks)
throws Exception {
validateSSLConfigurationIsReloaded(settings, env, true, true, keyManagerPreChecks, trustManagerPreChecks, modificationFunction,
keyManagerPostChecks, trustManagerPostChecks);
}
private void validateSSLConfigurationIsReloaded(Settings settings, Environment env, boolean checkKeys, boolean checkTrust,
BiFunction<X509ExtendedKeyManager, SSLConfiguration, Void> keyManagerPreChecks,
BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPreChecks,
BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPreChecks,
BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPreChecks,
Runnable modificationFunction,
BiFunction<X509ExtendedKeyManager, SSLConfiguration, Void> keyManagerPostChecks,
BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPostChecks)
BiConsumer<X509ExtendedKeyManager, SSLConfiguration> keyManagerPostChecks,
BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks)
throws Exception {
final CountDownLatch reloadLatch = new CountDownLatch(1);
@ -494,12 +507,12 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
// key manager checks
if (checkKeys) {
keyManagerPreChecks.apply(keyManager, config);
keyManagerPreChecks.accept(keyManager, config);
}
// trust manager checks
if (checkTrust) {
trustManagerPreChecks.apply(trustManager, config);
trustManagerPreChecks.accept(trustManager, config);
}
assertEquals("nothing should have called reload", 1, reloadLatch.getCount());
@ -512,14 +525,14 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
if (checkKeys) {
final X509ExtendedKeyManager updatedKeyManager = sslService.sslContextHolder(config).keyManager().getKeyManager();
assertThat(updatedKeyManager, not(sameInstance(keyManager)));
keyManagerPostChecks.apply(updatedKeyManager, config);
keyManagerPostChecks.accept(updatedKeyManager, config);
}
// check trust manager
if (checkTrust) {
final X509ExtendedTrustManager updatedTrustManager = sslService.sslContextHolder(config).trustManager().getTrustManager();
assertThat(updatedTrustManager, not(sameInstance(trustManager)));
trustManagerPostChecks.apply(updatedTrustManager, config);
trustManagerPostChecks.accept(updatedTrustManager, config);
}
}

View File

@ -7,8 +7,8 @@ package org.elasticsearch.xpack.ssl;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.ssl.DefaultJDKTrustConfig.CombiningTrustConfig;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.ssl.TrustConfig.CombiningTrustConfig;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;

View File

@ -42,7 +42,6 @@ import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;
import static org.hamcrest.Matchers.containsString;
@ -68,12 +67,8 @@ public class SSLReloadIntegTests extends SecurityIntegTestCase {
}
}
Settings settings = super.nodeSettings(nodeOrdinal);
Settings.Builder builder = Settings.builder();
for (Entry<String, String> entry : settings.getAsMap().entrySet()) {
if (entry.getKey().startsWith("xpack.ssl.") == false) {
builder.put(entry.getKey(), entry.getValue());
}
}
Settings.Builder builder = Settings.builder()
.put(settings.filter((s) -> s.startsWith("xpack.ssl.") == false));
builder.put("resource.reload.interval.high", "1s")
.put(SecuritySettingsSource.getSSLSettingsForStore(
@ -88,8 +83,8 @@ public class SSLReloadIntegTests extends SecurityIntegTestCase {
}
@Override
protected boolean sslTransportEnabled() {
return true;
protected boolean useGeneratedSSLConfig() {
return false;
}
public void testThatSSLConfigurationReloadsOnModification() throws Exception {

View File

@ -157,18 +157,9 @@ public class SSLServiceTests extends ESTestCase {
assertThat(sslEngine, notNullValue());
}
public void testCreateWithoutAnySettingsNotValidForServer() throws Exception {
public void testCreateWithoutAnySettingsValidForServer() throws Exception {
SSLService sslService = new SSLService(Settings.EMPTY, env);
assertFalse(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, Settings.EMPTY));
}
public void testCreateWithOnlyTruststoreNotValidForServer() throws Exception {
Settings settings = Settings.builder()
.put("xpack.ssl.truststore.path", testnodeStore)
.put("xpack.ssl.truststore.password", "testnode")
.build();
SSLService sslService = new SSLService(settings, env);
assertFalse(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, Settings.EMPTY));
assertTrue(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, true));
}
public void testCreateWithKeystoreIsValidForServer() throws Exception {
@ -177,7 +168,7 @@ public class SSLServiceTests extends ESTestCase {
.put("xpack.ssl.keystore.password", "testnode")
.build();
SSLService sslService = new SSLService(settings, env);
assertTrue(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, Settings.EMPTY));
assertTrue(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, false));
}
public void testValidForServerWithFallback() throws Exception {
@ -186,7 +177,8 @@ public class SSLServiceTests extends ESTestCase {
.put("xpack.ssl.truststore.password", "testnode")
.build();
SSLService sslService = new SSLService(settings, env);
assertFalse(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, settings.getByPrefix("xpack.ssl.")));
// transport is valid in default config
assertTrue(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, true));
settings = Settings.builder()
.put("xpack.ssl.truststore.path", testnodeStore)
@ -195,13 +187,13 @@ public class SSLServiceTests extends ESTestCase {
.put("xpack.security.transport.ssl.keystore.password", "testnode")
.build();
sslService = new SSLService(settings, env);
assertFalse(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, settings.getByPrefix("xpack.ssl.")));
assertFalse(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, false));
assertTrue(sslService.isConfigurationValidForServerUsage(
settings.getByPrefix("xpack.security.transport.ssl."), settings.getByPrefix("xpack.ssl.")));
assertTrue(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, settings.getByPrefix("xpack.security.transport.ssl.")));
settings.getByPrefix("xpack.security.transport.ssl."), false));
assertTrue(sslService.isConfigurationValidForServerUsage(Settings.EMPTY, true));
}
public void testGetVerificationMode() {
public void testGetVerificationMode() throws Exception {
SSLService sslService = new SSLService(Settings.EMPTY, env);
assertThat(sslService.getVerificationMode(Settings.EMPTY, Settings.EMPTY), is(XPackSettings.VERIFICATION_MODE_DEFAULT));
@ -220,7 +212,7 @@ public class SSLServiceTests extends ESTestCase {
is(VerificationMode.CERTIFICATE));
}
public void testIsSSLClientAuthEnabled() {
public void testIsSSLClientAuthEnabled() throws Exception {
SSLService sslService = new SSLService(Settings.EMPTY, env);
assertTrue(sslService.isSSLClientAuthEnabled(Settings.EMPTY));
assertTrue(sslService.isSSLClientAuthEnabled(Settings.EMPTY, Settings.EMPTY));
@ -238,7 +230,7 @@ public class SSLServiceTests extends ESTestCase {
settings.getByPrefix("xpack.security.transport.ssl.")));
}
public void testThatHttpClientAuthDefaultsToNone() {
public void testThatHttpClientAuthDefaultsToNone() throws Exception {
final Settings globalSettings = Settings.builder()
.put("xpack.security.http.ssl.enabled", true)
.put("xpack.ssl.client_authentication", SSLClientAuth.OPTIONAL.name())

View File

@ -5,17 +5,25 @@
*/
package org.elasticsearch.xpack.ssl;
import org.bouncycastle.operator.OperatorCreationException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import javax.net.ssl.SSLContext;
import javax.security.auth.DestroyFailedException;
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
/**
* Extending SSLService to make helper methods public to access in tests
*/
public class TestsSSLService extends SSLService {
public TestsSSLService(Settings settings, Environment environment) {
public TestsSSLService(Settings settings, Environment environment) throws CertificateException, UnrecoverableKeyException,
NoSuchAlgorithmException, IOException, DestroyFailedException, KeyStoreException, OperatorCreationException {
super(settings, environment);
}

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.xpack.watcher.test.bench;
import org.bouncycastle.operator.OperatorCreationException;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
@ -24,7 +25,12 @@ import org.elasticsearch.xpack.watcher.trigger.ScheduleTriggerEngineMock;
import org.elasticsearch.xpack.watcher.trigger.TriggerEngine;
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleRegistry;
import javax.security.auth.DestroyFailedException;
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.time.Clock;
import java.util.Arrays;
@ -201,7 +207,8 @@ public class WatcherExecutorServiceBenchmark {
public static final class XPackBenchmarkPlugin extends XPackPlugin {
public XPackBenchmarkPlugin(Settings settings) throws IOException {
public XPackBenchmarkPlugin(Settings settings) throws IOException, CertificateException, UnrecoverableKeyException,
NoSuchAlgorithmException, KeyStoreException, DestroyFailedException, OperatorCreationException {
super(settings);
watcher = new BenchmarkWatcher(settings);
}

View File

@ -47,6 +47,8 @@ Closure waitWithAuth = { NodeInfo node, AntBuilder ant ->
return tmpFile.exists()
}
String outputDir = "generated-resources/${project.name}"
task oldClusterTest(type: RestIntegTestTask) {
mustRunAfter(precommit)
cluster {
@ -58,6 +60,10 @@ task oldClusterTest(type: RestIntegTestTask) {
clusterName = 'rolling-upgrade'
waitCondition = waitWithAuth
setting 'logger.org.elasticsearch.xpack.security', 'TRACE'
setting 'xpack.security.transport.ssl.enabled', 'true'
setting 'xpack.ssl.keystore.path', 'testnode.jks'
setting 'xpack.ssl.keystore.password', 'testnode'
extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks')
}
systemProperty 'tests.rest.suite', 'old_cluster'
}
@ -71,6 +77,9 @@ task mixedClusterTest(type: RestIntegTestTask) {
unicastTransportUri = { seedNode, node, ant -> oldClusterTest.nodes.get(0).transportUri() }
dataDir = "${-> oldClusterTest.nodes[1].dataDir}"
waitCondition = waitWithAuth
setting 'xpack.ssl.keystore.path', 'testnode.jks'
setting 'xpack.ssl.keystore.password', 'testnode'
extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks')
}
systemProperty 'tests.rest.suite', 'mixed_cluster'
finalizedBy 'oldClusterTest#node0.stop'
@ -85,6 +94,9 @@ task upgradedClusterTest(type: RestIntegTestTask) {
unicastTransportUri = { seedNode, node, ant -> mixedClusterTest.nodes.get(0).transportUri() }
dataDir = "${-> oldClusterTest.nodes[0].dataDir}"
waitCondition = waitWithAuth
setting 'xpack.ssl.keystore.path', 'testnode.jks'
setting 'xpack.ssl.keystore.password', 'testnode'
extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks')
}
systemProperty 'tests.rest.suite', 'upgraded_cluster'
// only need to kill the mixed cluster tests node here because we explicitly told it to not stop nodes upon completion
@ -104,7 +116,6 @@ dependencies {
}
// copy x-pack plugin info so it is on the classpath and security manager has the right permissions
String outputDir = "generated-resources/${project.name}"
task copyXPackRestSpec(type: Copy) {
dependsOn(project.configurations.restSpec, 'processTestResources')
from project(':x-pack:elasticsearch').sourceSets.test.resources
@ -112,8 +123,15 @@ task copyXPackRestSpec(type: Copy) {
into project.sourceSets.test.output.resourcesDir
}
task copyXPackPluginProps(type: Copy) {
task copyTestNodeKeystore(type: Copy) {
dependsOn(copyXPackRestSpec)
from project(':x-pack:elasticsearch')
.file('src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks')
into outputDir
}
task copyXPackPluginProps(type: Copy) {
dependsOn(copyTestNodeKeystore)
from project(':x-pack:elasticsearch').file('src/main/plugin-metadata')
from project(':x-pack:elasticsearch').tasks.pluginProperties
into outputDir

View File

@ -171,10 +171,9 @@ integTest {
setting 'xpack.monitoring.exporters._http.auth.username', 'monitoring_agent'
setting 'xpack.monitoring.exporters._http.auth.password', 'changeme'
setting 'xpack.security.transport.ssl.enabled', 'true'
setting 'xpack.security.http.ssl.enabled', 'true'
setting 'xpack.ssl.keystore.path', nodeKeystore.name
setting 'xpack.ssl.keystore.password', 'keypass'
setting 'xpack.security.http.ssl.keystore.path', nodeKeystore.name
setting 'xpack.security.http.ssl.keystore.password', 'keypass'
plugin ':x-pack:elasticsearch'

View File

@ -5,27 +5,19 @@
*/
package org.elasticsearch.smoketest;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.xpack.XPackPlugin;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -47,7 +39,6 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo;
public class SmokeTestMonitoringWithSecurityIT extends ESIntegTestCase {
private static final String USER = "test_user";
private static final String PASS = "changeme";
private static final String KEYSTORE_PASS = "keypass";
private static final String MONITORING_PATTERN = ".monitoring-*";
@Override
@ -59,9 +50,6 @@ public class SmokeTestMonitoringWithSecurityIT extends ESIntegTestCase {
protected Settings externalClusterClientSettings() {
return Settings.builder()
.put(Security.USER_SETTING.getKey(), USER + ":" + PASS)
.put("xpack.security.transport.ssl.enabled", true)
.put("xpack.ssl.keystore.path", clientKeyStore)
.put("xpack.ssl.keystore.password", KEYSTORE_PASS)
.put(NetworkModule.TRANSPORT_TYPE_KEY, Security.NAME4).build();
}
@ -122,23 +110,4 @@ public class SmokeTestMonitoringWithSecurityIT extends ESIntegTestCase {
}
return httpAddresses;
}
static Path clientKeyStore;
@BeforeClass
public static void loadKeyStore() {
try {
clientKeyStore = PathUtils.get(SmokeTestMonitoringWithSecurityIT.class.getResource("/test-client.jks").toURI());
} catch (URISyntaxException e) {
throw new ElasticsearchException("exception while reading the store", e);
}
if (!Files.exists(clientKeyStore)) {
throw new IllegalStateException("Keystore file [" + clientKeyStore + "] does not exist.");
}
}
@AfterClass
public static void clearClientKeyStore() {
clientKeyStore = null;
}
}