Skip to content

[Proposal] Resource Permissions and Sharing #4500

@DarshitChanpura

Description

@DarshitChanpura

This proposal has seen few edits of designs, please read through comments to understand the latest approach.


Resource Sharing

Status Quo

Currently, there is no mechanism provided by the security plugin for plugins to utilize to provide fine-grained access control to own resources created by each plugin. Some plugins have addressed this by implementing their own custom authorization mechanism, however it is not sustainable for each plugin to handle resource-level FGAC using their own custom implementation. For example, in anomaly detection, granting permissions to delete detectors requires the user to be granted cluster:admin/opendistro/ad/detector/delete, which allows the user to delete all detectors in the cluster. Anomaly Detection has implemented their own authz mechanism.To address this, we are introducing a new concept of Resource Sharing. This feature introduces a new way of authorization in addition to existing RBAC authorization model.

Abstract:

With Resource Sharing, the high-level idea is that a resource owner determines how a resource can be shared with others. A user should be able to control access to the resources created by them. A cluster admin will not be able to control access unless they are defined as REST or TLS admins.

This will enable a collaborative usage of OpenSearch like never before. Quip is one of the well known tools around for document sharing and collaboration. With quip you can define documents private to you, or you can grant access to a group of people, or you can mark the document as public. Resource Sharing is following a similar approach where different access control scopes and levels can be defined to permission resources.

Motivation:

Due to the absence of any mechanism for resource access control, many plugins have resorted to implement custom authorization schemes to curb access to resources defined by such plugins. Here is one such example of a known public documentation for ML-Commons plugin (link) to control access to model groups.

ML Commons implements a robust authorization model that categorizes access to model groups into three distinct modes: public, private, and restricted. These modes provide flexible control over who can view and interact with model groups:

  • Public: All users have access to the model group. However, this does not grant full control over the resource. Access is further governed by the permissions model. For instance, users with ml_read_access will only be able to list and view model groups, but not modify them. Only users with ml_full_access will have complete control over public models.
  • Private : Only the resource owner has access to the model group.
  • Restricted: The resource owner has shared access with specific trusted users or parties.

This approach is inspired by the ML Commons architecture and aims to provide a reusable mechanism for any plugin to leverage the Security plugin for resource sharing. By centralizing authorization within the Security plugin, plugin development is simplified, while ensuring that access control remains robust and scalable. This model also provides a clear pathway for migrating existing access control mechanisms from plugins to the Security framework.

Feature Implementation:

Components:

There are 3 major components to this design:

  • resource_sharing index: This index will be stored centrally and will contain information about how a particular resource is shared.
  • ResourcePlugin : The contract class defined in OpenSearch to be utilized by the plugins who choose to utilize the resource access control framework provided by Security plugin.
  • Resource Access Control Framework: This framework is at the heart of this design and involves defining concrete implementation of access control methods defined in the contract above. It also involves implementing index action listener to ensure the resource sharing information remains consistent across the nodes in the cluster.

Assumptions:

A Resource is a document stored within a plugin’s system index, containing metadata about that resource. Examples of resources include Model Groups in ML Commons, Anomaly Detectors in anomaly-detection, and Detectors in alerting plugins.
The .resource-sharing index will always maintain a compound key that references the location of the original resource. This compound key consists of two components:

  1. source_idx: The system index where the original resource is stored, as defined by the plugin.
  2. resource_id: The DocID corresponding to the resource document within the system index source_idx.

This structure ensures a clear and direct link between the shared resource and its original location.

Index design:

The resource_sharing index document has following entries:

  1. source_idx : The system index which resource metadata. This is usually the plugin’s system index.
  2. resource_id : The id of the resource document defined in the source_idx .
  3. created_by : Defined the user or a backend_role that created this resource.
  4. shared_with : Defines access scope for this resource. It contains users, roles and backend_roles this resource is shared with. Access scope is defined in 3 ways:
    1. public: A shared_with entry exists, and contains users entry mapped to * .
    2. restricted: A shared_with entry exists, and contains users entry not mapped to * .
    3. private: A shared_with entry is empty or doesn’t exist.
  5. In addition each shared_with will also contain the level of access that each user, role or backend_role may have. These are two scopes at present read_only and read_write.

Note: The relationship between entries in this index and the resource metadata stored in plugin’s index is many-to-one.

