Only require TLS for standard/gold/platinum licenses elastic/x-pack-elasticsearch#2517

relates elastic/x-pack-elasticsearch#2463

Original commit: elastic/x-pack-elasticsearch@5213cf24f1
This commit is contained in:
Simon Willnauer 2017-09-15 20:21:15 +02:00
commit 96e01dce47
60 changed files with 603 additions and 792 deletions

View File

@ -98,7 +98,8 @@ IMPORTANT: Once you get these basic security measures in place, we strongly
recommend that you secure communications to and from nodes by
configuring your cluster to use {xpack-ref}/ssl-tls.html[SSL/TLS encryption].
Nodes that do not have encryption enabled send passwords in plain
text!
text and will not be able to install a non-trial license that enables the use
of {security}.
Depending on your security requirements, you might also want to:

View File

@ -4,8 +4,8 @@
Elasticsearch nodes store data that may be confidential. Attacks on the data may
come from the network. These attacks could include sniffing of the data,
manipulation of the data, and attempts to gain access to the server and thus the
files storing the data. Securing your nodes with the procedures below helps to
reduce risk from network-based attacks.
files storing the data. Securing your nodes is required in order to use a production
license that enables {security} and helps reduce the risk from network-based attacks.
This section shows how to:

View File

@ -38,19 +38,6 @@ transport.profiles.client.bind_host: 1.1.1.1 <2>
If separate networks are not available, then <<ip-filtering, IP Filtering>> can
be enabled to limit access to the profiles.
The TCP transport profiles also allow for enabling SSL on a per profile basis.
This is useful if you have a secured network for the node-to-node communication,
but the client is on an unsecured network. To enable SSL on a client profile when
SSL is disabled for node-to-node communication, add the following to
`elasticsearch.yml`:
[source, yaml]
--------------------------------------------------
transport.profiles.client.xpack.security.ssl.enabled: true <1>
--------------------------------------------------
<1> This enables SSL on the client profile. The default value for this setting
is the value of `xpack.security.transport.ssl.enabled`.
When using SSL for transport, a different set of certificates can also be used
for the client traffic by adding the following to `elasticsearch.yml`:

View File

@ -6,7 +6,7 @@ cluster. Connections are secured using Transport Layer Security (TLS), which is
commonly referred to as "SSL".
WARNING: Clusters that do not have encryption enabled send all data in plain text
including passwords.
including passwords and will not be able to install a license that enables {security}.
To enable encryption, you need to perform the following steps on each node in
the cluster:

View File

@ -715,11 +715,11 @@ are also available for each transport profile. By default, the settings for a
transport profile will be the same as the default transport unless they
are specified.
As an example, lets look at the enabled setting. For the default transport
this is `xpack.security.transport.ssl.enabled`. In order to use this setting in a
As an example, lets look at the key setting. For the default transport
this is `xpack.security.transport.ssl.key`. In order to use this setting in a
transport profile, use the prefix `transport.profiles.$PROFILE.xpack.security.` and
append the portion of the setting after `xpack.security.transport.`. For the enabled
setting, this would be `transport.profiles.$PROFILE.xpack.security.ssl.enabled`.
append the portion of the setting after `xpack.security.transport.`. For the key
setting, this would be `transport.profiles.$PROFILE.xpack.security.ssl.key`.
[float]
[[ip-filtering-settings]]

View File

@ -1,3 +1,4 @@
import org.elasticsearch.gradle.LoggedExec
import org.elasticsearch.gradle.MavenFilteringHack
import org.elasticsearch.gradle.test.NodeInfo
@ -198,7 +199,39 @@ integTestRunner {
systemProperty 'tests.rest.blacklist', 'getting_started/10_monitor_cluster_health/*'
}
// location of generated keystores and certificates
File keystoreDir = new File(project.buildDir, 'keystore')
// Generate the node's keystore
File nodeKeystore = new File(keystoreDir, 'test-node.jks')
task createNodeKeyStore(type: LoggedExec) {
doFirst {
if (nodeKeystore.parentFile.exists() == false) {
nodeKeystore.parentFile.mkdirs()
}
if (nodeKeystore.exists()) {
delete nodeKeystore
}
}
executable = new File(project.javaHome, 'bin/keytool')
standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8'))
args '-genkey',
'-alias', 'test-node',
'-keystore', nodeKeystore,
'-keyalg', 'RSA',
'-keysize', '2048',
'-validity', '712',
'-dname', 'CN=smoke-test-plugins-ssl',
'-keypass', 'keypass',
'-storepass', 'keypass'
}
// Add keystores to test classpath: it expects it there
sourceSets.test.resources.srcDir(keystoreDir)
processTestResources.dependsOn(createNodeKeyStore)
integTestCluster {
dependsOn createNodeKeyStore
setting 'xpack.ml.enabled', 'true'
setting 'logger.org.elasticsearch.xpack.ml.datafeed', 'TRACE'
// Integration tests are supposed to enable/disable exporters before/after each test
@ -206,11 +239,17 @@ integTestCluster {
setting 'xpack.monitoring.exporters._local.enabled', 'false'
setting 'xpack.monitoring.collection.interval', '-1'
setting 'xpack.security.authc.token.enabled', 'true'
setting 'xpack.security.transport.ssl.enabled', 'true'
setting 'xpack.security.transport.ssl.keystore.path', nodeKeystore.name
setting 'xpack.security.transport.ssl.verification_mode', 'certificate'
keystoreSetting 'bootstrap.password', 'x-pack-test-password'
keystoreSetting 'xpack.security.transport.ssl.keystore.secure_password', 'keypass'
distribution = 'zip' // this is important since we use the reindex module in ML
setupCommand 'setupTestUser', 'bin/x-pack/users', 'useradd', 'x_pack_rest_user', '-p', 'x-pack-test-password', '-r', 'superuser'
extraConfigFile nodeKeystore.name, nodeKeystore
waitCondition = { NodeInfo node, AntBuilder ant ->
File tmpFile = new File(node.cwd, 'wait.success')

View File

@ -782,4 +782,23 @@ public class License implements ToXContentObject {
}
}
}
/**
* Returns <code>true</code> iff the license is a production licnese
*/
public boolean isProductionLicense() {
switch (operationMode()) {
case MISSING:
case TRIAL:
case BASIC:
return false;
case STANDARD:
case GOLD:
case PLATINUM:
return true;
default:
throw new AssertionError("unknown operation mode: " + operationMode());
}
}
}

View File

