Skip to content

Commit b26fe5d

Browse files
Security Tokens moved to a new separate index (#40742)
This commit introduces the `.security-tokens` and `.security-tokens-7` alias-index pair. Because index snapshotting is at the index level granularity (ie you cannot snapshot a subset of an index) snapshoting .`security` had the undesirable effect of storing ephemeral security tokens. The changes herein address this issue by moving tokens "seamlessly" (without user intervention) to another index, so that a "Security Backup" (ie snapshot of `.security`) would not be bloated by ephemeral data.
1 parent e9c7c6b commit b26fe5d

File tree

50 files changed

+2021
-839
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2021
-839
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/index/RestrictedIndicesNames.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
import java.util.Set;
1515

1616
public final class RestrictedIndicesNames {
17-
public static final String INTERNAL_SECURITY_INDEX_6 = ".security-6";
18-
public static final String INTERNAL_SECURITY_INDEX_7 = ".security-7";
19-
public static final String SECURITY_INDEX_NAME = ".security";
17+
public static final String INTERNAL_SECURITY_MAIN_INDEX_6 = ".security-6";
18+
public static final String INTERNAL_SECURITY_MAIN_INDEX_7 = ".security-7";
19+
public static final String SECURITY_MAIN_ALIAS = ".security";
2020

21-
public static final Set<String> RESTRICTED_NAMES = Collections.unmodifiableSet(
22-
Sets.newHashSet(SECURITY_INDEX_NAME, INTERNAL_SECURITY_INDEX_6, INTERNAL_SECURITY_INDEX_7));
21+
public static final String INTERNAL_SECURITY_TOKENS_INDEX_7 = ".security-tokens-7";
22+
public static final String SECURITY_TOKENS_ALIAS = ".security-tokens";
23+
24+
public static final Set<String> RESTRICTED_NAMES = Collections.unmodifiableSet(Sets.newHashSet(SECURITY_MAIN_ALIAS,
25+
INTERNAL_SECURITY_MAIN_INDEX_6, INTERNAL_SECURITY_MAIN_INDEX_7, INTERNAL_SECURITY_TOKENS_INDEX_7, SECURITY_TOKENS_ALIAS));
2326

2427
public static final Automaton NAMES_AUTOMATON = Automatons.patterns(RESTRICTED_NAMES);
2528

x-pack/plugin/core/src/main/resources/security-index-template.json renamed to x-pack/plugin/core/src/main/resources/security-index-template-7.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"index_patterns" : [ ".security-*" ],
2+
"index_patterns" : [ ".security-7" ],
33
"order" : 1000,
44
"settings" : {
55
"number_of_shards" : 1,
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"index_patterns" : [ ".security-tokens-7" ],
3+
"order" : 1000,
4+
"settings" : {
5+
"number_of_shards" : 1,
6+
"number_of_replicas" : 0,
7+
"auto_expand_replicas" : "0-1",
8+
"index.priority": 1000,
9+
"index.format": 7
10+
},
11+
"mappings" : {
12+
"_doc" : {
13+
"_meta": {
14+
"security-version": "${security.template.version}"
15+
},
16+
"dynamic" : "strict",
17+
"properties" : {
18+
"doc_type" : {
19+
"type" : "keyword"
20+
},
21+
"creation_time" : {
22+
"type" : "date",
23+
"format" : "epoch_millis"
24+
},
25+
"refresh_token" : {
26+
"type" : "object",
27+
"properties" : {
28+
"token" : {
29+
"type" : "keyword"
30+
},
31+
"refreshed" : {
32+
"type" : "boolean"
33+
},
34+
"refresh_time": {
35+
"type": "date",
36+
"format": "epoch_millis"
37+
},
38+
"superseded_by": {
39+
"type": "keyword"
40+
},
41+
"invalidated" : {
42+
"type" : "boolean"
43+
},
44+
"client" : {
45+
"type" : "object",
46+
"properties" : {
47+
"type" : {
48+
"type" : "keyword"
49+
},
50+
"user" : {
51+
"type" : "keyword"
52+
},
53+
"realm" : {
54+
"type" : "keyword"
55+
}
56+
}
57+
}
58+
}
59+
},
60+
"access_token" : {
61+
"type" : "object",
62+
"properties" : {
63+
"user_token" : {
64+
"type" : "object",
65+
"properties" : {
66+
"id" : {
67+
"type" : "keyword"
68+
},
69+
"expiration_time" : {
70+
"type" : "date",
71+
"format" : "epoch_millis"
72+
},
73+
"version" : {
74+
"type" : "integer"
75+
},
76+
"metadata" : {
77+
"type" : "object",
78+
"dynamic" : false
79+
},
80+
"authentication" : {
81+
"type" : "binary"
82+
}
83+
}
84+
},
85+
"invalidated" : {
86+
"type" : "boolean"
87+
},
88+
"realm" : {
89+
"type" : "keyword"
90+
}
91+
}
92+
}
93+
}
94+
}
95+
}
96+
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -601,25 +601,25 @@ public void testRemoteMonitoringCollectorRole() {
601601

602602
private void assertMonitoringOnRestrictedIndices(Role role) {
603603
final Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
604-
final String internalSecurityIndex = randomFrom(RestrictedIndicesNames.INTERNAL_SECURITY_INDEX_6,
605-
RestrictedIndicesNames.INTERNAL_SECURITY_INDEX_7);
604+
final String internalSecurityIndex = randomFrom(RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_6,
605+
RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7);
606606
final MetaData metaData = new MetaData.Builder()
607607
.put(new IndexMetaData.Builder(internalSecurityIndex)
608608
.settings(indexSettings)
609609
.numberOfShards(1)
610610
.numberOfReplicas(0)
611-
.putAlias(new AliasMetaData.Builder(RestrictedIndicesNames.SECURITY_INDEX_NAME).build())
611+
.putAlias(new AliasMetaData.Builder(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).build())
612612
.build(), true)
613613
.build();
614614
final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
615615
final List<String> indexMonitoringActionNamesList = Arrays.asList(IndicesStatsAction.NAME, IndicesSegmentsAction.NAME,
616616
GetSettingsAction.NAME, IndicesShardStoresAction.NAME, UpgradeStatusAction.NAME, RecoveryAction.NAME);
617617
for (final String indexMonitoringActionName : indexMonitoringActionNamesList) {
618618
final Map<String, IndexAccessControl> authzMap = role.indices().authorize(indexMonitoringActionName,
619-
Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_INDEX_NAME),
619+
Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS),
620620
metaData.getAliasAndIndexLookup(), fieldPermissionsCache);
621621
assertThat(authzMap.get(internalSecurityIndex).isGranted(), is(true));
622-
assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_INDEX_NAME).isGranted(), is(true));
622+
assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true));
623623
}
624624
}
625625

@@ -713,8 +713,8 @@ public void testSuperuserRole() {
713713
assertThat(superuserRole.cluster().check("internal:admin/foo", request), is(false));
714714

715715
final Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
716-
final String internalSecurityIndex = randomFrom(RestrictedIndicesNames.INTERNAL_SECURITY_INDEX_6,
717-
RestrictedIndicesNames.INTERNAL_SECURITY_INDEX_7);
716+
final String internalSecurityIndex = randomFrom(RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_6,
717+
RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7);
718718
final MetaData metaData = new MetaData.Builder()
719719
.put(new IndexMetaData.Builder("a1").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
720720
.put(new IndexMetaData.Builder("a2").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
@@ -731,7 +731,7 @@ public void testSuperuserRole() {
731731
.settings(indexSettings)
732732
.numberOfShards(1)
733733
.numberOfReplicas(0)
734-
.putAlias(new AliasMetaData.Builder(RestrictedIndicesNames.SECURITY_INDEX_NAME).build())
734+
.putAlias(new AliasMetaData.Builder(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).build())
735735
.build(), true)
736736
.build();
737737

@@ -753,16 +753,16 @@ public void testSuperuserRole() {
753753
assertThat(authzMap.get("aaaaaa").isGranted(), is(true));
754754
assertThat(authzMap.get("b").isGranted(), is(true));
755755
authzMap = superuserRole.indices().authorize(randomFrom(IndexAction.NAME, DeleteIndexAction.NAME, SearchAction.NAME),
756-
Sets.newHashSet(RestrictedIndicesNames.SECURITY_INDEX_NAME), lookup, fieldPermissionsCache);
757-
assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_INDEX_NAME).isGranted(), is(true));
756+
Sets.newHashSet(RestrictedIndicesNames.SECURITY_MAIN_ALIAS), lookup, fieldPermissionsCache);
757+
assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true));
758758
assertThat(authzMap.get(internalSecurityIndex).isGranted(), is(true));
759759
assertTrue(superuserRole.indices().check(SearchAction.NAME));
760760
assertFalse(superuserRole.indices().check("unknown"));
761761

