diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java
index 62ffd76e8ea..e39b5b7dcc1 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java
@@ -780,22 +780,4 @@ public class License implements ToXContentObject {
}
}
- /**
- * Returns true
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());
-
- }
- }
}
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java
index 837caf2da07..f750d1349a0 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java
@@ -218,10 +218,13 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
}
}
+ // This check would be incorrect if "basic" licenses were allowed here
+ // 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 (newLicense.isProductionLicense()
+ 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
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java
index 131069d27f6..e206ed3db51 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java
@@ -282,7 +282,7 @@ public class XPackLicenseState {
public XPackLicenseState(Settings settings) {
this.listeners = new CopyOnWriteArrayList<>();
this.isSecurityEnabled = XPackSettings.SECURITY_ENABLED.get(settings);
- this.isSecurityExplicitlyEnabled = isSecurityEnabled && settings.hasValue(XPackSettings.SECURITY_ENABLED.getKey());
+ this.isSecurityExplicitlyEnabled = isSecurityEnabled && isSecurityExplicitlyEnabled(settings);
}
private XPackLicenseState(XPackLicenseState xPackLicenseState) {
@@ -292,6 +292,10 @@ public class XPackLicenseState {
this.status = xPackLicenseState.status;
}
+ private static boolean isSecurityExplicitlyEnabled(Settings settings) {
+ return settings.hasValue(XPackSettings.SECURITY_ENABLED.getKey());
+ }
+
/**
* Updates the current state of the license, which will change what features are available.
*
@@ -727,6 +731,25 @@ public class XPackLicenseState {
return false;
}
+ public static boolean isTransportTlsRequired(License license, Settings settings) {
+ if (license == null) {
+ return false;
+ }
+ switch (license.operationMode()) {
+ case STANDARD:
+ case GOLD:
+ case PLATINUM:
+ return XPackSettings.SECURITY_ENABLED.get(settings);
+ case BASIC:
+ return XPackSettings.SECURITY_ENABLED.get(settings) && isSecurityExplicitlyEnabled(settings);
+ case MISSING:
+ case TRIAL:
+ return false;
+ default:
+ throw new AssertionError("unknown operation mode [" + license.operationMode() + "]");
+ }
+ }
+
private static boolean isSecurityEnabled(final OperationMode mode, final boolean isSecurityExplicitlyEnabled,
final boolean isSecurityEnabled) {
switch (mode) {
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TLSLicenseBootstrapCheck.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TLSLicenseBootstrapCheck.java
index 6f6592bbdfc..a042aeb4a23 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TLSLicenseBootstrapCheck.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TLSLicenseBootstrapCheck.java
@@ -9,6 +9,7 @@ import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.bootstrap.BootstrapContext;
import org.elasticsearch.license.License;
import org.elasticsearch.license.LicenseService;
+import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.xpack.core.XPackSettings;
/**
@@ -19,10 +20,11 @@ public final class TLSLicenseBootstrapCheck implements BootstrapCheck {
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]");
+ if (XPackLicenseState.isTransportTlsRequired(license, context.settings())) {
+ return BootstrapCheckResult.failure("Transport SSL must be enabled if security is enabled on a [" +
+ license.operationMode().description() + "] license. " +
+ "Please set [xpack.security.transport.ssl.enabled] to [true] or disable security by setting " +
+ "[xpack.security.enabled] to [false]");
}
}
return BootstrapCheckResult.success();
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/TLSLicenseBootstrapCheckTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/TLSLicenseBootstrapCheckTests.java
index ac73418800c..3cb14180930 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/TLSLicenseBootstrapCheckTests.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/TLSLicenseBootstrapCheckTests.java
@@ -5,40 +5,115 @@
*/
package org.elasticsearch.xpack.core.ssl;
+import org.elasticsearch.bootstrap.BootstrapCheck;
+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.License.OperationMode;
import org.elasticsearch.license.TestUtils;
import org.elasticsearch.test.AbstractBootstrapCheckTestCase;
-import java.util.EnumSet;
-
public class TLSLicenseBootstrapCheckTests extends AbstractBootstrapCheckTestCase {
- public void testBootstrapCheck() throws Exception {
+ public void testBootstrapCheckOnEmptyMetadata() {
assertTrue(new TLSLicenseBootstrapCheck().check(emptyContext).isSuccess());
assertTrue(new TLSLicenseBootstrapCheck().check(createTestContext(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 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(createTestContext(
- Settings.builder().put("xpack.security.transport.ssl.enabled", true).build(), build)).isSuccess());
- } else {
- assertTrue(new TLSLicenseBootstrapCheck().check(createTestContext(
- 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(createTestContext(
- Settings.builder().put("xpack.security.transport.ssl.enabled", false).build(), build)).getMessage());
- }
+ , randomBoolean()).build(), MetaData.EMPTY_META_DATA)).isSuccess());
+ }
+
+ public void testBootstrapCheckFailureOnPremiumLicense() throws Exception {
+ final OperationMode mode = randomFrom(OperationMode.PLATINUM, OperationMode.GOLD, OperationMode.STANDARD);
+ final Settings.Builder settings = Settings.builder();
+ if (randomBoolean()) {
+ // randomise between default-false & explicit-false
+ settings.put("xpack.security.transport.ssl.enabled", false);
+ }
+ if (randomBoolean()) {
+ // randomise between default-true & explicit-true
+ settings.put("xpack.security.enabled", true);
+ }
+
+ final BootstrapCheck.BootstrapCheckResult result = runBootstrapCheck(mode, settings);
+ assertTrue("Expected bootstrap failure", result.isFailure());
+ assertEquals("Transport SSL must be enabled if security is enabled on a [" + mode.description() + "] license. Please set " +
+ "[xpack.security.transport.ssl.enabled] to [true] or disable security by setting " +
+ "[xpack.security.enabled] to [false]",
+ result.getMessage());
+ }
+
+ public void testBootstrapCheckSucceedsWithTlsEnabledOnPremiumLicense() throws Exception {
+ final OperationMode mode = randomFrom(OperationMode.PLATINUM, OperationMode.GOLD, OperationMode.STANDARD);
+ final Settings.Builder settings = Settings.builder().put("xpack.security.transport.ssl.enabled", true);
+ final BootstrapCheck.BootstrapCheckResult result = runBootstrapCheck(mode, settings);
+ assertSuccess(result);
+ }
+
+ public void testBootstrapCheckFailureOnBasicLicense() throws Exception {
+ final Settings.Builder settings = Settings.builder().put("xpack.security.enabled", true);
+ if (randomBoolean()) {
+ // randomise between default-false & explicit-false
+ settings.put("xpack.security.transport.ssl.enabled", false);
+ }
+ final BootstrapCheck.BootstrapCheckResult result = runBootstrapCheck(OperationMode.BASIC, settings);
+ assertTrue("Expected bootstrap failure", result.isFailure());
+ assertEquals("Transport SSL must be enabled if security is enabled on a [basic] license. Please set " +
+ "[xpack.security.transport.ssl.enabled] to [true] or disable security by setting " +
+ "[xpack.security.enabled] to [false]",
+ result.getMessage());
+ }
+
+ public void testBootstrapSucceedsIfSecurityIsNotEnabledOnBasicLicense() throws Exception {
+ final Settings.Builder settings = Settings.builder();
+ if (randomBoolean()) {
+ // randomise between default-false & explicit-false
+ settings.put("xpack.security.enabled", false);
+ }
+ if (randomBoolean()) {
+ // it does not matter whether or not this is set, as security is not enabled.
+ settings.put("xpack.security.transport.ssl.enabled", randomBoolean());
+ }
+ final BootstrapCheck.BootstrapCheckResult result = runBootstrapCheck(OperationMode.BASIC, settings);
+ assertSuccess(result);
+ }
+
+ public void testBootstrapSucceedsIfTlsIsEnabledOnBasicLicense() throws Exception {
+ final Settings.Builder settings = Settings.builder().put("xpack.security.transport.ssl.enabled", true);
+ if (randomBoolean()) {
+ // it does not matter whether or not this is set, as TLS is enabled.
+ settings.put("xpack.security.enabled", randomBoolean());
+ }
+ final BootstrapCheck.BootstrapCheckResult result = runBootstrapCheck(OperationMode.BASIC, settings);
+ assertSuccess(result);
+ }
+
+ public void testBootstrapCheckAlwaysSucceedsOnTrialLicense() throws Exception {
+ final Settings.Builder settings = Settings.builder();
+ if (randomBoolean()) {
+ // it does not matter whether this is set, or to which value.
+ settings.put("xpack.security.enabled", randomBoolean());
+ }
+ if (randomBoolean()) {
+ // it does not matter whether this is set, or to which value.
+ settings.put("xpack.security.transport.ssl.enabled", randomBoolean());
+ }
+ final BootstrapCheck.BootstrapCheckResult result = runBootstrapCheck(OperationMode.TRIAL, settings);
+ assertSuccess(result);
+ }
+
+ public BootstrapCheck.BootstrapCheckResult runBootstrapCheck(OperationMode mode, Settings.Builder settings) throws Exception {
+ final License license = TestUtils.generateSignedLicense(mode.description(), TimeValue.timeValueHours(24));
+ MetaData.Builder builder = MetaData.builder();
+ TestUtils.putLicense(builder, license);
+ MetaData metaData = builder.build();
+ final BootstrapContext context = createTestContext(settings.build(), metaData);
+ return new TLSLicenseBootstrapCheck().check(context);
+ }
+
+ public void assertSuccess(BootstrapCheck.BootstrapCheckResult result) {
+ if (result.isFailure()) {
+ fail("Bootstrap check failed unexpectedly: " + result.getMessage());
}
}
+
}
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
index 569293af3b5..9ba3bdab21f 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
@@ -258,8 +258,8 @@ import static java.util.Collections.singletonList;
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETTING;
import static org.elasticsearch.xpack.core.XPackSettings.API_KEY_SERVICE_ENABLED_SETTING;
import static org.elasticsearch.xpack.core.XPackSettings.HTTP_SSL_ENABLED;
-import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_MAIN_INDEX_FORMAT;
import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS;
+import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_MAIN_INDEX_FORMAT;
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_MAIN_TEMPLATE_7;
public class Security extends Plugin implements ActionPlugin, IngestPlugin, NetworkPlugin, ClusterPlugin,
@@ -985,7 +985,7 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
public BiConsumer getJoinValidator() {
if (enabled) {
return new ValidateTLSOnJoin(XPackSettings.TRANSPORT_SSL_ENABLED.get(settings),
- DiscoveryModule.DISCOVERY_TYPE_SETTING.get(settings))
+ DiscoveryModule.DISCOVERY_TYPE_SETTING.get(settings), settings)
.andThen(new ValidateUpgradedSecurityIndex())
.andThen(new ValidateLicenseCanBeDeserialized())
.andThen(new ValidateLicenseForFIPS(XPackSettings.FIPS_MODE_ENABLED.get(settings)));
@@ -996,18 +996,21 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
static final class ValidateTLSOnJoin implements BiConsumer {
private final boolean isTLSEnabled;
private final String discoveryType;
+ private final Settings settings;
- ValidateTLSOnJoin(boolean isTLSEnabled, String discoveryType) {
+ ValidateTLSOnJoin(boolean isTLSEnabled, String discoveryType, Settings settings) {
this.isTLSEnabled = isTLSEnabled;
this.discoveryType = discoveryType;
+ this.settings = settings;
}
@Override
public void accept(DiscoveryNode node, ClusterState state) {
License license = LicenseService.getLicense(state.metaData());
- if (license != null && license.isProductionLicense() &&
- isTLSEnabled == false && "single-node".equals(discoveryType) == false) {
- throw new IllegalStateException("TLS setup is required for license type [" + license.operationMode().name() + "]");
+ if (isTLSEnabled == false && "single-node".equals(discoveryType) == false
+ && XPackLicenseState.isTransportTlsRequired(license, settings)) {
+ throw new IllegalStateException("Transport TLS ([" + XPackSettings.TRANSPORT_SSL_ENABLED.getKey() +
+ "]) is required for license type [" + license.operationMode().description() + "] when security is enabled");
}
}
}
diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java
index 47484dcbce2..2a2178a0bf7 100644
--- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java
+++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java
@@ -54,7 +54,6 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -67,9 +66,8 @@ import java.util.stream.Collectors;
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETTING;
import static org.elasticsearch.discovery.DiscoveryModule.ZEN2_DISCOVERY_TYPE;
-import static org.elasticsearch.discovery.DiscoveryModule.ZEN_DISCOVERY_TYPE;
-import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_MAIN_INDEX_FORMAT;
import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS;
+import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_MAIN_INDEX_FORMAT;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
@@ -254,17 +252,45 @@ public class SecurityTests extends ESTestCase {
int numIters = randomIntBetween(1, 10);
for (int i = 0; i < numIters; i++) {
boolean tlsOn = randomBoolean();
- String discoveryType = randomFrom("single-node", ZEN_DISCOVERY_TYPE, ZEN2_DISCOVERY_TYPE, randomAlphaOfLength(4));
- Security.ValidateTLSOnJoin validator = new Security.ValidateTLSOnJoin(tlsOn, discoveryType);
+ boolean securityExplicitlyEnabled = randomBoolean();
+ String discoveryType = randomFrom("single-node", ZEN2_DISCOVERY_TYPE, ZEN2_DISCOVERY_TYPE, randomAlphaOfLength(4));
+
+ final Settings settings;
+ if (securityExplicitlyEnabled) {
+ settings = Settings.builder().put("xpack.security.enabled", true).build();
+ } else {
+ settings = Settings.EMPTY;
+ }
+ Security.ValidateTLSOnJoin validator = new Security.ValidateTLSOnJoin(tlsOn, discoveryType, settings);
MetaData.Builder builder = MetaData.builder();
- License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24));
+ License.OperationMode licenseMode = randomFrom(License.OperationMode.values());
+ License license = TestUtils.generateSignedLicense(licenseMode.description(), TimeValue.timeValueHours(24));
TestUtils.putLicense(builder, license);
ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metaData(builder.build()).build();
- EnumSet productionModes = EnumSet.of(License.OperationMode.GOLD, License.OperationMode.PLATINUM,
- License.OperationMode.STANDARD);
- if (productionModes.contains(license.operationMode()) && tlsOn == false && "single-node".equals(discoveryType) == false) {
+
+ final boolean expectFailure;
+ switch (licenseMode) {
+ case PLATINUM:
+ case GOLD:
+ case STANDARD:
+ expectFailure = tlsOn == false && "single-node".equals(discoveryType) == false;
+ break;
+ case BASIC:
+ expectFailure = tlsOn == false && "single-node".equals(discoveryType) == false && securityExplicitlyEnabled;
+ break;
+ case MISSING:
+ case TRIAL:
+ expectFailure = false;
+ break;
+ default:
+ throw new AssertionError("unknown operation mode [" + license.operationMode() + "]");
+ }
+ logger.info("Test TLS join; Lic:{} TLS:{} Disco:{} Settings:{} ; Expect Failure: {}",
+ licenseMode, tlsOn, discoveryType, settings.toDelimitedString(','), expectFailure);
+ if (expectFailure) {
IllegalStateException ise = expectThrows(IllegalStateException.class, () -> validator.accept(node, state));
- assertEquals("TLS setup is required for license type [" + license.operationMode().name() + "]", ise.getMessage());
+ assertEquals("Transport TLS ([xpack.security.transport.ssl.enabled]) is required for license type ["
+ + license.operationMode().description() + "] when security is enabled", ise.getMessage());
} else {
validator.accept(node, state);
}