Prevent 7.x nodes from joining cluster with un-upgraded 6.x .security indices (elastic/x-pack-elasticsearch#2921) (elastic/x-pack-elasticsearch#2940)
This is a forward-port of elastic/x-pack-elasticsearch/pull/2921. original commit message: Before this commit, a cluster with security enabled and backed by native-realm user permissions allowed rolled upgrades to clusters without upgrading the `.security` index. This resulted in the newly established 6.0 cluster not able to register the native-realm users previously established in the `.security` index. In order to fix this, one would have to rely on file-based users to re-configure and upgrade the `.security` index. Since this state is easily avoidable with an upgrade, this commit rejects the joining of upgraded nodes without upgrading the security index beforehand. modifications: Test with 7.x vs 6.x nodes. Original commit: elastic/x-pack-elasticsearch@56f81bfb20
This commit is contained in:
parent
0c10d82f78
commit
8c489b1a98
|
@ -17,6 +17,7 @@ import org.elasticsearch.bootstrap.BootstrapCheck;
|
|||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.NamedDiff;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
|
@ -198,8 +199,12 @@ import java.util.stream.Collectors;
|
|||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETTING;
|
||||
import static org.elasticsearch.xpack.XPackSettings.HTTP_SSL_ENABLED;
|
||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
|
||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_TEMPLATE_NAME;
|
||||
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.INTERNAL_INDEX_FORMAT;
|
||||
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.INTERNAL_SECURITY_INDEX;
|
||||
|
||||
public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, ClusterPlugin, DiscoveryPlugin {
|
||||
|
||||
|
@ -938,7 +943,11 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, Clus
|
|||
|
||||
@Override
|
||||
public BiConsumer<DiscoveryNode, ClusterState> getJoinValidator() {
|
||||
return enabled ? new ValidateTLSOnJoin(XPackSettings.TRANSPORT_SSL_ENABLED.get(settings)) : null;
|
||||
if (enabled) {
|
||||
return new ValidateTLSOnJoin(XPackSettings.TRANSPORT_SSL_ENABLED.get(settings))
|
||||
.andThen(new ValidateUpgradedSecurityIndex());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static final class ValidateTLSOnJoin implements BiConsumer<DiscoveryNode, ClusterState> {
|
||||
|
@ -956,4 +965,17 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, Clus
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final class ValidateUpgradedSecurityIndex implements BiConsumer<DiscoveryNode, ClusterState> {
|
||||
@Override
|
||||
public void accept(DiscoveryNode node, ClusterState state) {
|
||||
if (state.getNodes().getMinNodeVersion().before(Version.V_7_0_0_alpha1)) {
|
||||
IndexMetaData indexMetaData = state.getMetaData().getIndices().get(SECURITY_INDEX_NAME);
|
||||
if (indexMetaData != null && INDEX_FORMAT_SETTING.get(indexMetaData.getSettings()) < INTERNAL_INDEX_FORMAT) {
|
||||
throw new IllegalStateException("Security index is not on the current version [" + INTERNAL_INDEX_FORMAT + "] - " +
|
||||
"The Upgrade API must be run for 7.x nodes to join the cluster");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,10 @@ import org.elasticsearch.Version;
|
|||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.network.NetworkModule;
|
||||
import org.elasticsearch.common.settings.ClusterSettings;
|
||||
|
@ -46,7 +48,11 @@ import org.elasticsearch.xpack.security.authc.file.FileRealm;
|
|||
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
|
||||
import org.elasticsearch.xpack.ssl.SSLService;
|
||||
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETTING;
|
||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
|
||||
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.INTERNAL_INDEX_FORMAT;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
@ -203,7 +209,7 @@ public class SecurityTests extends ESTestCase {
|
|||
assertThat(filter, hasItem(Security.setting("authc.realms.*.ssl.truststore.algorithm")));
|
||||
}
|
||||
|
||||
public void testTLSJoinValidatorOnDisabledSecurity() throws Exception {
|
||||
public void testJoinValidatorOnDisabledSecurity() throws Exception {
|
||||
Settings disabledSettings = Settings.builder().put("xpack.security.enabled", false).build();
|
||||
createComponents(disabledSettings);
|
||||
BiConsumer<DiscoveryNode, ClusterState> joinValidator = security.getJoinValidator();
|
||||
|
@ -216,7 +222,6 @@ public class SecurityTests extends ESTestCase {
|
|||
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();
|
||||
|
@ -236,4 +241,72 @@ public class SecurityTests extends ESTestCase {
|
|||
validator.accept(node, ClusterState.builder(ClusterName.DEFAULT).metaData(MetaData.builder().build()).build());
|
||||
}
|
||||
}
|
||||
|
||||
public void testIndexJoinValidator_Old_And_Rolling() throws Exception {
|
||||
createComponents(Settings.EMPTY);
|
||||
BiConsumer<DiscoveryNode, ClusterState> joinValidator = security.getJoinValidator();
|
||||
assertNotNull(joinValidator);
|
||||
DiscoveryNode node = new DiscoveryNode("foo", buildNewFakeTransportAddress(), Version.CURRENT);
|
||||
IndexMetaData indexMetaData = IndexMetaData.builder(SECURITY_INDEX_NAME)
|
||||
.settings(settings(Version.V_6_1_0).put(INDEX_FORMAT_SETTING.getKey(), INTERNAL_INDEX_FORMAT - 1))
|
||||
.numberOfShards(1).numberOfReplicas(0)
|
||||
.build();
|
||||
DiscoveryNode existingOtherNode = new DiscoveryNode("bar", buildNewFakeTransportAddress(), Version.V_6_1_0);
|
||||
DiscoveryNodes discoveryNodes = DiscoveryNodes.builder().add(existingOtherNode).build();
|
||||
ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT)
|
||||
.nodes(discoveryNodes)
|
||||
.metaData(MetaData.builder().put(indexMetaData, true).build()).build();
|
||||
IllegalStateException e = expectThrows(IllegalStateException.class,
|
||||
() -> joinValidator.accept(node, clusterState));
|
||||
assertThat(e.getMessage(), equalTo("Security index is not on the current version [6] - " +
|
||||
"The Upgrade API must be run for 7.x nodes to join the cluster"));
|
||||
}
|
||||
|
||||
public void testIndexJoinValidator_FullyCurrentCluster() throws Exception {
|
||||
createComponents(Settings.EMPTY);
|
||||
BiConsumer<DiscoveryNode, ClusterState> joinValidator = security.getJoinValidator();
|
||||
assertNotNull(joinValidator);
|
||||
DiscoveryNode node = new DiscoveryNode("foo", buildNewFakeTransportAddress(), Version.CURRENT);
|
||||
int indexFormat = randomBoolean() ? INTERNAL_INDEX_FORMAT : INTERNAL_INDEX_FORMAT - 1;
|
||||
IndexMetaData indexMetaData = IndexMetaData.builder(SECURITY_INDEX_NAME)
|
||||
.settings(settings(Version.V_6_1_0).put(INDEX_FORMAT_SETTING.getKey(), indexFormat))
|
||||
.numberOfShards(1).numberOfReplicas(0)
|
||||
.build();
|
||||
DiscoveryNode existingOtherNode = new DiscoveryNode("bar", buildNewFakeTransportAddress(), Version.CURRENT);
|
||||
DiscoveryNodes discoveryNodes = DiscoveryNodes.builder().add(existingOtherNode).build();
|
||||
ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT)
|
||||
.nodes(discoveryNodes)
|
||||
.metaData(MetaData.builder().put(indexMetaData, true).build()).build();
|
||||
joinValidator.accept(node, clusterState);
|
||||
}
|
||||
|
||||
public void testIndexUpgradeValidatorWithUpToDateIndex() throws Exception {
|
||||
createComponents(Settings.EMPTY);
|
||||
BiConsumer<DiscoveryNode, ClusterState> joinValidator = security.getJoinValidator();
|
||||
assertNotNull(joinValidator);
|
||||
Version version = randomBoolean() ? Version.CURRENT : Version.V_6_1_0;
|
||||
DiscoveryNode node = new DiscoveryNode("foo", buildNewFakeTransportAddress(), Version.CURRENT);
|
||||
IndexMetaData indexMetaData = IndexMetaData.builder(SECURITY_INDEX_NAME)
|
||||
.settings(settings(version).put(INDEX_FORMAT_SETTING.getKey(), INTERNAL_INDEX_FORMAT))
|
||||
.numberOfShards(1).numberOfReplicas(0)
|
||||
.build();
|
||||
DiscoveryNode existingOtherNode = new DiscoveryNode("bar", buildNewFakeTransportAddress(), version);
|
||||
DiscoveryNodes discoveryNodes = DiscoveryNodes.builder().add(existingOtherNode).build();
|
||||
ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT)
|
||||
.nodes(discoveryNodes)
|
||||
.metaData(MetaData.builder().put(indexMetaData, true).build()).build();
|
||||
joinValidator.accept(node, clusterState);
|
||||
}
|
||||
|
||||
public void testIndexUpgradeValidatorWithMissingIndex() throws Exception {
|
||||
createComponents(Settings.EMPTY);
|
||||
BiConsumer<DiscoveryNode, ClusterState> joinValidator = security.getJoinValidator();
|
||||
assertNotNull(joinValidator);
|
||||
DiscoveryNode node = new DiscoveryNode("foo", buildNewFakeTransportAddress(), Version.CURRENT);
|
||||
DiscoveryNode existingOtherNode = new DiscoveryNode("bar", buildNewFakeTransportAddress(), Version.V_6_1_0);
|
||||
DiscoveryNodes discoveryNodes = DiscoveryNodes.builder().add(existingOtherNode).build();
|
||||
ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT)
|
||||
.nodes(discoveryNodes).build();
|
||||
joinValidator.accept(node, clusterState);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue