mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-17 10:25:15 +00:00
Prevent nodes from joining a non-TLS enabled cluster with a production license (elastic/x-pack-elasticsearch#2484)
This change prevents a node from joining a cluster with a production license (gold, platinum, standard) iff the cluster doesn't have TLS setup. This is mainly a BWC oriented change that prevents joining old 5.x clusters without a TLS setup. Relates to elastic/x-pack-elasticsearch#2463 Original commit: elastic/x-pack-elasticsearch@21f5a58472
This commit is contained in:
parent
2e3aca414b
commit
0680e41f36
@ -271,7 +271,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());
|
||||
return license == LicensesMetaData.LICENSE_TOMBSTONE ? null : license;
|
||||
}
|
||||
|
||||
@ -469,7 +469,12 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
};
|
||||
}
|
||||
|
||||
License getLicense(final LicensesMetaData metaData) {
|
||||
public static License getLicense(final ClusterState state) {
|
||||
final LicensesMetaData licensesMetaData = state.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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class XPackSettings {
|
||||
* 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 =
|
||||
public 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);
|
||||
|
@ -15,10 +15,10 @@ import org.elasticsearch.action.support.ActionFilter;
|
||||
import org.elasticsearch.action.support.DestructiveOperations;
|
||||
import org.elasticsearch.bootstrap.BootstrapCheck;
|
||||
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;
|
||||
@ -51,9 +51,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;
|
||||
@ -194,7 +197,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);
|
||||
|
||||
@ -900,4 +903,47 @@ 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);
|
||||
validateLicense(license);
|
||||
}
|
||||
|
||||
void validateLicense(License license) {
|
||||
if (license != null) {
|
||||
switch (license.operationMode()) {
|
||||
case MISSING:
|
||||
case TRIAL:
|
||||
case BASIC:
|
||||
break;
|
||||
case STANDARD:
|
||||
case GOLD:
|
||||
case PLATINUM:
|
||||
if (isTLSEnabled == false) {
|
||||
throw new IllegalStateException("TLS setup is required for license type [" + license.operationMode().name()
|
||||
+ "]");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("unknown operation mode: " + license.operationMode());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,35 @@ 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);
|
||||
ClusterState state = ClusterState.builder(ClusterName.DEFAULT).build();
|
||||
joinValidator.accept(new DiscoveryNode("foo", buildNewFakeTransportAddress(), Version.CURRENT), state);
|
||||
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);
|
||||
License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24));
|
||||
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.validateLicense(license));
|
||||
assertEquals("TLS setup is required for license type [" + license.operationMode().name() + "]", ise.getMessage());
|
||||
} else {
|
||||
validator.validateLicense(license);
|
||||
}
|
||||
validator.validateLicense(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user