Skip to content

Commit

Permalink
[docs] Prevent DLS/FLS if replication is assigned (#108839) (#108918)
Browse files Browse the repository at this point in the history
This commit adds documentation for the DLS/FLS restriction for RCS 2.0 API keys 
where both access and replication are defined and access has DSL/FLS.
This commit also fixes a few misleading variable names.
related: #108600
  • Loading branch information
jakelandis authored May 22, 2024
1 parent 6961641 commit 8bda971
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ At least one of them must be specified.
`names`:::: (required, list) A list of indices or name patterns to which the
permissions in this entry apply.
`field_security`:::: (optional, object) The document fields that the owners of the role have
read access to. For more information, check <<field-and-document-access-control>>.
read access to. This may not be set when the `replication` field is also defined. For more information,
see <<ccx-apikeys-dls-fls, Field and document level security with cross-cluster API keys>>.
`query`:::: (optional) A search query that defines the documents the owners of the role have
read access to. A document within the specified indices must match this query to be accessible by the owners of the role. For more information, check
<<field-and-document-access-control>>.
read access to. A document within the specified indices must match this query to be accessible by the
owners of the role. This may not be set when the `replication` field is also defined. For more information,
see <<ccx-apikeys-dls-fls, Field and document level security with cross-cluster API keys>>.
`allow_restricted_indices`:::: (optional, boolean) This needs to be set to `true` (default
is `false`) if the patterns in the `names` field should cover <<system-indices,system indices>>.
`replication`::: (optional, list) A list of indices permission entries for cross-cluster replication.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
=== Setting up field and document level security

You can control access to data within a data stream or index by adding field and document level
security permissions to a role.
<<field-level-security,Field level security permissions>> restrict access to
particular fields within a document.
<<document-level-security,Document level security permissions>> restrict access
security permissions to a role.
<<field-level-security,Field level security permissions>> restrict access to
particular fields within a document.
<<document-level-security,Document level security permissions>> restrict access
to particular documents.

NOTE: Document and field level security is currently meant to operate with
Expand Down Expand Up @@ -59,3 +59,27 @@ documents by index instead.

include::role-templates.asciidoc[]
include::set-security-user.asciidoc[]


[[ccx-apikeys-dls-fls]]
==== Field and document level security with Cross-cluster API keys

<<security-api-create-cross-cluster-api-key, Cross-Cluster API keys>> can be used to authenticate
requests to a remote cluster. The `search` parameter defines permissions for cross-cluster search.
The `replication` parameter defines permissions for cross-cluster replication.

`replication` does not support any field or document level security. `search` supports field and document level security.

For reasons similar to those described in <<multiple-roles-dls-fls,Multiple roles with document and field level security>>,
you can't create a single cross-cluster API key with both the `search` and `replication` parameters if the
`search` parameter has document or field level security defined.

If you need to use both of these parameters, and you need to define document or field level security for the `search` parameter,
create two separate cross-cluster API keys, one using the `search` parameter,
and one using the `replication` parameter. You will also need to set up two different
remote connections to the same cluster, with each named connection using the appropriate cross-cluster API key.





Original file line number Diff line number Diff line change
Expand Up @@ -154,18 +154,18 @@ public static void checkForInvalidLegacyRoleDescriptors(String apiKeyId, List<Ro
final String[] clusterPrivileges = roleDescriptor.getClusterPrivileges();
// only need to check if both "search" and "replication" are defined
// no need to check for DLS if set of cluster privileges are not the set used pre 8.14
final String[] pre8_14ClusterPrivileges = { "cross_cluster_search", "cross_cluster_replication" };
final boolean hasBoth = Arrays.equals(clusterPrivileges, pre8_14ClusterPrivileges);
final String[] legacyClusterPrivileges = { "cross_cluster_search", "cross_cluster_replication" };
final boolean hasBoth = Arrays.equals(clusterPrivileges, legacyClusterPrivileges);
if (false == hasBoth) {
return;
}

final RoleDescriptor.IndicesPrivileges[] indicesPrivileges = roleDescriptor.getIndicesPrivileges();
for (RoleDescriptor.IndicesPrivileges indexPrivilege : indicesPrivileges) {
final String[] privileges = indexPrivilege.getPrivileges();
final String[] pre8_14IndicesPrivileges = { "read", "read_cross_cluster", "view_index_metadata" };
final String[] legacyIndicesPrivileges = { "read", "read_cross_cluster", "view_index_metadata" };
// find the "search" privilege, no need to check for DLS if set of index privileges are not the set used pre 8.14
if (Arrays.equals(privileges, pre8_14IndicesPrivileges)) {
if (Arrays.equals(privileges, legacyIndicesPrivileges)) {
if (indexPrivilege.isUsingDocumentOrFieldLevelSecurity()) {
throw new IllegalArgumentException(
"Cross cluster API key ["
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,37 +243,39 @@ public void testBuildForSearchAndReplicationWithDLSandFLS() throws IOException {
}

public void testCheckForInvalidLegacyRoleDescriptors() {
final String[] pre8_14ClusterPrivileges_searchAndReplication = { "cross_cluster_search", "cross_cluster_replication" };
final String[] pre8_14ClusterPrivileges_searchOnly = { "cross_cluster_search" };
final String[] pre8_14IndexPrivileges = { "read", "read_cross_cluster", "view_index_metadata" };
// legacy here is in reference to RCS API privileges pre GA, we know which privileges are used in those versions and is used for
// minor optimizations. the "legacy" privileges might also be the same as in newer versions, and that is OK too.
final String[] legacyClusterPrivileges_searchAndReplication = { "cross_cluster_search", "cross_cluster_replication" };
final String[] legacyClusterPrivileges_searchOnly = { "cross_cluster_search" };
final String[] legacyIndexPrivileges = { "read", "read_cross_cluster", "view_index_metadata" };
final String[] otherPrivileges = randomArray(1, 5, String[]::new, () -> randomAlphaOfLength(5));
String apiKeyId = randomAlphaOfLength(5);
RoleDescriptor.IndicesPrivileges pre8_14SearchIndexPrivileges_noDLS = RoleDescriptor.IndicesPrivileges.builder()
RoleDescriptor.IndicesPrivileges legacySearchIndexPrivileges_noDLS = RoleDescriptor.IndicesPrivileges.builder()
.indices(randomAlphaOfLength(5))
.privileges(pre8_14IndexPrivileges)
.privileges(legacyIndexPrivileges)
.build();
RoleDescriptor.IndicesPrivileges pre8_14SearchIndexPrivileges_withDLS = RoleDescriptor.IndicesPrivileges.builder()
RoleDescriptor.IndicesPrivileges legacySearchIndexPrivileges_withDLS = RoleDescriptor.IndicesPrivileges.builder()
.indices(randomAlphaOfLength(5))
.privileges(pre8_14IndexPrivileges)
.privileges(legacyIndexPrivileges)
.query("{\"term\":{\"tag\":42}}")
.build();
RoleDescriptor.IndicesPrivileges otherIndexPrivilege = RoleDescriptor.IndicesPrivileges.builder()
.indices(randomAlphaOfLength(5))
.privileges(otherPrivileges) // replication has fixed index privileges, but for this test we don't care about the actual values
.build();

// role descriptor emulates pre 8.14 with search and replication with DLS: this is the primary case we are trying to catch
RoleDescriptor pre8_14ApiKeyRoleDescriptor_withSearchAndReplication_withDLS = new RoleDescriptor(
// role descriptor emulates pre GA with search and replication with DLS: this is the primary case we are trying to catch
RoleDescriptor legacyApiKeyRoleDescriptor_withSearchAndReplication_withDLS = new RoleDescriptor(
ROLE_DESCRIPTOR_NAME,
pre8_14ClusterPrivileges_searchAndReplication,
new RoleDescriptor.IndicesPrivileges[] { pre8_14SearchIndexPrivileges_withDLS, otherIndexPrivilege },
legacyClusterPrivileges_searchAndReplication,
new RoleDescriptor.IndicesPrivileges[] { legacySearchIndexPrivileges_withDLS, otherIndexPrivilege },
null
);
IllegalArgumentException exception = expectThrows(
IllegalArgumentException.class,
() -> CrossClusterApiKeyRoleDescriptorBuilder.checkForInvalidLegacyRoleDescriptors(
apiKeyId,
List.of(pre8_14ApiKeyRoleDescriptor_withSearchAndReplication_withDLS)
List.of(legacyApiKeyRoleDescriptor_withSearchAndReplication_withDLS)
)
);
assertThat(
Expand All @@ -284,32 +286,32 @@ public void testCheckForInvalidLegacyRoleDescriptors() {
+ "] is invalid: search does not support document or field level security if replication is assigned"
)
);
// role descriptor emulates search only with DLS, this could be a valid role descriptor for pre/post 8.14
// role descriptor emulates search only with DLS, this could be a valid role descriptor for pre/post GA
RoleDescriptor apiKeyRoleDescriptor_withSearch_withDLS = new RoleDescriptor(
ROLE_DESCRIPTOR_NAME,
pre8_14ClusterPrivileges_searchOnly,
new RoleDescriptor.IndicesPrivileges[] { pre8_14SearchIndexPrivileges_withDLS },
legacyClusterPrivileges_searchOnly,
new RoleDescriptor.IndicesPrivileges[] { legacySearchIndexPrivileges_withDLS },
null
);
noErrorCheckRoleDescriptor(apiKeyRoleDescriptor_withSearch_withDLS);

// role descriptor emulates search and replication without DLS, this could be a valid role descriptor for pre/post 8.14
// role descriptor emulates search and replication without DLS, this could be a valid role descriptor for pre/post GA
RoleDescriptor apiKeyRoleDescriptor_withSearchAndReplication_noDLS = new RoleDescriptor(
ROLE_DESCRIPTOR_NAME,
pre8_14ClusterPrivileges_searchAndReplication,
new RoleDescriptor.IndicesPrivileges[] { pre8_14SearchIndexPrivileges_noDLS, otherIndexPrivilege },
legacyClusterPrivileges_searchAndReplication,
new RoleDescriptor.IndicesPrivileges[] { legacySearchIndexPrivileges_noDLS, otherIndexPrivilege },
null
);
noErrorCheckRoleDescriptor(apiKeyRoleDescriptor_withSearchAndReplication_noDLS);

// role descriptor that will never have search and replication with DLS but may have other privileges
RoleDescriptor notpre8_14_apiKeyRoleDescriptor_withSearchAndReplication_DLS = new RoleDescriptor(
RoleDescriptor notLegacyApiKeyRoleDescriptor_withSearchAndReplication_DLS = new RoleDescriptor(
ROLE_DESCRIPTOR_NAME,
otherPrivileges,
new RoleDescriptor.IndicesPrivileges[] { otherIndexPrivilege, otherIndexPrivilege },
null
);
noErrorCheckRoleDescriptor(notpre8_14_apiKeyRoleDescriptor_withSearchAndReplication_DLS);
noErrorCheckRoleDescriptor(notLegacyApiKeyRoleDescriptor_withSearchAndReplication_DLS);
}

private void noErrorCheckRoleDescriptor(RoleDescriptor roleDescriptor) {
Expand Down

0 comments on commit 8bda971

Please sign in to comment.