762762
assertThat(superuserRole.runAs().check(randomAlphaOfLengthBetween(1, 30)), is(true));
763763

764764
assertThat(superuserRole.indices().allowedIndicesMatcher(randomFrom(IndexAction.NAME, DeleteIndexAction.NAME, SearchAction.NAME))
765-
.test(RestrictedIndicesNames.SECURITY_INDEX_NAME), is(true));
765+
.test(RestrictedIndicesNames.SECURITY_MAIN_ALIAS), is(true));
766766
assertThat(superuserRole.indices().allowedIndicesMatcher(randomFrom(IndexAction.NAME, DeleteIndexAction.NAME, SearchAction.NAME))
767767
.test(internalSecurityIndex), is(true));
768768
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,9 @@
258258
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETTING;
259259
import static org.elasticsearch.xpack.core.XPackSettings.API_KEY_SERVICE_ENABLED_SETTING;
260260
import static org.elasticsearch.xpack.core.XPackSettings.HTTP_SSL_ENABLED;
261-
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_INDEX_FORMAT;
262-
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_INDEX_NAME;
263-
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_TEMPLATE_NAME;
261+
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_MAIN_INDEX_FORMAT;
262+
import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS;
263+
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_MAIN_TEMPLATE_7;
264264

265265
public class Security extends Plugin implements ActionPlugin, IngestPlugin, NetworkPlugin, ClusterPlugin,
266266
DiscoveryPlugin, MapperPlugin, ExtensiblePlugin {
@@ -406,9 +406,10 @@ Collection<Object> createComponents(Client client, ThreadPool threadPool, Cluste
406406
components.add(auditTrailService);
407407
this.auditTrailService.set(auditTrailService);
408408

409-
securityIndex.set(SecurityIndexManager.buildSecurityIndexManager(client, clusterService));
409+
securityIndex.set(SecurityIndexManager.buildSecurityMainIndexManager(client, clusterService));
410410

411-
final TokenService tokenService = new TokenService(settings, Clock.systemUTC(), client, securityIndex.get(), clusterService);
411+
final TokenService tokenService = new TokenService(settings, Clock.systemUTC(), client, securityIndex.get(),
412+
SecurityIndexManager.buildSecurityTokensIndexManager(client, clusterService), clusterService);
412413
this.tokenService.set(tokenService);
413414
components.add(tokenService);
414415

@@ -965,7 +966,7 @@ public List<ExecutorBuilder<?>> getExecutorBuilders(final Settings settings) {
965966
public UnaryOperator<Map<String, IndexTemplateMetaData>> getIndexTemplateMetaDataUpgrader() {
966967
return templates -> {
967968
// .security index is not managed by using templates anymore
968-
templates.remove(SECURITY_TEMPLATE_NAME);
969+
templates.remove(SECURITY_MAIN_TEMPLATE_7);
969970
templates.remove("security_audit_log");
970971
return templates;
971972
};
@@ -1031,9 +1032,9 @@ static final class ValidateUpgradedSecurityIndex implements BiConsumer<Discovery
10311032
@Override
10321033
public void accept(DiscoveryNode node, ClusterState state) {
10331034
if (state.getNodes().getMinNodeVersion().before(Version.V_7_0_0)) {
1034-
IndexMetaData indexMetaData = state.getMetaData().getIndices().get(SECURITY_INDEX_NAME);
1035-
if (indexMetaData != null && INDEX_FORMAT_SETTING.get(indexMetaData.getSettings()) < INTERNAL_INDEX_FORMAT) {
1036-
throw new IllegalStateException("Security index is not on the current version [" + INTERNAL_INDEX_FORMAT + "] - " +
1035+
IndexMetaData indexMetaData = state.getMetaData().getIndices().get(SECURITY_MAIN_ALIAS);
1036+
if (indexMetaData != null && INDEX_FORMAT_SETTING.get(indexMetaData.getSettings()) < INTERNAL_MAIN_INDEX_FORMAT) {
1037+
throw new IllegalStateException("Security index is not on the current version [" + INTERNAL_MAIN_INDEX_FORMAT + "] - " +
10371038
"The Upgrade API must be run for 7.x nodes to join the cluster");
10381039
}
10391040
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
import static org.elasticsearch.search.SearchService.DEFAULT_KEEPALIVE_SETTING;
9999
import static org.elasticsearch.xpack.core.ClientHelper.SECURITY_ORIGIN;
100100
import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin;
101-
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_INDEX_NAME;
101+
import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS;
102102

103103
public class ApiKeyService {
104104

@@ -207,7 +207,7 @@ private void checkDuplicateApiKeyNameAndCreateApiKey(Authentication authenticati
207207
.should(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("expiration_time")));
208208
boolQuery.filter(expiredQuery);
209209

210-
final SearchRequest searchRequest = client.prepareSearch(SECURITY_INDEX_NAME)
210+
final SearchRequest searchRequest = client.prepareSearch(SECURITY_MAIN_ALIAS)
211211
.setScroll(DEFAULT_KEEPALIVE_SETTING.get(settings))
212212
.setQuery(boolQuery)
213213
.setVersion(false)
@@ -280,7 +280,7 @@ private void createApiKeyAndIndexIt(Authentication authentication, CreateApiKeyR
280280
.endObject()
281281
.endObject();
282282
final IndexRequest indexRequest =
283-
client.prepareIndex(SECURITY_INDEX_NAME, SINGLE_MAPPING_NAME)
283+
client.prepareIndex(SECURITY_MAIN_ALIAS, SINGLE_MAPPING_NAME)
284284
.setSource(builder)
285285
.setRefreshPolicy(request.getRefreshPolicy())
286286
.request();
@@ -313,7 +313,7 @@ void authenticateWithApiKeyIfPresent(ThreadContext ctx, ActionListener<Authentic
313313

314314
if (credentials != null) {
315315
final GetRequest getRequest = client
316-
.prepareGet(SECURITY_INDEX_NAME, SINGLE_MAPPING_NAME, credentials.getId())
316+
.prepareGet(SECURITY_MAIN_ALIAS, SINGLE_MAPPING_NAME, credentials.getId())
317317
.setFetchSource(true)
318318
.request();
319319
executeAsyncWithOrigin(ctx, SECURITY_ORIGIN, getRequest, ActionListener.<GetResponse>wrap(response -> {
@@ -721,7 +721,7 @@ private void findApiKeys(final BoolQueryBuilder boolQuery, boolean filterOutInva
721721
expiredQuery.should(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("expiration_time")));
722722
boolQuery.filter(expiredQuery);
723723
}
724-
final SearchRequest request = client.prepareSearch(SECURITY_INDEX_NAME)
724+
final SearchRequest request = client.prepareSearch(SECURITY_MAIN_ALIAS)
725725
.setScroll(DEFAULT_KEEPALIVE_SETTING.get(settings))
726726
.setQuery(boolQuery)
727727
.setVersion(false)
@@ -795,7 +795,7 @@ private void indexInvalidation(Collection<String> apiKeyIds, ActionListener<Inva
795795
BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
796796
for (String apiKeyId : apiKeyIds) {
797797
UpdateRequest request = client
798-
.prepareUpdate(SECURITY_INDEX_NAME, SINGLE_MAPPING_NAME, apiKeyId)
798+
.prepareUpdate(SECURITY_MAIN_ALIAS, SINGLE_MAPPING_NAME, apiKeyId)
799799
.setDoc(Collections.singletonMap("api_key_invalidated", true))
800800
.request();
801801
bulkRequestBuilder.add(request);

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ExpiredApiKeysRemover.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import org.elasticsearch.index.reindex.ScrollableHitSource;
2323
import org.elasticsearch.threadpool.ThreadPool;
2424
import org.elasticsearch.threadpool.ThreadPool.Names;
25-
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
25+
import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
2626

2727
import java.time.Duration;
2828
import java.time.Instant;
@@ -51,7 +51,7 @@ public final class ExpiredApiKeysRemover extends AbstractRunnable {
5151

5252
@Override
5353
public void doRun() {
54-
DeleteByQueryRequest expiredDbq = new DeleteByQueryRequest(SecurityIndexManager.SECURITY_INDEX_NAME);
54+
DeleteByQueryRequest expiredDbq = new DeleteByQueryRequest(RestrictedIndicesNames.SECURITY_MAIN_ALIAS);
5555
if (timeout != TimeValue.MINUS_ONE) {
5656
expiredDbq.setTimeout(timeout);
5757
expiredDbq.getSearchRequest().source().timeout(timeout);

0 commit comments

Comments
 (0)