@ -30,6 +30,7 @@ import org.elasticsearch.env.Environment;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.scheduler.SchedulerEngine;
import java.time.Clock;
@ -207,20 +208,31 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
}
}
}
clusterService.submitStateUpdateTask("register license [" + newLicense.uid() + "]", new
AckedClusterStateUpdateTask<PutLicenseResponse>(request, listener) {
@Override
protected PutLicenseResponse newResponse(boolean acknowledged) {
return new PutLicenseResponse(acknowledged, LicensesStatus.VALID);
}
@Override
public ClusterState execute(ClusterState currentState) throws Exception {
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense));
return ClusterState.builder(currentState).metaData(mdBuilder).build();
}
});
if (newLicense.isProductionLicense()
&& XPackSettings.SECURITY_ENABLED.get(settings)
&& XPackSettings.TRANSPORT_SSL_ENABLED.get(settings) == false) {
// security is on but TLS is not configured we gonna fail the entire request and throw an exception
throw new IllegalStateException("Can not upgrade to a production license unless TLS is configured or " +
"security is disabled");
// TODO we should really validate that all nodes have xpack in stalled and are consistently configured but this
// should happen on a different level and not in this code
} else {
clusterService.submitStateUpdateTask("register license [" + newLicense.uid() + "]", new
AckedClusterStateUpdateTask<PutLicenseResponse>(request, listener) {
@Override
protected PutLicenseResponse newResponse(boolean acknowledged) {
return new PutLicenseResponse(acknowledged, LicensesStatus.VALID);
}
@Override
public ClusterState execute(ClusterState currentState) throws Exception {
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense));
return ClusterState.builder(currentState).metaData(mdBuilder).build();
}
});
}
}
}
@ -271,7 +283,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
}
public License getLicense() {
final License license = getLicense(clusterService.state().metaData().custom(LicensesMetaData.TYPE));
final License license = getLicense(clusterService.state().metaData());
return license == LicensesMetaData.LICENSE_TOMBSTONE ? null : license;
}
@ -469,7 +481,12 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
};
}
License getLicense(final LicensesMetaData metaData) {
public static License getLicense(final MetaData metaData) {
final LicensesMetaData licensesMetaData = metaData.custom(LicensesMetaData.TYPE);
return getLicense(licensesMetaData);
}
static License getLicense(final LicensesMetaData metaData) {
if (metaData != null) {
License license = metaData.getLicense();
if (license == LicensesMetaData.LICENSE_TOMBSTONE) {

View File

@ -16,6 +16,7 @@ import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Binder;
@ -43,6 +44,7 @@ import org.elasticsearch.license.Licensing;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.plugins.ClusterPlugin;
import org.elasticsearch.plugins.DiscoveryPlugin;
import org.elasticsearch.plugins.IngestPlugin;
import org.elasticsearch.plugins.NetworkPlugin;
import org.elasticsearch.plugins.Plugin;
@ -124,6 +126,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
@ -131,7 +134,7 @@ import java.util.stream.Stream;
import static org.elasticsearch.xpack.watcher.Watcher.ENCRYPT_SENSITIVE_DATA_SETTING;
public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, IngestPlugin, NetworkPlugin, ClusterPlugin {
public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, IngestPlugin, NetworkPlugin, ClusterPlugin, DiscoveryPlugin {
public static final String NAME = "x-pack";
@ -605,4 +608,9 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
public Map<String, Supplier<ClusterState.Custom>> getInitialClusterStateCustomSupplier() {
return security.getInitialClusterStateCustomSupplier();
}
@Override
public BiConsumer<DiscoveryNode, ClusterState> getJoinValidator() {
return security.getJoinValidator();
}
}

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
@ -56,20 +55,9 @@ public class XPackSettings {
public static final Setting<Boolean> LOGSTASH_ENABLED = Setting.boolSetting("xpack.logstash.enabled", true,
Setting.Property.NodeScope);
/**
* 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<>("xpack.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 TLS. Defaults to false. */
public static final Setting<Boolean> TRANSPORT_SSL_ENABLED = Setting.boolSetting("xpack.security.transport.ssl.enabled", false,
Property.NodeScope);
/** Setting for enabling or disabling http ssl. Defaults to false. */
public static final Setting<Boolean> HTTP_SSL_ENABLED = Setting.boolSetting("xpack.security.http.ssl.enabled", false,

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security;
import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.bootstrap.BootstrapContext;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.security.authc.RealmSettings;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4Transport;
@ -46,16 +47,18 @@ class PkiRealmBootstrapCheck implements BootstrapCheck {
}
// Default Transport
final boolean transportSSLEnabled = XPackSettings.TRANSPORT_SSL_ENABLED.get(settings);
final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl."));
final boolean clientAuthEnabled = sslService.isSSLClientAuthEnabled(transportSSLSettings);
if (clientAuthEnabled) {
if (transportSSLEnabled && clientAuthEnabled) {
return BootstrapCheckResult.success();
}
// Transport Profiles
Map<String, Settings> groupedSettings = settings.getGroups("transport.profiles.");
for (Map.Entry<String, Settings> entry : groupedSettings.entrySet()) {
if (sslService.isSSLClientAuthEnabled(SecurityNetty4Transport.profileSslSettings(entry.getValue()), transportSSLSettings)) {
if (transportSSLEnabled && sslService.isSSLClientAuthEnabled(
SecurityNetty4Transport.profileSslSettings(entry.getValue()), transportSSLSettings)) {
return BootstrapCheckResult.success();
}
}

View File

@ -16,10 +16,10 @@ import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.LocalNodeMasterListener;
import org.elasticsearch.cluster.NamedDiff;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Booleans;
@ -52,9 +52,12 @@ import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.index.IndexModule;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.ingest.Processor;
import org.elasticsearch.license.License;
import org.elasticsearch.license.LicenseService;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.plugins.ClusterPlugin;
import org.elasticsearch.plugins.DiscoveryPlugin;
import org.elasticsearch.plugins.IngestPlugin;
import org.elasticsearch.plugins.NetworkPlugin;
import org.elasticsearch.rest.RestController;
@ -163,9 +166,9 @@ 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.SSLConfigurationSettings;
import org.elasticsearch.xpack.ssl.SSLService;
import org.elasticsearch.xpack.ssl.TLSLicenseBootstrapCheck;
import org.elasticsearch.xpack.template.TemplateUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@ -195,7 +198,7 @@ import static java.util.Collections.singletonList;
import static org.elasticsearch.xpack.XPackSettings.HTTP_SSL_ENABLED;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_TEMPLATE_NAME;
public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, ClusterPlugin {
public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, ClusterPlugin, DiscoveryPlugin {
private static final Logger logger = Loggers.getLogger(XPackPlugin.class);
@ -243,9 +246,9 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, Clus
// fetched
final List<BootstrapCheck> checks = new ArrayList<>();
checks.addAll(Arrays.asList(
new SSLBootstrapCheck(sslService, env),
new TokenSSLBootstrapCheck(),
new PkiRealmBootstrapCheck(sslService)));
new PkiRealmBootstrapCheck(sslService),
new TLSLicenseBootstrapCheck()));
checks.addAll(InternalRealms.getBootstrapChecks(settings));
this.bootstrapChecks = Collections.unmodifiableList(checks);
} else {
@ -902,4 +905,25 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, Clus
return Collections.emptyMap();
}
}
@Override
public BiConsumer<DiscoveryNode, ClusterState> getJoinValidator() {
return enabled ? new ValidateTLSOnJoin(XPackSettings.TRANSPORT_SSL_ENABLED.get(settings)) : null;
}
static final class ValidateTLSOnJoin implements BiConsumer<DiscoveryNode, ClusterState> {
private final boolean isTLSEnabled;
ValidateTLSOnJoin(boolean isTLSEnabled) {
this.isTLSEnabled = isTLSEnabled;
}
@Override
public void accept(DiscoveryNode node, ClusterState state) {
License license = LicenseService.getLicense(state.metaData());
if (license != null && license.isProductionLicense() && isTLSEnabled == false) {
throw new IllegalStateException("TLS setup is required for license type [" + license.operationMode().name() + "]");
}
}
}
}

View File

@ -171,10 +171,12 @@ public class SecurityServerTransportInterceptor extends AbstractComponent implem
Map<String, ServerTransportFilter> profileFilters = new HashMap<>(profileSettingsMap.size() + 1);
final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl."));
final boolean transportSSLEnabled = XPackSettings.TRANSPORT_SSL_ENABLED.get(settings);
for (Map.Entry<String, Settings> entry : profileSettingsMap.entrySet()) {
Settings profileSettings = entry.getValue();
final Settings profileSslSettings = SecurityNetty4Transport.profileSslSettings(profileSettings);
final boolean extractClientCert = sslService.isSSLClientAuthEnabled(profileSslSettings, transportSSLSettings);
final boolean extractClientCert = transportSSLEnabled &&
sslService.isSSLClientAuthEnabled(profileSslSettings, transportSSLSettings);
String type = TRANSPORT_TYPE_SETTING_TEMPLATE.apply(TRANSPORT_TYPE_SETTING_KEY).get(entry.getValue());
switch (type) {
case "client":
@ -193,7 +195,7 @@ public class SecurityServerTransportInterceptor extends AbstractComponent implem
}
if (!profileFilters.containsKey(TcpTransport.DEFAULT_PROFILE)) {
final boolean extractClientCert = sslService.isSSLClientAuthEnabled(transportSSLSettings);
final boolean extractClientCert = transportSSLEnabled && sslService.isSSLClientAuthEnabled(transportSSLSettings);
profileFilters.put(TcpTransport.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService,
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled, securityContext));
}

View File

@ -21,6 +21,7 @@ import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TcpTransport;
import org.elasticsearch.transport.netty4.Netty4Transport;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.ssl.SSLConfiguration;
import org.elasticsearch.xpack.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
@ -47,6 +48,7 @@ public class SecurityNetty4Transport extends Netty4Transport {
@Nullable private final IPFilter authenticator;
private final SSLConfiguration sslConfiguration;
private final Map<String, SSLConfiguration> profileConfiguration;
private final boolean sslEnabled;
public SecurityNetty4Transport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays,
NamedWriteableRegistry namedWriteableRegistry, CircuitBreakerService circuitBreakerService,
@ -54,23 +56,28 @@ public class SecurityNetty4Transport extends Netty4Transport {
super(settings, threadPool, networkService, bigArrays, namedWriteableRegistry, circuitBreakerService);
this.authenticator = authenticator;
this.sslService = sslService;
this.sslEnabled = XPackSettings.TRANSPORT_SSL_ENABLED.get(settings);
final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl."));
sslConfiguration = sslService.sslConfiguration(transportSSLSettings, Settings.EMPTY);
Map<String, Settings> profileSettingsMap = settings.getGroups("transport.profiles.", true);
Map<String, SSLConfiguration> profileConfiguration = new HashMap<>(profileSettingsMap.size() + 1);
for (Map.Entry<String, Settings> entry : profileSettingsMap.entrySet()) {
Settings profileSettings = entry.getValue();
final Settings profileSslSettings = profileSslSettings(profileSettings);
SSLConfiguration configuration = sslService.sslConfiguration(profileSslSettings, transportSSLSettings);
profileConfiguration.put(entry.getKey(), configuration);
if (sslEnabled) {
this.sslConfiguration = sslService.sslConfiguration(transportSSLSettings, Settings.EMPTY);
Map<String, Settings> profileSettingsMap = settings.getGroups("transport.profiles.", true);
Map<String, SSLConfiguration> profileConfiguration = new HashMap<>(profileSettingsMap.size() + 1);
for (Map.Entry<String, Settings> entry : profileSettingsMap.entrySet()) {
Settings profileSettings = entry.getValue();
final Settings profileSslSettings = profileSslSettings(profileSettings);
SSLConfiguration configuration = sslService.sslConfiguration(profileSslSettings, transportSSLSettings);
profileConfiguration.put(entry.getKey(), configuration);
}
if (profileConfiguration.containsKey(TcpTransport.DEFAULT_PROFILE) == false) {
profileConfiguration.put(TcpTransport.DEFAULT_PROFILE, sslConfiguration);
}
this.profileConfiguration = Collections.unmodifiableMap(profileConfiguration);
} else {
this.profileConfiguration = Collections.emptyMap();
this.sslConfiguration = null;
}
if (profileConfiguration.containsKey(TcpTransport.DEFAULT_PROFILE) == false) {
profileConfiguration.put(TcpTransport.DEFAULT_PROFILE, sslConfiguration);
}
this.profileConfiguration = Collections.unmodifiableMap(profileConfiguration);
}
@Override
@ -83,11 +90,15 @@ public class SecurityNetty4Transport extends Netty4Transport {
@Override
protected ChannelHandler getServerChannelInitializer(String name) {
SSLConfiguration configuration = profileConfiguration.get(name);
if (configuration == null) {
throw new IllegalStateException("unknown profile: " + name);
if (sslEnabled) {
SSLConfiguration configuration = profileConfiguration.get(name);
if (configuration == null) {
throw new IllegalStateException("unknown profile: " + name);
}
return new SecurityServerChannelInitializer(name, configuration);
} else {
return new IPFilterServerChannelInitializer(name);
}
return new SecurityServerChannelInitializer(name, configuration);
}
@Override
@ -127,13 +138,26 @@ public class SecurityNetty4Transport extends Netty4Transport {
}
}
class SecurityServerChannelInitializer extends ServerChannelInitializer {
class IPFilterServerChannelInitializer extends ServerChannelInitializer {
IPFilterServerChannelInitializer(String name) {
super(name);
}
@Override
protected void initChannel(Channel ch) throws Exception {
super.initChannel(ch);
if (authenticator != null) {
ch.pipeline().addFirst("ipfilter", new IpFilterRemoteAddressFilter(authenticator, name));
}
}
}
class SecurityServerChannelInitializer extends IPFilterServerChannelInitializer {
private final SSLConfiguration configuration;
SecurityServerChannelInitializer(String name, SSLConfiguration configuration) {
super(name);
this.configuration = configuration;
}
@Override
@ -141,9 +165,12 @@ public class SecurityNetty4Transport extends Netty4Transport {
super.initChannel(ch);
SSLEngine serverEngine = sslService.createSSLEngine(configuration, null, -1);
serverEngine.setUseClientMode(false);
ch.pipeline().addFirst(new SslHandler(serverEngine));
if (authenticator != null) {
ch.pipeline().addFirst(new IpFilterRemoteAddressFilter(authenticator, name));
IpFilterRemoteAddressFilter remoteAddressFilter = ch.pipeline().get(IpFilterRemoteAddressFilter.class);
final SslHandler sslHandler = new SslHandler(serverEngine);
if (remoteAddressFilter == null) {
ch.pipeline().addFirst("sslhandler", sslHandler);
} else {
ch.pipeline().addAfter("ipfilter", "sslhandler", sslHandler);
}
}
}
@ -153,13 +180,15 @@ public class SecurityNetty4Transport extends Netty4Transport {
private final boolean hostnameVerificationEnabled;
SecurityClientChannelInitializer() {
this.hostnameVerificationEnabled = sslConfiguration.verificationMode().isHostnameVerificationEnabled();
this.hostnameVerificationEnabled = sslEnabled && sslConfiguration.verificationMode().isHostnameVerificationEnabled();
}
@Override
protected void initChannel(Channel ch) throws Exception {
super.initChannel(ch);
ch.pipeline().addFirst(new ClientSslHandlerInitializer(sslConfiguration, sslService, hostnameVerificationEnabled));
if (sslEnabled) {
ch.pipeline().addFirst(new ClientSslHandlerInitializer(sslConfiguration, sslService, hostnameVerificationEnabled));
}
}
}
@ -197,5 +226,4 @@ public class SecurityNetty4Transport extends Netty4Transport {
public static Settings profileSslSettings(Settings profileSettings) {
return profileSettings.getByPrefix(setting("ssl."));
}
}

View File

@ -1,211 +0,0 @@
/*
* 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

@ -1,99 +0,0 @@
/*
* 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.bootstrap.BootstrapContext;
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 Environment environment;
public SSLBootstrapCheck(SSLService sslService, @Nullable Environment environment) {
this.sslService = sslService;
this.environment = environment;
}
@Override
public BootstrapCheckResult check(BootstrapContext context) {
final Settings transportSSLSettings = context.settings.getByPrefix(XPackSettings.TRANSPORT_SSL_PREFIX);
if (sslService.sslConfiguration(transportSSLSettings).keyConfig() == KeyConfig.NONE
|| isDefaultCACertificateTrusted() || isDefaultPrivateKeyUsed()) {
return BootstrapCheckResult.failure(
"default SSL key and certificate do not provide security; please generate keys and certificates");
} else {
return BootstrapCheckResult.success();
}
}
/**
* 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);
}
}

View File

@ -10,7 +10,6 @@ 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;
@ -33,7 +32,6 @@ 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;
@ -447,74 +445,12 @@ public class SSLService extends AbstractComponent {
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) {
createDevelopmentTLSConfiguration(sslConfigurations, transportSSLConfiguration, profileSettings);
} else {
sslConfigurations.computeIfAbsent(transportSSLConfiguration, this::createSslContext);
profileSettings.forEach((profileSetting) ->
sslConfigurations.computeIfAbsent(new SSLConfiguration(profileSetting, transportSSLConfiguration), this::createSslContext));
}
sslConfigurations.computeIfAbsent(transportSSLConfiguration, this::createSslContext);
profileSettings.forEach((profileSetting) ->
sslConfigurations.computeIfAbsent(new SSLConfiguration(profileSetting, transportSSLConfiguration), this::createSslContext));
return Collections.unmodifiableMap(sslConfigurations);
}
private void createDevelopmentTLSConfiguration(Map<SSLConfiguration, SSLContextHolder> sslConfigurations,
SSLConfiguration transportSSLConfiguration, List<Settings> profileSettings)
throws NoSuchAlgorithmException, IOException, CertificateException, OperatorCreationException, UnrecoverableKeyException,
KeyStoreException {
// 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);
}
});
}
/**
* This socket factory wraps an existing SSLSocketFactory and sets the protocols and ciphers on each SSLSocket after it is created. This
* is needed even though the SSLContext is configured properly as the configuration does not flow down to the sockets created by the

View File

@ -0,0 +1,30 @@
/*
* 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.bootstrap.BootstrapCheck;
import org.elasticsearch.bootstrap.BootstrapContext;
import org.elasticsearch.license.License;
import org.elasticsearch.license.LicenseService;
import org.elasticsearch.xpack.XPackSettings;
/**
* Bootstrap check to ensure that if we are starting up with a production license in the local clusterstate TLS is enabled
*/
public final class TLSLicenseBootstrapCheck implements BootstrapCheck {
@Override
public BootstrapCheckResult check(BootstrapContext context) {
if (XPackSettings.TRANSPORT_SSL_ENABLED.get(context.settings) == false) {
License license = LicenseService.getLicense(context.metaData);
if (license != null && license.isProductionLicense()) {
return BootstrapCheckResult.failure("Transport SSL must be enabled for setups with production licenses. Please set " +
"[xpack.security.transport.ssl.enabled] to [true] or disable security by setting [xpack.security.enabled] " +
"to [false]");
}
}
return BootstrapCheckResult.success();
}
}

View File

@ -1,20 +0,0 @@
-----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

@ -1,27 +0,0 @@
-----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

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

View File

@ -34,7 +34,6 @@ import org.junit.BeforeClass;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.Arrays;
@ -221,8 +220,8 @@ public abstract class AbstractAdLdapRealmTestCase extends SecurityIntegTestCase
}
@Override
protected boolean useGeneratedSSLConfig() {
return useGlobalSSL == false;
protected boolean transportSSLEnabled() {
return useGlobalSSL;
}
protected final void configureFileRoleMappings(Settings.Builder builder, List<RoleMappingEntry> mappings) {

View File

@ -48,11 +48,11 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase {
environment = mock(Environment.class);
}
protected void setInitialState(License license, XPackLicenseState licenseState) {
protected void setInitialState(License license, XPackLicenseState licenseState, Settings settings) {
Path tempDir = createTempDir();
when(environment.configFile()).thenReturn(tempDir);
licenseType = randomBoolean() ? "trial" : "basic";
Settings settings = Settings.builder().put(LicenseService.SELF_GENERATED_LICENSE_TYPE.getKey(), licenseType).build();
settings = Settings.builder().put(settings).put(LicenseService.SELF_GENERATED_LICENSE_TYPE.getKey(), licenseType).build();
licenseService = new LicenseService(settings, clusterService, clock, environment, resourceWatcherService, licenseState);
ClusterState state = mock(ClusterState.class);
final ClusterBlocks noBlock = ClusterBlocks.builder().build();

View File

@ -13,6 +13,7 @@ import org.elasticsearch.cluster.ClusterStateUpdateTask;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.junit.After;
import org.junit.Before;
@ -33,7 +34,7 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
@Before
public void setup() {
licenseState = new TestUtils.AssertingLicenseState();
setInitialState(null, licenseState);
setInitialState(null, licenseState, Settings.EMPTY);
licenseService.start();
}

View File

@ -8,6 +8,7 @@ package org.elasticsearch.license;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateUpdateTask;
import org.elasticsearch.common.settings.Settings;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@ -19,7 +20,7 @@ public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase {
public void testTrialLicenseRequestOnEmptyLicenseState() throws Exception {
XPackLicenseState licenseState = new XPackLicenseState();
setInitialState(null, licenseState);
setInitialState(null, licenseState, Settings.EMPTY);
when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true);
licenseService.start();

View File

@ -0,0 +1,53 @@
/*
* 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.license;
import org.elasticsearch.analysis.common.CommonAnalysisPlugin;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.transport.Netty4Plugin;
import org.elasticsearch.xpack.XPackPlugin;
import java.util.Arrays;
import java.util.Collection;
import static org.hamcrest.CoreMatchers.equalTo;
/**
* Basic integration test that checks if license can be upgraded to a production license if TLS is enabled and vice versa.
*/
public class LicenseServiceWithSecurityTests extends SecurityIntegTestCase {
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Arrays.asList(XPackPlugin.class, CommonAnalysisPlugin.class, Netty4Plugin.class);
}
@Override
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
return nodePlugins();
}
public void testLicenseUpgradeFailsWithoutTLS() throws Exception {
assumeFalse("transport ssl is enabled", isTransportSSLEnabled());
LicensingClient licensingClient = new LicensingClient(client());
License license = licensingClient.prepareGetLicense().get().license();
License prodLicense = TestUtils.generateSignedLicense("platinum", TimeValue.timeValueHours(24));
IllegalStateException ise = expectThrows(IllegalStateException.class, () -> licensingClient.preparePutLicense(prodLicense).get());
assertEquals("Can not upgrade to a production license unless TLS is configured or security is disabled", ise.getMessage());
assertThat(licensingClient.prepareGetLicense().get().license(), equalTo(license));
}
public void testLicenseUpgradeSucceedsWithTLS() throws Exception {
assumeTrue("transport ssl is disabled", isTransportSSLEnabled());
LicensingClient licensingClient = new LicensingClient(client());
License prodLicense = TestUtils.generateSignedLicense("platinum", TimeValue.timeValueHours(24));
PutLicenseResponse putLicenseResponse = licensingClient.preparePutLicense(prodLicense).get();
assertEquals(putLicenseResponse.status(), LicensesStatus.VALID);
assertThat(licensingClient.prepareGetLicense().get().license(), equalTo(prodLicense));
}
}

View File

@ -7,6 +7,7 @@ package org.elasticsearch.license;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.ClusterStateUpdateTask;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import static org.hamcrest.Matchers.equalTo;
@ -20,7 +21,7 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase
public void testAcknowledgment() throws Exception {
XPackLicenseState licenseState = new XPackLicenseState();
setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licenseState);
setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licenseState, Settings.EMPTY);
licenseService.start();
// try installing a signed license
License signedLicense = TestUtils.generateSignedLicense("basic", TimeValue.timeValueHours(10));
@ -37,6 +38,58 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase
verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
}
public void testRejectUpgradeToProductionWithoutTLS() throws Exception {
XPackLicenseState licenseState = new XPackLicenseState();
setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licenseState, Settings.EMPTY);
licenseService.start();
// try installing a signed license
License signedLicense = TestUtils.generateSignedLicense("platinum", TimeValue.timeValueHours(10));
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense);
// ensure acknowledgement message was part of the response
IllegalStateException ise = expectThrows(IllegalStateException.class, () ->
licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID, true)));
assertEquals("Can not upgrade to a production license unless TLS is configured or security is disabled", ise.getMessage());
}
public void testUpgradeToProductionWithoutTLSAndSecurityDisabled() throws Exception {
XPackLicenseState licenseState = new XPackLicenseState();
setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licenseState, Settings.builder()
.put("xpack.security.enabled", false).build());
licenseService.start();
// try installing a signed license
License signedLicense = TestUtils.generateSignedLicense("platinum", TimeValue.timeValueHours(10));
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense);
licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID, true));
assertThat(licenseService.getLicense(), not(signedLicense));
verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
// try installing a signed license with acknowledgement
putLicenseRequest = new PutLicenseRequest().license(signedLicense).acknowledge(true);
// ensure license was installed and no acknowledgment message was returned
licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID, false));
verify(clusterService, times(2)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
}
public void testUpgradeToProductionWithTLSAndSecurity() throws Exception {
XPackLicenseState licenseState = new XPackLicenseState();
setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licenseState, Settings.builder()
.put("xpack.security.enabled", true)
.put("xpack.security.transport.ssl.enabled", true).build());
licenseService.start();
// try installing a signed license
License signedLicense = TestUtils.generateSignedLicense("platinum", TimeValue.timeValueHours(10));
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense);
licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID, true));
assertThat(licenseService.getLicense(), not(signedLicense));
verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
// try installing a signed license with acknowledgement
putLicenseRequest = new PutLicenseRequest().license(signedLicense).acknowledge(true);
// ensure license was installed and no acknowledgment message was returned
licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID, false));
verify(clusterService, times(2)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
}
private static class AssertingLicensesUpdateResponse implements ActionListener<PutLicenseResponse> {
private final boolean expectedAcknowledgement;
private final LicensesStatus expectedStatus;

View File

@ -77,19 +77,19 @@ public class LicensesManagerServiceTests extends XPackSingleNodeTestCase {
// put gold license
TestUtils.registerAndAckSignedLicenses(licenseService, goldLicense, LicensesStatus.VALID);
LicensesMetaData licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licenseService.getLicense(licensesMetaData), equalTo(goldLicense));
assertThat(LicenseService.getLicense(licensesMetaData), equalTo(goldLicense));
License platinumLicense = TestUtils.generateSignedLicense("platinum", TimeValue.timeValueSeconds(3));
// put platinum license
TestUtils.registerAndAckSignedLicenses(licenseService, platinumLicense, LicensesStatus.VALID);
licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licenseService.getLicense(licensesMetaData), equalTo(platinumLicense));
assertThat(LicenseService.getLicense(licensesMetaData), equalTo(platinumLicense));
License basicLicense = TestUtils.generateSignedLicense("basic", TimeValue.timeValueSeconds(3));
// put basic license
TestUtils.registerAndAckSignedLicenses(licenseService, basicLicense, LicensesStatus.VALID);
licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licenseService.getLicense(licensesMetaData), equalTo(basicLicense));
assertThat(LicenseService.getLicense(licensesMetaData), equalTo(basicLicense));
}
public void testInvalidLicenseStorage() throws Exception {

View File

@ -7,6 +7,7 @@ package org.elasticsearch.license;
import com.carrotsearch.randomizedtesting.RandomizedTest;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.joda.DateMathParser;
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
@ -345,4 +346,8 @@ public class TestUtils {
super.update(mode, active);
}
}
public static void putLicense(MetaData.Builder builder, License license) {
builder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license));
}
}

