Skip to content

Add setting to ignore source security roles on CCS requests - #6402

Open
sharathkanaka wants to merge 1 commit into
opensearch-project:mainfrom
sharathkanaka:ccs-remote-recompute-setting
Open

Add setting to ignore source security roles on CCS requests#6402
sharathkanaka wants to merge 1 commit into
opensearch-project:mainfrom
sharathkanaka:ccs-remote-recompute-setting

Conversation

@sharathkanaka

Copy link
Copy Markdown

Description

  • Category: Enhancement

  • Why these changes are required?

On cross-cluster search (CCS) requests, the remote cluster inherits the source cluster's pre-computed security roles for the user. This prevents the remote cluster from independently controlling what permissions CCS users receive based on its own configuration.

  • What is the old behavior before changes and new behavior after changes?

Old behavior: Remote cluster always unions source-propagated securityRoles into its own role mapping result for CCS requests. The remote cannot independently control what permissions a CCS user receives.

New behavior: A new cluster setting plugins.security.ccs.ignore_source_security_roles (default: false) allows users to skip source cluster propagated securityRoles on CCS requests. When enabled, the remote cluster evaluates access through its own roles_mapping.yml. This gives the remote cluster independent control over CCS user permissions.

Issues Resolved

Resolves #6401

Not a backport. No new permissions introduced.

Testing

  • Unit tests (4): ConfigurableRoleMapperTest.CcsSkipSourceSecurityRolesTest : covers flag on/off with and without CCS request context
  • Integration tests (3): CcsIgnoreSourceSecurityRolesIntTests : end-to-end CCS with two remote clusters

Check List

  • New functionality includes testing
  • New functionality has been documented
  • New Roles/Permissions have a corresponding security dashboards plugin PR
  • API changes companion pull request created
  • Commits are signed per the DCO using --signoff

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.


Note: Documentation will be added in a follow-up PR to the documentation-website repo once this change is merged.

Signed-off-by: Sharath Kanaka <sharatcr@amazon.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Code Analyzer ❗

AI-powered 'Code-Diff-Analyzer' found issues on commit 31cd053.

Hard block: Issues at High severity or above will block this PR from merging.

PathLineSeverityDescription
src/main/java/org/opensearch/security/privileges/ConfigurableRoleMapper.java46mediumThe `activeConfiguration` field was changed from `private` to package-private (no access modifier). This allows any class in the same package to call `.set()` on the AtomicReference, replacing the active role-mapping configuration at runtime. The change appears motivated by test access needs, but it widens the attack surface: any code co-located in `org.opensearch.security.privileges` can silently swap the compiled role configuration without going through the normal subscription/update path.

The table above displays the top 10 most important findings.

Total: 1 | Critical: 0 | High: 0 | Medium: 1 | Low: 0


Pull Requests Author(s): Please update your Pull Request according to the report above.

Repository Maintainer(s): You can bypass diff analyzer by adding label skip-diff-analyzer after reviewing the changes carefully, then re-run failed actions. To re-enable the analyzer, remove the label, then re-run all actions.


⚠️ Note: The Code-Diff-Analyzer helps protect against potentially harmful code patterns. Please ensure you have thoroughly reviewed the changes beforehand.

Thanks.

@github-actions

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🧪 PR contains tests
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ Recommended focus areas for review

Incorrect CCS Detection

The check uses OPENDISTRO_SECURITY_SSL_TRANSPORT_TRUSTED_CLUSTER_REQUEST to detect CCS requests, but this transient flag is set for any trusted inter-node request from another cluster (including non-CCS trusted requests). This may cause source security roles to be stripped for legitimate non-CCS trusted requests, potentially breaking authorization in unintended paths. Verify that this thread-context flag reliably distinguishes CCS from other trusted transport requests.

boolean isTrustedClusterRequest = threadContext != null
    && Boolean.TRUE.equals(threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_SSL_TRANSPORT_TRUSTED_CLUSTER_REQUEST));
boolean ignoreSourceRoles = ccsIgnoreSourceSecurityRoles.get();

return activeConfiguration.map(user, caller, isTrustedClusterRequest && ignoreSourceRoles);
Test May Not Reflect Runtime Behavior

The unit test constructs ConfigurableRoleMapper with null configurationRepository and then manually sets activeConfiguration. Since activeConfiguration is package-private and set directly, this bypasses the normal configuration flow. If the constructor logic changes (e.g., visibility of activeConfiguration tightened), the test will silently break. Consider adding a test using a real/mock ConfigurationRepository to exercise the production code path.