Here are 3 examples of entries in resource_sharing index, one for each scope.

  1. Private:
{
   "source_idx": ".plugins-ml-model-group",
   "resource_id": "<doc_id_of_model_group>",
   "created_by": {
      "user": "darshit",
      "backend_role": "",
   }
}
  1. Restricted:
{
   "source_idx": ".plugins-ml-model-group",
   "resource_id": "<doc_id_of_model_group>",
   "created_by": {
      "user": "darshit",
      "backend_role": "",
   },
   "share_with": {
      "read_only": {
         "users": [
            "derek"
         ],
         "roles": [],
         "backend_roles": []
      },
      "read_write": {
         "users": [
            "craig"
         ],
         "roles": [],
         "backend_roles": []
      }
   }
}
  1. Public:
{
   "source_idx": ".plugins-ml-model-group",
   "resource_id": "<doc_id_of_model_group>",
   "created_by": {
      "user": "darshit",
      "backend_role": "",
   },
   "share_with": {
      "read_only": {
         "users": ["*"],
         "roles": ["*"],
         "backend_roles": ["*"]
      },
      "read_write": {
         "users": [
            "*"
         ],
         "roles": ["*"],
         "backend_roles": ["*"]
      }
   }
}

NOTE: Each user, role and backend_role must be individually added as there is no pattern matching at present by design.

Code Implementation:

A new plugin type ResourcePlugin will be defined in OpenSearch. This is an extensible contract and will define core Java APIs to be used by plugins to check access:

Security will add a concrete implementation to these APIs to determine access to a particular resource, and plugins will call these APIs to verify a user’s access to requested resource.

Flow Diagram:
sequenceDiagram
    participant Plugins
    participant ResourcePlugin as ResourcePlugin<br>Defines Core APIs
    participant Security as Security<br>Concrete Implementation of APIs

    Plugins ->> ResourcePlugin: Call updateSharingInformation
    ResourcePlugin ->> Security: Forward to Security Plugin
    Security -->> ResourcePlugin: Return result
    ResourcePlugin -->> Plugins: Return result

    Plugins ->> ResourcePlugin: Call listAccessibleResourcesForUser
    ResourcePlugin ->> Security: Forward to Security Plugin
    Security -->> ResourcePlugin: Return accessible resources
    ResourcePlugin -->> Plugins: Return accessible resources

    Plugins ->> ResourcePlugin: Call hasPermission
    ResourcePlugin ->> Security: Forward to Security Plugin
    Security -->> ResourcePlugin: Return permission status
    ResourcePlugin -->> Plugins: Return permission status
Loading

Pros & Cons:

  • Pros
    • A highly scalable approach where the resource owner is empowered to determine how the resource is shared
    • Provides a clear migration path for plugins like ML Commons
  • Cons
    • No code re-use
    • Not all unknowns are identified
    • Introduces a new authorization model in addition to and different from Role Based Access Control

Test Plan

  1. Add local tests in the security plugin.
  2. Add mixed cluster tests. (To evaluate requests coming in during rolling-upgrade scenario)
  3. Add tests in anomaly detection or other plugins that could leverage this feature.

Will the feature require a security review?

This feature will require a security review to confirm that it correctly evaluates the scope of the requested resource.

Documentation

The documentation should be added for this feature detailing how users can utilize this feature.

--

Next Steps

  • Open a META issue that tracks this feature
    • Add a task to add ResourcePlugin definition in core
    • Open a task to add this feature in Security Plugin by extending the APIs offered (preferred if done via feature branch)
    • Open a follow-up issue to add tests for this feature
    • Open a PoC PR in anomaly detector plugin to demonstrate this feature

Expand to see old design:
## _High-Level Design_

### Status Quo