View File

@ -44,14 +44,12 @@ import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.ExternalResource;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -170,12 +168,12 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
case SUITE:
if (customSecuritySettingsSource == null) {
customSecuritySettingsSource =
new CustomSecuritySettingsSource(useGeneratedSSLConfig(), createTempDir(), currentClusterScope);
new CustomSecuritySettingsSource(transportSSLEnabled(), createTempDir(), currentClusterScope);
}
break;
case TEST:
customSecuritySettingsSource =
new CustomSecuritySettingsSource(useGeneratedSSLConfig(), createTempDir(), currentClusterScope);
new CustomSecuritySettingsSource(transportSSLEnabled(), createTempDir(), currentClusterScope);
break;
}
}
@ -326,7 +324,7 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
/**
* Allows to control whether ssl key information is auto generated or not on the transport layer
*/
protected boolean useGeneratedSSLConfig() {
protected boolean transportSSLEnabled() {
return randomBoolean();
}
@ -340,8 +338,8 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
private class CustomSecuritySettingsSource extends SecuritySettingsSource {
private CustomSecuritySettingsSource(boolean useGeneratedSSLConfig, Path configDir, Scope scope) {
super(maxNumberOfNodes(), useGeneratedSSLConfig, configDir, scope);
private CustomSecuritySettingsSource(boolean sslEnabled, Path configDir, Scope scope) {
super(maxNumberOfNodes(), sslEnabled, configDir, scope);
}
@Override
@ -520,4 +518,8 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
}
return null;
}
protected boolean isTransportSSLEnabled() {
return customSecuritySettingsSource.isSslEnabled();
}
}