ConfigurableRoleMapper mapper = new ConfigurableRoleMapper(null, ConfigurableRoleMapper.ResolutionMode.MAPPING_ONLY, threadContext, settingsOff);
// Manually set active configuration since we passed null for configurationRepository
mapper.activeConfiguration.set(new ConfigurableRoleMapper.CompiledConfiguration(
    roleMapping,
    HostResolverMode.IP_HOSTNAME,
    ConfigurableRoleMapper.ResolutionMode.MAPPING_ONLY
));

@github-actions

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Guard against null settings at init

settings may be null in some call paths (e.g., tests instantiating with legacy
constructors), which would cause a NullPointerException at construction. Guard the
read of the setting so the mapper still initializes with the default (false) when
settings are not provided.

src/main/java/org/opensearch/security/privileges/ConfigurableRoleMapper.java [54]

 public ConfigurableRoleMapper(ConfigurationRepository configurationRepository, ResolutionMode resolutionMode, ThreadContext threadContext, Settings settings) {
     this.threadContext = threadContext;
     this.settings = settings;
-    this.ccsIgnoreSourceSecurityRoles.set(settings.getAsBoolean(ConfigConstants.SECURITY_CCS_IGNORE_SOURCE_SECURITY_ROLES, false));
+    this.ccsIgnoreSourceSecurityRoles.set(settings != null && settings.getAsBoolean(ConfigConstants.SECURITY_CCS_IGNORE_SOURCE_SECURITY_ROLES, false));
Suggestion importance[1-10]: 6

__

Why: Valid defensive coding: if settings is null (possible in test paths), the current code will NPE. The guard is a reasonable improvement, though the impact is limited to non-production paths.

Low
General
Fix builder size hint in skip path

When resolutionMode is MAPPING_ONLY, user.getRoles() (backend roles) are not added
to result later, so pre-sizing the builder with user.getRoles().size() is misleading
and may over/under-allocate. Use a size of 0 (or the mapping-derived size) to
reflect that only mapped roles will populate the builder in the skip path.

src/main/java/org/opensearch/security/privileges/ConfigurableRoleMapper.java [218-227]

 if (skipSourceSecurityRoles) {
     // CCS request with ignore_source_security_roles enabled:
     // Do not include security roles propagated from the source cluster
-    result = ImmutableSet.builderWithExpectedSize(user.getRoles().size());
+    result = ImmutableSet.builder();
 } else {
Suggestion importance[1-10]: 3

__

Why: Minor optimization concern about builder pre-sizing. It's only a size hint and does not affect correctness; impact is negligible.

Low
Guard dynamic setting registration

The dynamic settings update consumer is only registered when createComponents runs
(i.e., when the security plugin is not disabled/SSL-only). Ensure this path is not
skipped when SSL_ONLY or disabled flags are set, otherwise the dynamic update via
_cluster/settings will silently be a no-op and the tests that flip the flag at
runtime will fail intermittently.

src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java [1602-1607]

 ConfigurableRoleMapper configurableRoleMapper = new ConfigurableRoleMapper(cr, settings, threadPool.getThreadContext());
-clusterService.getClusterSettings()
-    .addSettingsUpdateConsumer(SecuritySettings.CCS_IGNORE_SOURCE_SECURITY_ROLES_SETTING, newValue -> {
-        log.info("CCS ignore source security roles dynamically set to {}", newValue);
-        configurableRoleMapper.setCcsIgnoreSourceSecurityRoles(newValue);
-    });
+if (clusterService != null && clusterService.getClusterSettings() != null) {
+    clusterService.getClusterSettings()
+        .addSettingsUpdateConsumer(SecuritySettings.CCS_IGNORE_SOURCE_SECURITY_ROLES_SETTING, newValue -> {
+            log.info("CCS ignore source security roles dynamically set to {}", newValue);
+            configurableRoleMapper.setCcsIgnoreSourceSecurityRoles(newValue);
+        });
+}
Suggestion importance[1-10]: 2

__

Why: The clusterService is typically non-null in createComponents, and the concern about SSL_ONLY paths is speculative rather than concretely addressed by the null guard proposed.

Low

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a flag to allow remote cluster to independently compute mapped roles for CCS

1 participant