Skip to content

Commit

Permalink
Enforce Transport TLS check on all licenses. (elastic#79602)
Browse files Browse the repository at this point in the history
Historically, we haven't enabled the transport TLS bootstrap
check for trial licenses because:
- We wanted to make the experience of trial license users as
easy as possible and configuring transport TLS was considered
cumbersome.
- Trial licenses have a limited lifetime so that minimizes the
impact of this potentially insecure configuration.

With security on by default project we are:
- Enabling security by default for basic and trial licenses
- We offer an easy, automated way for users to configure
transport TLS
- Enabling by default this bootstrap check for basic licenses.

It doesn't make much sense for us to enforce the bootstrap check
on basic licenses but not on trial and given that the concerns
that were driving the original decision are not there or have been
partly alleviated, this commit changes our behavior so that we
enable the TLS bootstrap check regardless of the license level.
  • Loading branch information
jkakavas authored and Adam Locke committed Oct 28, 2021
1 parent c35c14f commit cd542ae
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 286 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.component.Lifecycle;
Expand All @@ -25,7 +24,6 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.discovery.DiscoveryModule;
import org.elasticsearch.env.Environment;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
Expand Down Expand Up @@ -243,15 +241,7 @@ public void registerLicense(final PutLicenseRequest request, final ActionListene
// because the defaults there mean that security can be "off", even if the setting is "on"
// BUT basic licenses are explicitly excluded earlier in this method, so we don't need to worry
if (XPackSettings.SECURITY_ENABLED.get(settings)) {
// TODO we should really validate that all nodes have xpack installed and are consistently configured but this
// should happen on a different level and not in this code
if (XPackLicenseState.isTransportTlsRequired(newLicense, settings)
&& XPackSettings.TRANSPORT_SSL_ENABLED.get(settings) == false
&& isProductionMode(settings, clusterService.localNode())) {
// security is on but TLS is not configured we gonna fail the entire request and throw an exception
throw new IllegalStateException("Cannot install a [" + newLicense.operationMode() +
"] license unless TLS is configured or security is disabled");
} else if (XPackSettings.FIPS_MODE_ENABLED.get(settings)
if (XPackSettings.FIPS_MODE_ENABLED.get(settings)
&& false == XPackLicenseState.isFipsAllowedForOperationMode(newLicense.operationMode())) {
throw new IllegalStateException("Cannot install a [" + newLicense.operationMode() +
"] license unless FIPS mode is disabled");
Expand Down Expand Up @@ -583,15 +573,6 @@ static License getLicense(final LicensesMetadata metadata) {
return null;
}

private static boolean isProductionMode(Settings settings, DiscoveryNode localNode) {
final boolean singleNodeDisco = "single-node".equals(DiscoveryModule.DISCOVERY_TYPE_SETTING.get(settings));
return singleNodeDisco == false && isBoundToLoopback(localNode) == false;
}

private static boolean isBoundToLoopback(DiscoveryNode localNode) {
return localNode.getAddress().address().getAddress().isLoopbackAddress();
}

private static List<License.LicenseType> getAllowableUploadTypes() {
return Stream.of(License.LicenseType.values())
.filter(t -> t != License.LicenseType.BASIC)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.logging.HeaderWarning;
import org.elasticsearch.common.logging.LoggerMessageFormat;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.license.License.OperationMode;
import org.elasticsearch.xpack.core.XPackField;
import org.elasticsearch.xpack.core.XPackSettings;

import java.util.Collections;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -461,25 +459,6 @@ public static boolean isFipsAllowedForOperationMode(final OperationMode operatio
return isAllowedByOperationMode(operationMode, OperationMode.PLATINUM);
}

public static boolean isTransportTlsRequired(License license, Settings settings) {
if (license == null) {
return false;
}
switch (license.operationMode()) {
case STANDARD:
case GOLD:
case PLATINUM:
case ENTERPRISE:
case BASIC:
return XPackSettings.SECURITY_ENABLED.get(settings);
case MISSING:
case TRIAL:
return false;
default:
throw new AssertionError("unknown operation mode [" + license.operationMode() + "]");
}
}

public static boolean isCcrAllowedForOperationMode(final OperationMode operationMode) {
return isAllowedByOperationMode(operationMode, OperationMode.PLATINUM);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.core.ssl;

import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.bootstrap.BootstrapContext;
import org.elasticsearch.xpack.core.XPackSettings;

/**
* Bootstrap check to ensure that if we are starting up with security enabled, transport TLS is enabled
*/
public final class TransportTLSBootstrapCheck implements BootstrapCheck {
@Override
public BootstrapCheckResult check(BootstrapContext context) {
assert XPackSettings.SECURITY_ENABLED.get(context.settings())
: "Bootstrap check should not be installed unless security is enabled";
if (XPackSettings.TRANSPORT_SSL_ENABLED.get(context.settings()) == false) {
return BootstrapCheckResult.failure(
"Transport SSL must be enabled if security is enabled. "
+ "Please set [xpack.security.transport.ssl.enabled] to [true] or disable security by setting "
+ "[xpack.security.enabled] to [false]"
);
}
return BootstrapCheckResult.success();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
Expand Down Expand Up @@ -56,43 +55,6 @@ public void testApplyLicenseInDevMode() throws Exception {
verify(clusterService, times(2)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
}

public void testApplyLicenseInProdMode() throws Exception {
final String licenseType = randomFrom("GOLD", "PLATINUM");
License newLicense = TestUtils.generateSignedLicense(licenseType, TimeValue.timeValueHours(24L));
PutLicenseRequest request = new PutLicenseRequest();
request.acknowledge(true);
request.license(newLicense);
Settings settings = Settings.builder().put("xpack.security.enabled", true).build();
XPackLicenseState licenseState = new XPackLicenseState(() -> 0);
inetAddress = TransportAddress.META_ADDRESS;

setInitialState(null, licenseState, settings);
licenseService.start();
PlainActionFuture<PutLicenseResponse> responseFuture = new PlainActionFuture<>();
IllegalStateException e = expectThrows(IllegalStateException.class, () -> licenseService.registerLicense(request, responseFuture));
assertThat(e.getMessage(),
containsString("Cannot install a [" + licenseType + "] license unless TLS is configured or security is disabled"));

settings = Settings.builder().put("xpack.security.enabled", false).build();
licenseService.stop();
licenseState = new XPackLicenseState(() -> 0);
setInitialState(null, licenseState, settings);
licenseService.start();
licenseService.registerLicense(request, responseFuture);
verify(clusterService).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));

settings = Settings.builder()
.put("xpack.security.enabled", true)
.put("xpack.security.transport.ssl.enabled", true)
.build();
licenseService.stop();
licenseState = new XPackLicenseState(() -> 0);
setInitialState(null, licenseState, settings);
licenseService.start();
licenseService.registerLicense(request, responseFuture);
verify(clusterService, times(2)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
}

@Override
protected DiscoveryNode getLocalNode() {
return new DiscoveryNode("localnode", new TransportAddress(inetAddress, randomIntBetween(9300, 9399)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,58 +42,6 @@ public void testAcknowledgment() throws Exception {
verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
}

public void testRejectUpgradeToProductionWithoutTLS() throws Exception {
XPackLicenseState licenseState = TestUtils.newTestLicenseState();
setInitialState(TestUtils.generateSignedLicense("trial", timeValueHours(2)), licenseState, Settings.EMPTY);
licenseService.start();
// try installing a signed license
License signedLicense = TestUtils.generateSignedLicense("platinum", 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("Cannot install a [PLATINUM] license unless TLS is configured or security is disabled", ise.getMessage());
}

public void testUpgradeToProductionWithoutTLSAndSecurityDisabled() throws Exception {
XPackLicenseState licenseState = TestUtils.newTestLicenseState();
setInitialState(TestUtils.generateSignedLicense("trial", timeValueHours(2)), licenseState, Settings.builder()
.put("xpack.security.enabled", false).build());
licenseService.start();
// try installing a signed license
License signedLicense = TestUtils.generateSignedLicense("platinum", 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 = TestUtils.newTestLicenseState();
setInitialState(TestUtils.generateSignedLicense("trial", 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", 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;
Expand Down
Loading

0 comments on commit cd542ae

Please sign in to comment.