View File

@ -79,7 +79,7 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
private final Path parentFolder;
private final String subfolderPrefix;
private final boolean useGeneratedSSLConfig;
private final boolean sslEnabled;
private final boolean hostnameVerificationEnabled;
private final boolean usePEM;
@ -87,15 +87,15 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
* 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 useGeneratedSSLConfig whether ssl key/cert should be auto-generated
* @param sslEnabled whether ssl is enabled
* @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 useGeneratedSSLConfig, Path parentFolder, Scope scope) {
public SecuritySettingsSource(int numOfNodes, boolean sslEnabled, Path parentFolder, Scope scope) {
super(numOfNodes, DEFAULT_SETTINGS);
this.parentFolder = parentFolder;
this.subfolderPrefix = scope.name();
this.useGeneratedSSLConfig = useGeneratedSSLConfig;
this.sslEnabled = sslEnabled;
this.hostnameVerificationEnabled = randomBoolean();
this.usePEM = randomBoolean();
}
@ -203,20 +203,24 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
}
private void addNodeSSLSettings(Settings.Builder builder) {
if (usePEM) {
addSSLSettingsForPEMFiles(builder, "",
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem", "testnode",
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt",
Arrays.asList("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-client-profile.crt",
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/active-directory-ca.crt",
"/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"),
useGeneratedSSLConfig, hostnameVerificationEnabled, false);
if (sslEnabled) {
if (usePEM) {
addSSLSettingsForPEMFiles(builder, "",
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem", "testnode",
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt",
Arrays.asList("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-client-profile.crt",
"/org/elasticsearch/xpack/security/transport/ssl/certs/simple/active-directory-ca.crt",
"/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"),
sslEnabled, hostnameVerificationEnabled, false);
} else {
addSSLSettingsForStore(builder, "", "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks",
"testnode", useGeneratedSSLConfig, hostnameVerificationEnabled, false);
} else {
addSSLSettingsForStore(builder, "", "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks",
"testnode", sslEnabled, hostnameVerificationEnabled, false);
}
} else if (randomBoolean()) {
builder.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), false);
}
}
@ -227,10 +231,10 @@ 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"),
useGeneratedSSLConfig, hostnameVerificationEnabled, true);
sslEnabled, hostnameVerificationEnabled, true);
} else {
addSSLSettingsForStore(builder, prefix, "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks",
"testclient", useGeneratedSSLConfig, hostnameVerificationEnabled, true);
"testclient", sslEnabled, hostnameVerificationEnabled, true);
}
}
@ -241,31 +245,30 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
* @param password the password
*/
public static void addSSLSettingsForStore(Settings.Builder builder, String resourcePathToStore, String password) {
addSSLSettingsForStore(builder, "", resourcePathToStore, password, false, true, true);
addSSLSettingsForStore(builder, "", resourcePathToStore, password, true, true, true);
}
private static void addSSLSettingsForStore(Settings.Builder builder, String prefix, String resourcePathToStore, String password,
boolean useGeneratedSSLConfig, boolean hostnameVerificationEnabled,
boolean sslEnabled, boolean hostnameVerificationEnabled,
boolean transportClient) {
Path store = resolveResourcePath(resourcePathToStore);
if (transportClient == false) {
builder.put(prefix + "xpack.security.http.ssl.enabled", false);
}
builder.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), sslEnabled);
builder.put(prefix + "xpack.ssl.verification_mode", hostnameVerificationEnabled ? "full" : "certificate");
if (useGeneratedSSLConfig == false) {
builder.put(prefix + "xpack.ssl.keystore.path", store);
if (transportClient) {
// continue using insecure settings for clients until we figure out what to do there...
builder.put(prefix + "xpack.ssl.keystore.password", password);
} else {
addSecureSettings(builder, secureSettings ->
secureSettings.setString(prefix + "xpack.ssl.keystore.secure_password", password));
}
builder.put(prefix + "xpack.ssl.keystore.path", store);
if (transportClient) {
// continue using insecure settings for clients until we figure out what to do there...
builder.put(prefix + "xpack.ssl.keystore.password", password);
} else {
addSecureSettings(builder, secureSettings ->
secureSettings.setString(prefix + "xpack.ssl.keystore.secure_password", password));
}
if (useGeneratedSSLConfig == false && true /*randomBoolean()*/) {
if (randomBoolean()) {
builder.put(prefix + "xpack.ssl.truststore.path", store);
if (transportClient) {
// continue using insecure settings for clients until we figure out what to do there...
@ -278,29 +281,28 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
}
private static void addSSLSettingsForPEMFiles(Settings.Builder builder, String prefix, String keyPath, String password,
String certificatePath, List<String> trustedCertificates, boolean useGeneratedSSLConfig,
String certificatePath, List<String> trustedCertificates, boolean sslEnabled,
boolean hostnameVerificationEnabled, boolean transportClient) {
if (transportClient == false) {
builder.put(prefix + "xpack.security.http.ssl.enabled", false);
}
builder.put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), sslEnabled);
builder.put(prefix + "xpack.ssl.verification_mode", hostnameVerificationEnabled ? "full" : "certificate");
if (useGeneratedSSLConfig == false) {
builder.put(prefix + "xpack.ssl.key", resolveResourcePath(keyPath))
.put(prefix + "xpack.ssl.certificate", resolveResourcePath(certificatePath));
if (transportClient) {
// continue using insecure settings for clients until we figure out what to do there...
builder.put(prefix + "xpack.ssl.key_passphrase", password);
} else {
addSecureSettings(builder, secureSettings ->
secureSettings.setString(prefix + "xpack.ssl.secure_key_passphrase", password));
}
builder.put(prefix + "xpack.ssl.key", resolveResourcePath(keyPath))
.put(prefix + "xpack.ssl.certificate", resolveResourcePath(certificatePath));
if (transportClient) {
// continue using insecure settings for clients until we figure out what to do there...
builder.put(prefix + "xpack.ssl.key_passphrase", password);
} else {
addSecureSettings(builder, secureSettings ->
secureSettings.setString(prefix + "xpack.ssl.secure_key_passphrase", password));
}
if (trustedCertificates.isEmpty() == false) {
builder.put(prefix + "xpack.ssl.certificate_authorities",
Strings.arrayToCommaDelimitedString(resolvePathsToString(trustedCertificates)));
}
if (trustedCertificates.isEmpty() == false) {
builder.put(prefix + "xpack.ssl.certificate_authorities",
Strings.arrayToCommaDelimitedString(resolvePathsToString(trustedCertificates)));
}
}
@ -337,4 +339,8 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
throw new ElasticsearchException("exception while reading the store", e);
}
}
public boolean isSslEnabled() {
return sslEnabled;
}
}