Currently, there is no mechanism provided by the security plugin for plugins to utilize to provide fine-grained access control to own resources created by each plugin. Some plugins have addressed this by implementing their own custom authorization mechanism, however it is not sustainable for each plugin to handle resource-level FGAC using their own custom implementation. For example, in [anomaly detection](https://opensearch.org/docs/latest/observing-your-data/ad/security/), granting permissions to delete detectors requires the user to be granted `cluster:admin/opendistro/ad/detector/delete`, which allows the user to delete all detectors in the cluster. Anomaly Detection has implemented their own authz mechanism. To avoid this the Security plugin should be the only place where authorization happens. To address this, we are introducing a new concept of Resource Permissions. This feature adds an additional step to the authorization evaluation, allowing plugins to define resource-specific permissions.


### **Feature Implementation**

Once plugins define the resource permissions associated with resources, cluster admins can assign the relevant resource permissions to roles, which will then be evaluated within the Security plugin. Resource permissions can be defined in two ways:

1. Starts with `resource:<plugin-identifier>/<permission>`  **(proposed)**
2. Starts with `<plugin-identifier>:<permission>`

The main difference is the presence of the standard prefix `resource:`, **which simplifies validation [here](https://github.com/opensearch-project/OpenSearch/blob/2069bd805804dd93bd69695a4c6521cc6f2b9bb6/server/src/main/java/org/opensearch/transport/TransportService.java#L1097)**. The structure would look like:

Allows users to use all Anomaly Detection functionality on selected resources, i.e., detectors

anomaly_full_access:
reserved: true
cluster_permissions:
- 'cluster_monitor'
- 'cluster:admin/opendistro/ad/'
index_permissions:
- index_patterns:
- '
'
allowed_actions:
- 'indices_monitor'
- 'indices:admin/aliases/get'
- 'indices:admin/mappings/get'
resource_permissions:
- identifier_pattern:
- 'detector1'
- 'detector2'
allowed_actions:
- 'resource:ad/detectors/read'



**This allows admins to add a layer of control, restricting access to selected detectors instead of all detectors. Plugins like [alerting](https://opensearch.org/docs/latest/observing-your-data/alerting/security/), [ml-commons](https://quip-amazon.com/CBKNACJHvnRY/ML-Commons-Security-model-access-control), and flow-framework can similarly benefit by defining permissions at the resource level.**



### **Evaluation Flow Diagram**

* ResourceAccessEvaluator.java
```mermaid
flowchart TD
    A[Resource Access Evaluator.evaluate]
    A --> B{Does request have resources defined?}
    B -->|No| C[Return response as incomplete]
    B -->|Yes| D{Do roles have resource_permissions defined?}
    D -->|No| E[Return response as denied and mark complete]
    D -->|Yes| F[Evaluate resource_permissions for all roles]
    F --> G{Do resource_permissions match with supplied resource names?}
    G -->|Yes| H[Return response as allowed and mark complete]
    G -->|No| E
    H --> J[Return presponse]
    E --> J
    C --> J
  • PrivilegesEvaluator.java’s evaluate() method with resource permissions check:
flowchart TD
    A[PrivilegesEvaluator.evaluate]
    A --> B[Resolve roles]
    B --> C{Is bulk request for default tenant?}
    C -->|Yes| D[Evaluate and return presponse]
    C -->|No| E[Resolve all indices in the request]
    E --> F[Evaluate snapshot restore request]
    F -->|Complete| G[Return response]
    F -->|Incomplete| H[Evaluate security index access request]
    H -->|Complete| I[Return response]
    H -->|Incomplete| J[Evaluate protected index access request]
    J -->|Complete| K[Return response]
    J -->|Incomplete| L[Evaluate Resource Access request]
    L -->|Complete| M[Return response]
    L -->|Incomplete| N[Evaluate PIT request]
    N -->|Complete| O[Return response]
    N -->|Incomplete| P{Is cluster permission request?}
    P -->|Yes| Q[Evaluate cluster-level permissions and return response]
    P -->|No| R[Evaluater terms aggregation request]
    R -->|Complete| S[Return response]
    R -->|Incomplete| T[Perform DNFOF evaluation if enabled]
    T --> U[Return result]
    style L fill:green,stroke:#333,stroke-width:4px
Loading

Low-Level Design

Design Questions

  • Are we allowing a mix of roles with and without resource permissions?
    • If yes, what is the default behavior when none of the roles resolved for a user contain a resource permission?
    • Should the user be allowed permission to that resource?
      • If yes, there won’t be backward compatibility issues, but it is not ideal as the feature can be circumvented by not adding any resource permissions.
      • (Proposed) If no, there might be backward compatibility issues, especially in mixed cluster requests, which need thorough testing.
  • Should we allow * (match_all) patterns for accessing resources? OR should they be individually listed under the role?
    • Note: detector_c* can still be allowed but the question here is, should * be prohibited as a resource_name pattern.
  • Should ressource_permissions be hidden behind feature flag?
    • No, the primary purpose of introducing this feature is to move the authorization logic, implemented by individual plugins with resources, to Security plugin.

File Changes

  1. ActionRequest.java
    1. This base class is extended by every plugin’s implementation of transport request calls. Hence, the proposal is to add a standard class property List<String> resources . This should then be implemented by plugins to populate this property when handling an actionRequest after which SecurityFilter.java will intercept this call to perform privilege evaluation. (It is important to note this change when creating a documentation to onboard plugin developers to this change)
      1. Note: this is done for standardization purposes, however this can be skipped and left on the plugin developers to add this property to each request class. (Not recommended)
  2. TransportService.java
    1. Add resource: as a valid transport action prefix here.
  3. RoleV7.java
    1. This class will now define a new type of class: Resource (Use the class Index as reference)
    2. Add this newly defined class as a property to the parent
  4. ResourceAccessEvaluator.java (new file)
    1. A new class that defines the evaluator method to verify the resource permissions.
    2. This method should assume the resource names are passed in the request (the string list defined above).
    3. If no resources are present in the list, it should assume that the plugin has not opted into this feature and so should bypass this authorization check to return a success response.
  5. PrivilegeEvaluator.java
    1. Modify evaluate() method to add a check that calls the evaluator defined above.
</details>






Metadata

Metadata

Labels

Roadmap:SecurityProject-wide roadmap labelenhancementNew feature or requestresource-permissionsLabel to track all items related to resource permissionstriagedIssues labeled as 'Triaged' have been reviewed and are deemed actionable.v3.0.0

Type

No type

Projects

Status

New

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions