diff --git a/CHANGELOG.md b/CHANGELOG.md index c7f1a0951d..5d002ecf65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), * Create a mechanism for plugins to explicitly declare actions they need to perform with their assigned PluginSubject ([#5341](https://github.com/opensearch-project/security/pull/5341)) * Moves OpenSAML jars to a Shadow Jar configuration to facilitate its use in FIPS enabled environments ([#5400](https://github.com/opensearch-project/security/pull/5404)) * Replaced the standard distribution of BouncyCastle with BC-FIPS ([#5439](https://github.com/opensearch-project/security/pull/5439)) - +* Introduced setting `plugins.security.privileges_evaluation.precomputed_privileges.enabled` ([#5465](https://github.com/opensearch-project/security/pull/5465)) ### Bug Fixes * Fix compilation issue after change to Subject interface in core and bump to 3.2.0 ([#5423](https://github.com/opensearch-project/security/pull/5423)) diff --git a/src/integrationTest/java/org/opensearch/security/privileges/actionlevel/RoleBasedActionPrivilegesTest.java b/src/integrationTest/java/org/opensearch/security/privileges/actionlevel/RoleBasedActionPrivilegesTest.java index 475cc8fc22..765cace0e2 100644 --- a/src/integrationTest/java/org/opensearch/security/privileges/actionlevel/RoleBasedActionPrivilegesTest.java +++ b/src/integrationTest/java/org/opensearch/security/privileges/actionlevel/RoleBasedActionPrivilegesTest.java @@ -1015,6 +1015,25 @@ public void aliasesOnDataStreamBackingIndices() throws Exception { ); assertThat(resultForIndexNotCoveredByAlias, isForbidden()); } + + @Test + public void statefulDisabled() throws Exception { + SecurityDynamicConfiguration roles = SecurityDynamicConfiguration.fromYaml( + "test_role:\n" + " index_permissions:\n" + " - index_patterns: ['test_*']\n" + " allowed_actions: ['indices:*']", + CType.ROLES + ); + Map metadata = indices("test_1", "test_2", "test_3")// + .build() + .getIndicesLookup(); + + RoleBasedActionPrivileges subject = new RoleBasedActionPrivileges( + roles, + FlattenedActionGroups.EMPTY, + Settings.builder().put(RoleBasedActionPrivileges.PRECOMPUTED_PRIVILEGES_ENABLED.getKey(), false).build() + ); + subject.updateStatefulIndexPrivileges(metadata, 1); + assertEquals(0, subject.getEstimatedStatefulIndexByteSize()); + } } /** diff --git a/src/integrationTest/java/org/opensearch/security/privileges/dlsfls/DocumentPrivilegesTest.java b/src/integrationTest/java/org/opensearch/security/privileges/dlsfls/DocumentPrivilegesTest.java index 4d93f05ece..a63db960df 100644 --- a/src/integrationTest/java/org/opensearch/security/privileges/dlsfls/DocumentPrivilegesTest.java +++ b/src/integrationTest/java/org/opensearch/security/privileges/dlsfls/DocumentPrivilegesTest.java @@ -55,6 +55,7 @@ import org.opensearch.security.privileges.PrivilegesConfigurationValidationException; import org.opensearch.security.privileges.PrivilegesEvaluationContext; import org.opensearch.security.privileges.PrivilegesEvaluationException; +import org.opensearch.security.privileges.actionlevel.RoleBasedActionPrivileges; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.securityconf.impl.v7.RoleV7; @@ -540,7 +541,13 @@ private DocumentPrivileges createSubject(SecurityDynamicConfiguration ro roleConfig, statefulness == Statefulness.STATEFUL ? INDEX_METADATA.getIndicesLookup() : Map.of(), xContentRegistry, - Settings.builder().put("plugins.security.dfm_empty_overrides_all", this.dfmEmptyOverridesAll).build() + Settings.builder() + .put("plugins.security.dfm_empty_overrides_all", this.dfmEmptyOverridesAll) + .put( + RoleBasedActionPrivileges.PRECOMPUTED_PRIVILEGES_ENABLED.getKey(), + statefulness == Statefulness.STATEFUL || statefulness == Statefulness.NON_STATEFUL + ) + .build() ); } } @@ -856,7 +863,13 @@ private DocumentPrivileges createSubject(SecurityDynamicConfiguration ro roleConfig, statefulness == Statefulness.STATEFUL ? INDEX_METADATA.getIndicesLookup() : Map.of(), xContentRegistry, - Settings.builder().put("plugins.security.dfm_empty_overrides_all", this.dfmEmptyOverridesAll).build() + Settings.builder() + .put("plugins.security.dfm_empty_overrides_all", this.dfmEmptyOverridesAll) + .put( + RoleBasedActionPrivileges.PRECOMPUTED_PRIVILEGES_ENABLED.getKey(), + statefulness == Statefulness.STATEFUL || statefulness == Statefulness.NON_STATEFUL + ) + .build() ); } } @@ -1108,7 +1121,13 @@ private DocumentPrivileges createSubject(SecurityDynamicConfiguration ro roleConfig, statefulness == Statefulness.STATEFUL ? INDEX_METADATA.getIndicesLookup() : Map.of(), xContentRegistry, - Settings.builder().put("plugins.security.dfm_empty_overrides_all", this.dfmEmptyOverridesAll).build() + Settings.builder() + .put("plugins.security.dfm_empty_overrides_all", this.dfmEmptyOverridesAll) + .put( + RoleBasedActionPrivileges.PRECOMPUTED_PRIVILEGES_ENABLED.getKey(), + statefulness == Statefulness.STATEFUL || statefulness == Statefulness.NON_STATEFUL + ) + .build() ); } @@ -1236,7 +1255,8 @@ public String toString() { */ static enum Statefulness { STATEFUL, - NON_STATEFUL + NON_STATEFUL, + DISABLED } /** diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index d8e757122b..04ef048965 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -2180,6 +2180,7 @@ public List> getSettings() { // Privileges evaluation settings.add(RoleBasedActionPrivileges.PRECOMPUTED_PRIVILEGES_MAX_HEAP_SIZE); + settings.add(RoleBasedActionPrivileges.PRECOMPUTED_PRIVILEGES_ENABLED); // Resource Sharing settings.add( diff --git a/src/main/java/org/opensearch/security/privileges/actionlevel/RoleBasedActionPrivileges.java b/src/main/java/org/opensearch/security/privileges/actionlevel/RoleBasedActionPrivileges.java index 5fc9a4c578..eea26aff58 100644 --- a/src/main/java/org/opensearch/security/privileges/actionlevel/RoleBasedActionPrivileges.java +++ b/src/main/java/org/opensearch/security/privileges/actionlevel/RoleBasedActionPrivileges.java @@ -76,11 +76,24 @@ public class RoleBasedActionPrivileges extends RuntimeOptimizedActionPrivileges Setting.Property.NodeScope ); + /** + * This setting controls whether the precomputed/denormalized index privileges (in the inner class StatefulIndexPrivileges) + * will be created or not. This is on by default to provide the best action throughput. It can make sense to + * disable this when it is seen that the initialisation process takes so much time/resources that it negatively + * affects the cluster performance. This come at the price of a reduced action throughput. + */ + public static Setting PRECOMPUTED_PRIVILEGES_ENABLED = Setting.boolSetting( + "plugins.security.privileges_evaluation.precomputed_privileges.enabled", + true, + Setting.Property.NodeScope + ); + private static final Logger log = LogManager.getLogger(RoleBasedActionPrivileges.class); private final SecurityDynamicConfiguration roles; private final FlattenedActionGroups actionGroups; private final ByteSizeValue statefulIndexMaxHeapSize; + private final boolean statefulIndexEnabled; private final AtomicReference statefulIndex = new AtomicReference<>(); @@ -89,6 +102,7 @@ public RoleBasedActionPrivileges(SecurityDynamicConfiguration roles, Fla this.roles = roles; this.actionGroups = actionGroups; this.statefulIndexMaxHeapSize = PRECOMPUTED_PRIVILEGES_MAX_HEAP_SIZE.get(settings); + this.statefulIndexEnabled = PRECOMPUTED_PRIVILEGES_ENABLED.get(settings); } /** @@ -101,6 +115,10 @@ public RoleBasedActionPrivileges(SecurityDynamicConfiguration roles, Fla * the async method updateStatefulIndexPrivilegesAsync(). Should be preferred. */ public void updateStatefulIndexPrivileges(Map indices, long metadataVersion) { + if (!this.statefulIndexEnabled) { + return; + } + StatefulIndexPrivileges statefulIndex = this.statefulIndex.get(); indices = StatefulIndexPrivileges.relevantOnly(indices); @@ -632,6 +650,8 @@ static class StatefulIndexPrivileges extends RuntimeOptimizedActionPrivileges.St long metadataVersion, ByteSizeValue statefulIndexMaxHeapSize ) { + long startTime = System.currentTimeMillis(); + Map< String, CompactMapGroupBuilder.MapBuilder>> actionToIndexToRoles = @@ -753,6 +773,14 @@ static class StatefulIndexPrivileges extends RuntimeOptimizedActionPrivileges.St this.indices = ImmutableMap.copyOf(indices); this.metadataVersion = metadataVersion; + + long duration = System.currentTimeMillis() - startTime; + + if (duration > 30000) { + log.warn("Creation of StatefulIndexPrivileges took {} ms", duration); + } else { + log.debug("Creation of StatefulIndexPrivileges took {} ms", duration); + } } /** diff --git a/src/main/java/org/opensearch/security/privileges/dlsfls/AbstractRuleBasedPrivileges.java b/src/main/java/org/opensearch/security/privileges/dlsfls/AbstractRuleBasedPrivileges.java index 43baf8090d..f7ba4442c8 100644 --- a/src/main/java/org/opensearch/security/privileges/dlsfls/AbstractRuleBasedPrivileges.java +++ b/src/main/java/org/opensearch/security/privileges/dlsfls/AbstractRuleBasedPrivileges.java @@ -30,6 +30,7 @@ import org.opensearch.security.privileges.PrivilegesConfigurationValidationException; import org.opensearch.security.privileges.PrivilegesEvaluationContext; import org.opensearch.security.privileges.PrivilegesEvaluationException; +import org.opensearch.security.privileges.actionlevel.RoleBasedActionPrivileges; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.securityconf.impl.v7.RoleV7; @@ -91,6 +92,11 @@ abstract class AbstractRuleBasedPrivileges roles, Map indexMetadata, @@ -101,7 +107,8 @@ public AbstractRuleBasedPrivileges( this.roleToRuleFunction = roleToRuleFunction; this.staticRules = new StaticRules<>(roles, roleToRuleFunction); this.dfmEmptyOverridesAll = settings.getAsBoolean(ConfigConstants.SECURITY_DFM_EMPTY_OVERRIDES_ALL, false); - this.statefulRules = new StatefulRules<>(roles, indexMetadata, roleToRuleFunction); + this.statefulIndexEnabled = RoleBasedActionPrivileges.PRECOMPUTED_PRIVILEGES_ENABLED.get(settings); + this.statefulRules = this.statefulIndexEnabled ? new StatefulRules<>(roles, indexMetadata, roleToRuleFunction) : null; } /** @@ -524,6 +531,10 @@ protected abstract JoinedRule compile(PrivilegesEvaluationContext context, Colle throws PrivilegesEvaluationException; synchronized void updateIndices(Map indexMetadata) { + if (!this.statefulIndexEnabled) { + return; + } + StatefulRules statefulRules = this.statefulRules; if (statefulRules == null || !statefulRules.indexMetadata.keySet().equals(indexMetadata.keySet())) {