View File

@ -11,6 +11,7 @@ import org.elasticsearch.cluster.ClusterModule;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
@ -60,6 +61,8 @@ import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.authc.TokenMetaData;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -80,10 +83,20 @@ abstract class MlNativeAutodetectIntegTestCase extends SecurityIntegTestCase {
@Override
protected Settings externalClusterClientSettings() {
Path keyStore;
try {
keyStore = PathUtils.get(getClass().getResource("/test-node.jks").toURI());
} catch (URISyntaxException e) {
throw new IllegalStateException("error trying to get keystore path", e);
}
Settings.Builder builder = Settings.builder();
builder.put(NetworkModule.TRANSPORT_TYPE_KEY, Security.NAME4);
builder.put(Security.USER_SETTING.getKey(), "x_pack_rest_user:" + SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING);
builder.put(XPackSettings.MACHINE_LEARNING_ENABLED.getKey(), true);
builder.put("xpack.security.transport.ssl.enabled", true);
builder.put("xpack.security.transport.ssl.keystore.path", keyStore.toAbsolutePath().toString());
builder.put("xpack.security.transport.ssl.keystore.password", "keypass");
builder.put("xpack.security.transport.ssl.verification_mode", "certificate");
return builder.build();
}

View File

@ -26,6 +26,12 @@ public class PkiRealmBootstrapCheckTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Environment env = new Environment(settings);
assertTrue(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
// enable transport tls
settings = Settings.builder().put(settings)
.put("xpack.security.transport.ssl.enabled", true)
.build();
assertFalse(new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)).isFailure());
// disable client auth default

View File

@ -8,18 +8,28 @@ package org.elasticsearch.xpack.security;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import org.elasticsearch.Version;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.license.License;
import org.elasticsearch.license.TestUtils;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
@ -191,4 +201,38 @@ public class SecurityTests extends ESTestCase {
assertThat(filter, hasItem(Security.setting("authc.realms.*.ssl.truststore.path")));
assertThat(filter, hasItem(Security.setting("authc.realms.*.ssl.truststore.algorithm")));
}
public void testTLSJoinValidatorOnDisabledSecurity() throws Exception {
Settings disabledSettings = Settings.builder().put("xpack.security.enabled", false).build();
createComponents(disabledSettings);
BiConsumer<DiscoveryNode, ClusterState> joinValidator = security.getJoinValidator();
assertNull(joinValidator);
}
public void testTLSJoinValidator() throws Exception {
createComponents(Settings.EMPTY);
BiConsumer<DiscoveryNode, ClusterState> joinValidator = security.getJoinValidator();
assertNotNull(joinValidator);
DiscoveryNode node = new DiscoveryNode("foo", buildNewFakeTransportAddress(), Version.CURRENT);
joinValidator.accept(node, ClusterState.builder(ClusterName.DEFAULT).build());
assertTrue(joinValidator instanceof Security.ValidateTLSOnJoin);
int numIters = randomIntBetween(1,10);
for (int i = 0; i < numIters; i++) {
boolean tlsOn = randomBoolean();
Security.ValidateTLSOnJoin validator = new Security.ValidateTLSOnJoin(tlsOn);
MetaData.Builder builder = MetaData.builder();
License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24));
TestUtils.putLicense(builder, license);
ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metaData(builder.build()).build();
EnumSet<License.OperationMode> productionModes = EnumSet.of(License.OperationMode.GOLD, License.OperationMode.PLATINUM,
License.OperationMode.STANDARD);
if (productionModes.contains(license.operationMode()) && tlsOn == false) {
IllegalStateException ise = expectThrows(IllegalStateException.class, () -> validator.accept(node, state));
assertEquals("TLS setup is required for license type [" + license.operationMode().name() + "]", ise.getMessage());
} else {
validator.accept(node, state);
}
validator.accept(node, ClusterState.builder(ClusterName.DEFAULT).metaData(MetaData.builder().build()).build());
}
}
}

View File

@ -69,14 +69,14 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
private static final String SECOND_CLUSTER_NODE_PREFIX = "node_cluster2_";
private static InternalTestCluster cluster2;
private static boolean useGeneratedSSL;
private static boolean useSSL;
private Node tribeNode;
private Client tribeClient;
@BeforeClass
public static void setupSSL() {
useGeneratedSSL = randomBoolean();
useSSL = randomBoolean();
}
@Override
@ -84,7 +84,7 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
super.setUp();
if (cluster2 == null) {
SecuritySettingsSource cluster2SettingsSource =
new SecuritySettingsSource(defaultMaxNumberOfNodes(), useGeneratedSSL, createTempDir(), Scope.SUITE) {
new SecuritySettingsSource(defaultMaxNumberOfNodes(), useSSL, createTempDir(), Scope.SUITE) {
@Override
public Settings nodeSettings(int nodeOrdinal) {
Settings.Builder builder = Settings.builder()
@ -118,8 +118,8 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
}
@Override
public boolean useGeneratedSSLConfig() {
return useGeneratedSSL;
public boolean transportSSLEnabled() {
return useSSL;
}
@AfterClass
@ -216,7 +216,7 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
private void setupTribeNode(Settings settings) throws Exception {
SecuritySettingsSource cluster2SettingsSource =
new SecuritySettingsSource(1, useGeneratedSSL, createTempDir(), Scope.TEST) {
new SecuritySettingsSource(1, useSSL, createTempDir(), Scope.TEST) {
@Override
public Settings nodeSettings(int nodeOrdinal) {
return Settings.builder()

View File

@ -35,7 +35,6 @@ import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import static org.elasticsearch.test.SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
@ -79,7 +78,7 @@ public class AuditTrailTests extends SecurityIntegTestCase {
}
@Override
public boolean useGeneratedSSLConfig() {
public boolean transportSSLEnabled() {
return true;
}

View File

@ -84,6 +84,7 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
public static final String SECOND_CLUSTER_NODE_PREFIX = "remote_" + SUITE_CLUSTER_NODE_PREFIX;
private static boolean remoteIndexing;
private static boolean useSSL;
private static InternalTestCluster remoteCluster;
private static Settings remoteSettings;
@ -99,6 +100,7 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
@BeforeClass
public static void configureBeforeClass() {
useSSL = randomBoolean();
remoteIndexing = randomBoolean();
if (remoteIndexing == false) {
remoteSettings = Settings.EMPTY;
@ -114,6 +116,11 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
remoteSettings = null;
}
@Override
protected boolean transportSSLEnabled() {
return useSSL;
}
@Before
public void initializeRemoteClusterIfNecessary() throws Exception {
if (remoteIndexing == false) {
@ -131,11 +138,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 useGeneratedSSL = useSecurity && randomBoolean();
logger.info("--> remote indexing enabled. security enabled: [{}], SSL enabled: [{}], nodes: [{}]", useSecurity, useGeneratedSSL,
final boolean remoteUseSSL = useSecurity && useSSL;
logger.info("--> remote indexing enabled. security enabled: [{}], SSL enabled: [{}], nodes: [{}]", useSecurity, useSSL,
numNodes);
SecuritySettingsSource cluster2SettingsSource =
new SecuritySettingsSource(numNodes, useGeneratedSSL, createTempDir(), Scope.SUITE) {
new SecuritySettingsSource(numNodes, useSSL, createTempDir(), Scope.SUITE) {
@Override
public Settings nodeSettings(int nodeOrdinal) {
Settings.Builder builder = Settings.builder()
@ -192,8 +199,9 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
.put("xpack.security.audit.index.client.xpack.security.user", SecuritySettingsSource.TEST_USER_NAME + ":" +
SecuritySettingsSource.TEST_PASSWORD);
if (useGeneratedSSL == false) {
if (remoteUseSSL) {
cluster2SettingsSource.addClientSSLSettings(builder, "xpack.security.audit.index.client.");
builder.put("xpack.security.audit.index.client.xpack.security.transport.ssl.enabled", true);
}
if (useSecurity == false && builder.get(NetworkModule.TRANSPORT_TYPE_KEY) == null) {
builder.put("xpack.security.audit.index.client." + NetworkModule.TRANSPORT_TYPE_KEY, getTestTransportType());

View File

@ -28,7 +28,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.StreamSupport;
@ -50,13 +49,13 @@ public class RemoteIndexAuditTrailStartingTests extends SecurityIntegTestCase {
private InternalTestCluster remoteCluster;
private final boolean useGeneratedSSL = randomBoolean();
private final boolean sslEnabled = randomBoolean();
private final boolean localAudit = randomBoolean();
private final String outputs = randomFrom("index", "logfile", "index,logfile");
@Override
public boolean useGeneratedSSLConfig() {
return useGeneratedSSL;
public boolean transportSSLEnabled() {
return sslEnabled;
}
@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, useGeneratedSSL, createTempDir(), Scope.TEST) {
new SecuritySettingsSource(numNodes, sslEnabled, createTempDir(), Scope.TEST) {
@Override
public Settings nodeSettings(int nodeOrdinal) {
Settings.Builder builder = Settings.builder()
@ -104,6 +103,7 @@ public class RemoteIndexAuditTrailStartingTests extends SecurityIntegTestCase {
.put("xpack.security.audit.index.client.xpack.security.user", TEST_USER_NAME + ":" + TEST_PASSWORD);
addClientSSLSettings(builder, "xpack.security.audit.index.client.");
builder.put("xpack.security.audit.index.client.xpack.security.transport.ssl.enabled", sslEnabled);
return builder.build();
}
};

View File

@ -83,8 +83,8 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
}
@Override
public boolean useGeneratedSSLConfig() {
return true;
protected boolean transportSSLEnabled() {
return false;
}
public void testUserImpersonation() throws Exception {

View File

@ -17,7 +17,6 @@ import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.authc.support.CharArrays;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import java.nio.charset.StandardCharsets;
@ -52,9 +51,8 @@ public class ESNativeMigrateToolTests extends NativeRealmIntegTestCase {
}
@Override
protected boolean useGeneratedSSLConfig() {
// don't use autogenerated when we expect a different cert
return useSSL == false;
protected boolean transportSSLEnabled() {
return useSSL;
}
@Override

View File

@ -15,7 +15,6 @@ import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.MockSecureSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
@ -73,8 +72,8 @@ public class PkiAuthenticationTests extends SecurityIntegTestCase {
}
@Override
protected boolean useGeneratedSSLConfig() {
return false;
protected boolean transportSSLEnabled() {
return true;
}
public void testTransportClientCanAuthenticateViaPki() {

View File

@ -66,8 +66,8 @@ public class PkiOptionalClientAuthTests extends SecurityIntegTestCase {
}
@Override
protected boolean useGeneratedSSLConfig() {
return false;
protected boolean transportSSLEnabled() {
return true;
}
public void testRestClientWithoutClientCertificate() throws Exception {

View File

@ -13,8 +13,6 @@ 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.common.unit.TimeValue;
import org.elasticsearch.discovery.MasterNotDiscoveredException;
import org.elasticsearch.node.MockNode;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeValidationException;
@ -42,7 +40,6 @@ import java.nio.file.Path;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.test.SecuritySettingsSource.addSSLSettingsForStore;
import static org.elasticsearch.xpack.security.test.SecurityTestUtils.writeFile;
import static org.hamcrest.CoreMatchers.equalTo;
@ -57,10 +54,9 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase
randomClientPort = randomIntBetween(49000, 65500); // ephemeral port
}
// don't use it here to simplify the settings we need
@Override
public boolean useGeneratedSSLConfig() {
return false;
public boolean transportSSLEnabled() {
return true;
}
@Override

View File

@ -87,8 +87,8 @@ public class DNSOnlyHostnameVerificationTests extends SecurityIntegTestCase {
}
@Override
public boolean useGeneratedSSLConfig() {
return false;
public boolean transportSSLEnabled() {
return true;
}
@Override

View File

@ -23,8 +23,8 @@ public class IPHostnameVerificationTests extends SecurityIntegTestCase {
Path keystore;
@Override
protected boolean useGeneratedSSLConfig() {
return false;
protected boolean transportSSLEnabled() {
return true;
}
@Override

View File

@ -42,6 +42,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
MockSecureSettings secureSettings = new MockSecureSettings();
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings = Settings.builder()
.put("xpack.security.transport.ssl.enabled", true)
.put("xpack.ssl.keystore.path", testnodeStore)
.setSecureSettings(secureSettings)
.put("path.home", createTempDir())
@ -51,12 +52,13 @@ public class SecurityNetty4TransportTests extends ESTestCase {
}
private SecurityNetty4Transport createTransport() {
return createTransport(Settings.EMPTY);
return createTransport(Settings.builder().put("xpack.security.transport.ssl.enabled", true).build());
}
private SecurityNetty4Transport createTransport(Settings additionalSettings) {
final Settings settings =
Settings.builder()
.put("xpack.security.transport.ssl.enabled", true)
.put(additionalSettings)
.build();
return new SecurityNetty4Transport(
@ -185,6 +187,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
secureSettings.setString("xpack.security.transport.ssl.keystore.secure_password", "testnode");
secureSettings.setString("xpack.ssl.truststore.secure_password", "truststore-testnode-only");
Settings.Builder builder = Settings.builder()
.put("xpack.security.transport.ssl.enabled", true)
.put("xpack.security.transport.ssl.keystore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.transport.ssl.client_authentication", "none")

View File

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

View File

@ -66,8 +66,8 @@ public class EllipticCurveSSLTests extends SecurityIntegTestCase {
}
@Override
protected boolean useGeneratedSSLConfig() {
return false;
protected boolean transportSSLEnabled() {
return true;
}
public void testConnection() throws Exception {

View File

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

View File

@ -73,8 +73,8 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
}
@Override
protected boolean useGeneratedSSLConfig() {
return false;
protected boolean transportSSLEnabled() {
return true;
}
private TransportClient createTransportClient(Settings additionalSettings) {
@ -82,6 +82,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
.put(transportClientSettings().filter(s -> s.startsWith("xpack.ssl") == false))
.put("node.name", "programmatic_transport_client")
.put("cluster.name", internalCluster().getClusterName())
.put("xpack.security.transport.ssl.enabled", true)
.put(additionalSettings)
.build();
return new TestXPackTransportClient(settings);
@ -105,6 +106,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
public void testThatStandardTransportClientCanConnectToNoClientAuthProfile() throws Exception {
try(TransportClient transportClient = new TestXPackTransportClient(Settings.builder()
.put(transportClientSettings())
.put("xpack.security.transport.ssl.enabled", true)
.put("node.name", "programmatic_transport_client")
.put("cluster.name", internalCluster().getClusterName())
.build())) {
@ -247,6 +249,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
Settings settings = Settings.builder()
.put(Security.USER_SETTING.getKey(), TEST_USER_NAME + ":" + TEST_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")
@ -254,7 +257,6 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
try (TransportClient transportClient = new TestXPackTransportClient(settings)) {
transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(),
getProfilePort("no_client_auth")));
assertGreenClusterState(transportClient);
}
}
@ -268,6 +270,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
Settings settings = Settings.builder()
.put(Security.USER_SETTING.getKey(), TEST_USER_NAME + ":" + TEST_PASSWORD)
.put("cluster.name", internalCluster().getClusterName())
.put("xpack.security.transport.ssl.enabled", true)
.put("xpack.ssl.client_authentication", SSLClientAuth.REQUIRED)
.put("xpack.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
@ -292,6 +295,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
Settings settings = Settings.builder()
.put(Security.USER_SETTING.getKey(), TEST_USER_NAME + ":" + TEST_PASSWORD)
.put("cluster.name", internalCluster().getClusterName())
.put("xpack.security.transport.ssl.enabled", true)
.put("xpack.ssl.client_authentication", SSLClientAuth.REQUIRED)
.put("xpack.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
@ -316,6 +320,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
.put(Security.USER_SETTING.getKey(), TEST_USER_NAME + ":" + TEST_PASSWORD)
.put("cluster.name", internalCluster().getClusterName())
.put("xpack.ssl.client_authentication", SSLClientAuth.REQUIRED)
.put("xpack.security.transport.ssl.enabled", true)
.build();
try (TransportClient transportClient = new TestXPackTransportClient(settings)) {
transportClient.addTransportAddress(randomFrom(internalCluster().getInstance(Transport.class).boundAddress().boundAddresses()));
@ -336,6 +341,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
.put(Security.USER_SETTING.getKey(), TEST_USER_NAME + ":" + TEST_PASSWORD)
.put("cluster.name", internalCluster().getClusterName())
.put("xpack.ssl.client_authentication", SSLClientAuth.REQUIRED)
.put("xpack.security.transport.ssl.enabled", true)
.build();
try (TransportClient transportClient = new TestXPackTransportClient(settings)) {
transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("client")));
@ -356,6 +362,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
.put(Security.USER_SETTING.getKey(), TEST_USER_NAME + ":" + TEST_PASSWORD)
.put("cluster.name", internalCluster().getClusterName())
.put("xpack.ssl.client_authentication", SSLClientAuth.REQUIRED)
.put("xpack.security.transport.ssl.enabled", true)
.build();
try (TransportClient transportClient = new TestXPackTransportClient(settings)) {
transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(),

View File

@ -17,7 +17,7 @@ import org.elasticsearch.test.SecurityIntegTestCase;
public class SslNullCipherTests extends SecurityIntegTestCase {
@Override
public boolean useGeneratedSSLConfig() {
public boolean transportSSLEnabled() {
return true;
}
@ -25,7 +25,7 @@ public class SslNullCipherTests extends SecurityIntegTestCase {
public Settings nodeSettings(int nodeOrdinal) {
Settings settings = super.nodeSettings(nodeOrdinal);
Settings.Builder builder = Settings.builder()
.put(settings.filter((s) -> s.startsWith("xpack.ssl") == false));
.put(settings);
builder.put("xpack.security.transport.ssl.cipher_suites", "TLS_RSA_WITH_NULL_SHA256");
return builder.build();
}
@ -34,7 +34,7 @@ public class SslNullCipherTests extends SecurityIntegTestCase {
public Settings transportClientSettings() {
Settings settings = super.transportClientSettings();
Settings.Builder builder = Settings.builder()
.put(settings.filter((s) -> s.startsWith("xpack.ssl") == false));
.put(settings);
builder.put("xpack.security.transport.ssl.cipher_suites", "TLS_RSA_WITH_NULL_SHA256");
return builder.build();

View File

@ -1,45 +0,0 @@
/*
* 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(), randomAlphaOfLengthBetween(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

@ -1,93 +0,0 @@
/*
* 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.bootstrap.BootstrapContext;
import org.elasticsearch.common.settings.MockSecureSettings;
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, null);
assertTrue(bootstrapCheck.check(new BootstrapContext(Settings.EMPTY, null)).isFailure());
}
public void testSSLBootstrapCheckWithKey() throws Exception {
final String keyPrefix = randomBoolean() ? "security.transport." : "";
MockSecureSettings secureSettings = new MockSecureSettings();
secureSettings.setString("xpack." + keyPrefix + "ssl.secure_key_passphrase", "testclient");
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.certificate",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"))
.setSecureSettings(secureSettings)
.build();
final Environment env = randomBoolean() ? new Environment(settings) : null;
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), env);
assertFalse(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
}
public void testSSLBootstrapCheckWithDefaultCABeingTrusted() throws Exception {
final String keyPrefix = randomBoolean() ? "security.transport." : "";
MockSecureSettings secureSettings = new MockSecureSettings();
secureSettings.setString("xpack." + keyPrefix + "ssl.secure_key_passphrase", "testclient");
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.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())
.setSecureSettings(secureSettings)
.build();
final Environment env = randomBoolean() ? new Environment(settings) : null;
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), env);
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
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), env);
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
}
public void testSSLBootstrapCheckWithDefaultKeyBeingUsed() throws Exception {
final String keyPrefix = randomBoolean() ? "security.transport." : "";
MockSecureSettings secureSettings = new MockSecureSettings();
secureSettings.setString("xpack." + keyPrefix + "ssl.secure_key_passphrase", "testclient");
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.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())
.setSecureSettings(secureSettings)
.build();
final Environment env = randomBoolean() ? new Environment(settings) : null;
SSLBootstrapCheck bootstrapCheck = new SSLBootstrapCheck(new SSLService(settings, env), env);
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
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), env);
assertTrue(bootstrapCheck.check(new BootstrapContext(settings, null)).isFailure());
}
}

View File

@ -56,8 +56,8 @@ public class SSLClientAuthTests extends SecurityIntegTestCase {
}
@Override
protected boolean useGeneratedSSLConfig() {
return false;
protected boolean transportSSLEnabled() {
return true;
}
public void testThatHttpFailsWithoutSslClientAuth() throws IOException {
@ -93,6 +93,7 @@ public class SSLClientAuthTests extends SecurityIntegTestCase {
MockSecureSettings secureSettings = new MockSecureSettings();
secureSettings.setString("xpack.ssl.keystore.secure_password", "testclient-client-profile");
Settings settings = Settings.builder()
.put("xpack.security.transport.ssl.enabled", true)
.put("xpack.ssl.client_authentication", SSLClientAuth.NONE)
.put("xpack.ssl.keystore.path", store)
.setSecureSettings(secureSettings)

View File

@ -219,15 +219,12 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
.setSecureSettings(secureSettings)
.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 BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPreChecks = (trustManager, config) -> {
// trust manager checks
Certificate[] certificates = trustManager.getAcceptedIssuers();
trustedCount.set(certificates.length);
assertTrue(Arrays.stream(trustManager.getAcceptedIssuers())
.anyMatch((cert) -> expectedPrincipal.equals(cert.getSubjectX500Principal())));
};
@ -247,8 +244,6 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks = (updatedTrustManager, config) -> {
assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(5));
assertTrue(Arrays.stream(updatedTrustManager.getAcceptedIssuers())
.anyMatch((cert) -> expectedPrincipal.equals(cert.getSubjectX500Principal())));
};
validateTrustConfigurationIsReloaded(settings, env, trustManagerPreChecks, modifier, trustManagerPostChecks);
@ -267,15 +262,12 @@ 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 BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPreChecks = (trustManager, config) -> {
// trust manager checks
Certificate[] certificates = trustManager.getAcceptedIssuers();
assertThat(certificates.length, is(2));
assertThat(certificates.length, is(1));
assertThat(((X509Certificate)certificates[0]).getSubjectX500Principal().getName(), containsString("Test Client"));
assertTrue(Arrays.stream(trustManager.getAcceptedIssuers())
.anyMatch((cert) -> expectedPrincipal.equals(cert.getSubjectX500Principal())));
};
final Runnable modifier = () -> {
@ -291,10 +283,8 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks = (updatedTrustManager, config) -> {
Certificate[] updatedCerts = updatedTrustManager.getAcceptedIssuers();
assertThat(updatedCerts.length, is(2));
assertThat(updatedCerts.length, is(1));
assertThat(((X509Certificate)updatedCerts[0]).getSubjectX500Principal().getName(), containsString("Test Node"));
assertTrue(Arrays.stream(updatedTrustManager.getAcceptedIssuers())
.anyMatch((cert) -> expectedPrincipal.equals(cert.getSubjectX500Principal())));
};
validateTrustConfigurationIsReloaded(settings, env, trustManagerPreChecks, modifier, trustManagerPostChecks);

View File

@ -85,8 +85,8 @@ public class SSLReloadIntegTests extends SecurityIntegTestCase {
}
@Override
protected boolean useGeneratedSSLConfig() {
return false;
protected boolean transportSSLEnabled() {
return true;
}
public void testThatSSLConfigurationReloadsOnModification() throws Exception {

View File

@ -134,8 +134,8 @@ public class SSLTrustRestrictionsTests extends SecurityIntegTestCase {
}
@Override
protected boolean useGeneratedSSLConfig() {
return false;
protected boolean transportSSLEnabled() {
return true;
}
public void testCertificateWithTrustedNameIsAccepted() throws Exception {

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.bootstrap.BootstrapContext;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.License;
import org.elasticsearch.license.TestUtils;
import org.elasticsearch.test.ESTestCase;
import java.util.EnumSet;
public class TLSLicenseBootstrapCheckTests extends ESTestCase {
public void testBootstrapCheck() throws Exception {
assertTrue(new TLSLicenseBootstrapCheck().check(new BootstrapContext(Settings.EMPTY, MetaData.EMPTY_META_DATA)).isSuccess());
assertTrue(new TLSLicenseBootstrapCheck().check(new BootstrapContext(Settings.builder().put("xpack.security.transport.ssl.enabled"
, randomBoolean()).build(), MetaData.EMPTY_META_DATA)).isSuccess());
int numIters = randomIntBetween(1,10);
for (int i = 0; i < numIters; i++) {
License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24));
EnumSet<License.OperationMode> productionModes = EnumSet.of(License.OperationMode.GOLD, License.OperationMode.PLATINUM,
License.OperationMode.STANDARD);
MetaData.Builder builder = MetaData.builder();
TestUtils.putLicense(builder, license);
MetaData build = builder.build();
if (productionModes.contains(license.operationMode()) == false) {
assertTrue(new TLSLicenseBootstrapCheck().check(new BootstrapContext(
Settings.builder().put("xpack.security.transport.ssl.enabled", true).build(), build)).isSuccess());
} else {
assertTrue(new TLSLicenseBootstrapCheck().check(new BootstrapContext(
Settings.builder().put("xpack.security.transport.ssl.enabled", false).build(), build)).isFailure());
assertEquals("Transport SSL must be enabled for setups with production licenses. Please set " +
"[xpack.security.transport.ssl.enabled] to [true] or disable security by setting " +
"[xpack.security.enabled] to [false]",
new TLSLicenseBootstrapCheck().check(new BootstrapContext(
Settings.builder().put("xpack.security.transport.ssl.enabled", false).build(), build)).getMessage());
}
}
}
}

View File

@ -159,6 +159,7 @@ subprojects {
unicastTransportUri = { seedNode, node, ant -> oldClusterTest.nodes.get(0).transportUri() }
dataDir = { nodeNumber -> oldClusterTest.nodes[1].dataDir }
waitCondition = waitWithAuth
setting 'xpack.security.transport.ssl.enabled', 'true'
setting 'xpack.ssl.keystore.path', 'testnode.jks'
keystoreSetting 'xpack.ssl.keystore.secure_password', 'testnode'
setting 'node.attr.upgraded', 'first'
@ -190,6 +191,7 @@ subprojects {
unicastTransportUri = { seedNode, node, ant -> mixedClusterTest.nodes.get(0).transportUri() }
dataDir = { nodeNumber -> oldClusterTest.nodes[0].dataDir }
waitCondition = waitWithAuth
setting 'xpack.security.transport.ssl.enabled', 'true'
setting 'xpack.ssl.keystore.path', 'testnode.jks'
keystoreSetting 'xpack.ssl.keystore.secure_password', 'testnode'
setting 'xpack.security.authc.token.enabled', 'true'

View File

@ -154,6 +154,8 @@ processTestResources.dependsOn(
importNodeCertificateInClientKeyStore, importClientCertificateInNodeKeyStore
)
integTestCluster.dependsOn(importClientCertificateInNodeKeyStore)
ext.pluginsCount = 1 // we install xpack explicitly
project.rootProject.subprojects.findAll { it.path.startsWith(':plugins:') }.each { subproj ->
// need to get a non-decorated project object, so must re-lookup the project by path