Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] Granting Permissions to Extensions #2552

Closed
4 tasks done
Tracked by #2586 ...
stephen-crawford opened this issue Mar 14, 2023 · 6 comments
Closed
4 tasks done
Tracked by #2586 ...

[Question] Granting Permissions to Extensions #2552

stephen-crawford opened this issue Mar 14, 2023 · 6 comments
Assignees
Labels
enhancement New feature or request triaged Issues labeled as 'Triaged' have been reviewed and are deemed actionable.

Comments

@stephen-crawford
Copy link
Contributor

stephen-crawford commented Mar 14, 2023

Problem Statement

We need to be able to configure and query the permissions an extension has.

$\textcolor{orange}{\textsf{Orange text is a header for a decision that is not made }}$
$\textcolor{teal}{\textsf{Teal text is a header for a decision that is made }}$
$\textcolor{violet}{\textsf{Violet text is the recommended option }}$
$\textcolor{lime}{\textsf{Lime text is the chosen option }}$

Action items decided on this issue:

  • How do extensions get starting permissions?
  • How should extensions register ‘predefined’ roles?
  • How does an admin allow/disallow optional permissions for an extension?
  • How and where does the security plugin enforce extension permissions?

Why do we need to be able to configure permissions for extensions?

There are two main scenarios which require us to be able to manage the permissions an extension has directly.

  1. We need to be able to restrict what an extension can do. Part of the motivation behind extensions is designing a more secure system where extensions are not granted the same type of wide-spread authorization that plugins are. In order to meet this goal, we need to be able to limit the permissions that extensions have to a target set.

  2. We need to be able to query the granted permissions of an extension when an extension tries to execute a request. If an extension tries to read an index, we need to be able to confirm that the cluster administrator (or whoever is configuring the permissions) is alright with the extension reading that index.

What design considerations should be made?

Permission Resolution: User privileges are resolved through the permissions associated with roles and action groups. This can make it challenging to determine which permissions a user has been granted. We should avoid this pattern with extensions. If we want to allow for roles to be created and use these roles to grant permissions to extensions, that is fine. What we should avoid is relying on roles as the sole means to credit permissions to an extension. In isolated cases where an extension only has a single role, the operation of resolving the permissions will be O(n). In the majority of cases however, the operations will be O(n^2). If we instead directly assign the permissions to the extension and resolve them without the roles, we will always have O(n) matching the best case scenario of the role-based processing.

Permission Querying: It is important that we allow users to query the list of permissions which an extension has been granted. Though the list may become long, it is important for the list of permissions to be fetch-able so that users can check what permissions an extension has. This will improve user experience and also minimize the chances that a user grants permissions to an extension they do not mean to. A user will still be able to grant permissions erroneously but if they can query the permissions an extension has, it should be straightforward for them to confirm the extension has the permissions they intend.

Permission Delta: One feature that is likely to be very helpful is a permission delta preview. Basically, when a user makes a change to permissions, it would be helpful if they could see the result of that permission change either all the time or when it is a large enough change. When designing the method of granting permissions to extensions, we should keep this feature in mind.

What are the options for granting permissions to extensions?

The act of granting extensions permissions should occur in two places. First, when an extension is registered, it is likely that it will immediately require some permissions to operate. When a user installs an extension, they should then be provided with a list of all the permissions that the extension would like to be immediately granted. The user can then accept or deny this grant and the extension will not be fully installed unless they accept.

The second scenario where an extension is granted permissions should be when a user wishes to grant it access to data, resources, or additional operations. This process should occur whenever the user wants but it must occur outside of the operation of the extension. We do not want an extension to be able to begin some operation without having all the required permissions as a prerequisite. I have concerns this could potentially lead to an in-process extension locking down the cluster.

In both scenarios where a permission is granted to an extension, we should wait for all node states to be consistent before we allow operations using the extension. This will prevent any de-synchronicity issues from arising where one node is up-to-date and another is not.

$\textcolor{orange}{\textsf{How do extensions get starting permissions? }}$

It is likely that extensions will require a set of permissions in order to function at all. For instance, a database extension may require indices:data/read/* in order to function. As opposed to optional access permissions which can grant extensions the freedom to execute extra operations or at an expanded scope, basic access permissions are necessary to the extensions core functionality. Basic access permissions represent the necessary requirements of the extension's operation and will need to be granted to the extension upon installation.

In order to grant an extension its basic access permissions, extensions will need to provide a list of them to the cluster. When an extension Extension1 is going to be installed, it will need a mechanism to communicate its requirements to the cluster which in turn will wait for the cluster administrator to approve this grant before the extension can complete.

There are two ways that an extension could specify its base access permissions.

  1. $\textcolor{lime}{\textsf{Use a configuration file}}$ One option is for it to specify a configuration file which could be read on install and processed.

For example:

Extension1_Config.yml {
BasicAccess: [ indices:data/read/* ]
ServiceAccountant 
}
Pros Cons
Easy to use One more thing for extensions to need to modify
Similar to existing plugin model Need a unique file for each extension
Requires a file update to change

What do code changes look like?

Currently, plugins can contain a config directory where they may store configuration files. When a plugin is installed and has a config directory, the files are added to a subdirectory of the OpenSearch core config directory. Extension authors will just need to add a file into the config directory of their extension and it should be installed into OpenSearch when they install the extension. The configuration settings will need to be applied from the installed location so a handler will need to be added that checks for updates to the core config directory and applies the changes to the cluster. This is relatively similar to how plugins work now, but needs to be made hotswappable. To make the installation hotswappable, code will also need to be added to the installation process. For instance, the InstallPluginCommand will need to be updated to support extensions and also an API should be added so that this command can be run while the cluster is live.

  1. $\textcolor{violet}{\textsf{Use an API}}$ Alternatively, an API could be used which would be automatically called when an extension was installed:
curl -X GET http://userX:password@localhost:9200/_extensions/_Extension1/_basic_access

The response would then contain a list of the basic access requirements for the extension and the installer would either accept or decline them.

Pros Cons
Reusable between extensions Have to write the API
Does not require file updates to change May still require a file for storing the API inputs

What do code changes look like?

This will require making numerous new classes in the OpenSearch core server directory. The classes will add the API code and the service that can update the core config file for the extension. Like the first option, code will need to be changed in the InstallPluginCommand since the command will need to be able to be run while the cluster is live. Most changes will take place in the core code base as opposed to the Security Plugin.

Given the overlap in the two options, one solution does not necessarily need to be picked over the other. It would be reasonable to start with the first option and add the second at a later date.

$\textcolor{magenta}{\textsf{NOTE: Eventually both should be implemented.}}$

$\textcolor{orange}{\textsf{How should extensions register ‘predefined’ roles?}}$

In the plugin model, plugins are able to register 'predefined' roles which reduce the complexity of permission assignment for the end user. Instead of an administrator needing to grant all the individual permissions a plugin introduces, they can assign the my_plugin_full_access role. Similarly, predefined roles such as my_plugin_read_only contain all the relevant permissions for read operations.

Plugins converting to extensions and some native extensions will need methods of registering these predefined roles. There are two main options for handling the registration of predefined roles for extensions. The method of granting starting permissions to extensions may make this decision obvious or vice versa.

  1. $\textcolor{lime}{\textsf{Use a configuration file}}$ Use a configuration file to define the roles and then inject them into the security plugin roles.yml file. This option is the same as what plugins do today. In a configuration file such as my_extension_config.yml, extensions would include a section for the roles they would like to register upon installation.
my_extension_config.yml

...

Predefined Roles:
  my_extension_full_access:
      my_extension/read/*
      my_extension/write/*
      my_extension/delete/*
      my_extension/calculate/*
  my_extension_read_only:
      my_extension/read/*   
Pros Cons
Reusable for plugins converting to extensions Not clear how it would work with hotswappable extensions
Fast to implement May lock us into configuration files

What do code changes look like?

This option is similar to what plugins do today, so fewer code changes would be required. In general, some additional logic may be required for handling the extensions: path in core if extensions are to be stored separately from plugins. The other area where code changes may be required is in the the Security Plugin if changes were to be made to the static_action_groups etc.

  1. $\textcolor{violet}{\textsf{Use an API.}}$ The second option is similar to the second option for an extension specifying its basic access permissions. Namely, an API could be used to pass the predefined roles to the cluster.

The API could either contain a single role as the payload and be called multiple times upon installation of the extension.

If calling the API multiple times for one role at a time an example of the API could look something like this:

curl -X PUT http://userX:password@localhost:9200/_extensions/_Extension1/_predefined_role {

  Predefined_Role {
  
      my_extension_full_access {
        permissions: [ my_extension/read/*, my_extension/write/*, my_extension/delete/*,   my_extension/calculate/*]
      }
  }
}
curl -X PUT http://userX:password@localhost:9200/_extensions/_Extension1/_predefined_role {

  Predefined_Role {
  
      my_extension_read_only {
        permissions: [ my_extension/read/*]
      }
  }
}

If instead calling the API a single time for all roles to be added at once, an example could look something like:

curl -X PUT http://userX:password@localhost:9200/_extensions/_Extension1/_predefined_roles {

  Predefined_Roles: [
  
      my_extension_full_access {
        permissions: [ my_extension/read/*, my_extension/write/*, my_extension/delete/*,   my_extension/calculate/*]
      },
      my_extension_read_only {
        permissions: [ my_extension/read/*]
      }
  ]
}
Pros Cons
Reusable between extensions Have to write the API
Does not require file updates to change May still require a file for storing the API inputs
Extensions could change the predefined roles without needing to re-install to the cluster Plugins transitioning to extensions would have to make changes

What do code changes look like?

This option requires significantly more changes than the first. In order to add the API, new classes will need to be added into the server directory of OpenSearch core. Additionally, changes will need to be added so that the information passed via the API is added to the configuration subdirectory of the extension. The upside of this option is that many of the steps from the previous question can be repeated. There is not much difference between registering the predefined roles and reading the starting permissions outside of the execute functions on the transport layer.

$\textcolor{magenta}{\textsf{NOTE: Eventually both should be implemented.}}$

$\textcolor{orange}{\textsf{How does an admin allow/disallow optional permissions requested by an extension?}}$

$\textcolor{lime}{\textsf{For now, there will not be any optional permissions. All permissions will be granted on registration at once. This can be changed later.}}$

While an administrator has the ability to confirm or deny an extension's request for its basic access permissions, it is also likely that an installed extension will require further permissions to complete certain tasks. For example, a database extension may be able to complete basic functions with only read access. However, in order for it reorganize data it may also require write and update access.

There are two primary ways that granting optional permissions to an extension could occur. Unlike some other proposals,
these options are not expected to be compared against one another and should instead be used together.

$\textcolor{magenta}{\textsf{NOTE: The following two methods are expected to be used together!}}$

  1. The first way that an administrator could grant additional permissions to an extension would be directly. In a similar manner to how users are granted permissions, it make sense that a cluster administrator would want to be able to give an extension specific permissions. This process should be able to happen via an API similar to how permissions are granted to users. Instead of using roles to grant the permissions, it will likely more sense to grant the permissions individually or as part of some type of action group.

  2. The second way that an administrator can grant permissions to an extension aims to reduce the work the administrator needs to take before the extension is fully operative. If extensions register their actions with the security plugin when they are installed, it will be possible for the security plugin to search the permissions required for an extension to execute an action. After searching the required permissions the extension will require, the security plugin should then return the permissions the extension currently lacks. The user will then be prompted to grant these permissions to the extension--assuming they are an administrator and confirmed they wanted to grant the additional permissions the extension will now have the new permissions.

Pros Cons
Allows for hotswap-able extensions Have to write the API
Does not impact plugins How will the search work
User friendly Requires being able to know extensions current permissions & all permissions required based off of a request

What do code changes look like?

Code changes for this option depend on whether both suggested methods are implemented.

If only method 1 is implemented, the code changes will be isolated to the creation of an Extension object class and maybe a couple code changes in the permission granting API. When an extension is added, we will want to create a an instance of an Extension object in core (this is a class that will need to be added) which contains metadata about the extension. Part of this metadata should be a service account name that is similar to a user account. The cluster administrator should then be able to reference the service account in a similar manner to how they would a user account name and grant permissions.

If method 2 is also implemented, further code changes will be required. The extension installation process will need to be changed in core so that all of the actions that an extension may attempt to perform are mapped to the corresponding user request. For example, if my_extension has an action my_action which performs a read, write, and update action on a target index, the extension will need to provide this information to OpenSearch core when it is installed. This requirement would require redesign from existing plugins so would likely need to be optional. It may be possible to use the PrivilegesEvaluator in the Security Plugin to identify the privileges that the extension has and compare those to the registered, required permissions for the target action and then prompt the administrator. This option should probably be implemented as a follow-up option.

$\textcolor{teal}{\textsf{How and where does the Security Plugin enforce extension permissions?}}$

When an extension is installed, the cluster administrator likely grants it a set of basic access permissions. Then, if the administrator wants to perform any complex actions with the extension, they probably will have to grant the extension additional permissions. With various permissions being granted, it is important that there is a clear mechanism for enforcing authorization. How should the grants be used to check whether a given action is allowed? Where should this check occur?

$\textcolor{magenta}{\textsf{NOTE: The exact storage mechanism for the permissions should not matter so long as the permissions can be queried.}}$

There are two times when it makes the most sense to check whether an extension has the required permission to perform an action.

  1. The first option for checking if an extension has the required permissions to perform an action is before the request initially reaches the extension.

This option makes the most sense if we do not want a request to reach the extension if it can only be partially processed. A request which cannot be completed may be concerning if the cluster has no way of ending deadlocks with extensions. For example, if the extension refuses to respond to the cluster until it completes the action but the cluster will not allow the extension to complete because it lacks the necessary permissions, you could run into a scenario where the request hangs forever. For asynchronous processes this may not be concerning but if an extension is operating in-process, then such a scenario could result in cluster failure.

In order to avoid such a scenario, the cluster should check that an extension has all the required permissions to perform a given action before the request ever reaches it. Referring to the following diagram, the cluster should verify that the extension has all relevant permissions at 4.
image
At 4, the cluster has not yet sent the request to the extension. Instead, the Security Plugin is able to interact with the request and verify that the permissions that the request requires have been granted to the extension. For the cluster to be able to verify that the extension has all the required permissions by step 4, the cluster would need to have a complete archive of all the permissions each of the extension's operations requires.

Pros Cons
Can check client and extension authorization at the same time Requires knowing all permissions for all extension tasks
Already plan to interact with the security plugin here Have to store the permissions requirements somewhere
Avoids potential deadlocks May be slow to fetch all requirements

What do code changes look like?

In order for this change to be implemented, modifications will need to be made in OpenSearch core and the Security Plugin. This option will require OpenSearch core to register all the actions of an extension beforehand and being able to map a user request to an extension to the corresponding child actions that the extension will try to enact. This is similar to the more complicated method of granting optional permissions to an extension. A hook will also need to be added in the Security Plugin inside the PrivilegesEvaluator to grab the extension permissions when the user permissions are checked for the request. Alternatively the hook could be implemented into the step of token creation. This would also take part in the Security Plugin.

  1. $\textcolor{lime}{\textsf{Check at after token is passed from extension to OpenSearch Core.}}$ The second option for checking if an extension has the required permissions is to perform an action is 6. This step occurs after the extension has received the request but before the action has actually occurred.

This option does not require the cluster to know all the actions the extension will want to take in advance. The cluster can intercept the action request and confirm that the operations the extension wants to perform are allowed.

Pros Cons
Does not require knowing the actions an extension will call in advance No connection to Security Plugin here
Does not impact plugins Have to check user permissions somewhere else
Does not require search for every request Requires all actions the extension will need to take are shown in the body

What do code changes look like?

This option is easier to implement since the request can be treated as a new sub-request. When the request reaches OpenSearch from the extension, the Security Plugin can simply extract the extension information from a token and then authorize the requests against the permission storage. The permission evaluation can be handled with minimal changes to PrivilegesEvaluator.java. This option should not require any changes to OpenSearch core.

@stephen-crawford stephen-crawford added enhancement New feature or request untriaged Require the attention of the repository maintainers and may need to be prioritized labels Mar 14, 2023
@stephen-crawford stephen-crawford added triaged Issues labeled as 'Triaged' have been reviewed and are deemed actionable. and removed untriaged Require the attention of the repository maintainers and may need to be prioritized labels Mar 20, 2023
@RyanL1997
Copy link
Collaborator

Hi @scrawfor99 , thank you for making this document with these detailed design. I do have a question on be half of the following paragraph:

While an administrator has the ability to confirm or deny an extension's request for its basic access permissions, it is also likely that an installed extension will require further permissions to complete certain tasks. For example, a database extension may be able to complete basic functions with only read access. However, in order for it reorganize data it may also require write and update access.

My question is mainly about "how can we distinguish 'basic' permissions and 'optional' permissions". I was thinking that if an extension needs certain extra/optional permissions to accomplish their jobs, these permissions should be also considered as "basic" permissions. According to this, I think all the required permissions should be given to the extension during the installation step, and no need for adding them later.

@stephen-crawford
Copy link
Contributor Author

Hi @RyanL1997, thank you for following up. To answer your question, it should not matter whether a permission is "basic" or "optional". The distinction in the document is simply made so that it is clear when it refers to a permission granted on installation or later. I agree with you that all required permissions should be granted on installation. For optional permissions, I was more referring to permissions which would allow extra functionality or perhaps some type of metric tracking a user may not want. For instance, something like a cookie for an extension users may not want. Does that answer your question?

@stephen-crawford stephen-crawford moved this to Awaiting Review in Security for Extensions Mar 28, 2023
@stephen-crawford stephen-crawford self-assigned this Mar 30, 2023
@stephen-crawford stephen-crawford changed the title Granting Permissions to Extensions [Question] Granting Permissions to Extensions Mar 30, 2023
@stephen-crawford
Copy link
Contributor Author

stephen-crawford commented Mar 30, 2023

Hi @opensearch-project/security, I am going to make some executive decisions to try to drive these questions to a resolution. For each of the points below, I will be moving forward unless I hear objections by Tuesday 4/4.

  • How do extensions get starting permissions?: Option 1. Extension authors specify their permissions in a configuration file that is parsed on extension registration. An API can be added later.
  • How should extensions register ‘predefined’ roles?: Option 1. Same rationale as the previous point.
  • How does an admin allow/disallow optional permissions for an extension?: An API will be added but for now, there won't be optional permissions. Too little is offered by having optional permissions for it to be part of the initial support.

@cwperks
Copy link
Member

cwperks commented Mar 30, 2023

@scrawfor99 See responses below:

  1. See comment on here: [Question] How should service account permissions be stored and where? #2566 (comment). I think we are not using the same language and I am getting confused. I agree that extension authors should publish the policy (role) that will work best for their extension and the cluster admin can modify/accept. I think this should be stored in the roles list, but separate from normal roles in that it should not be mapped to a user. For experimental release it will not support FLS/DLS in the definition and have a naming convention that makes it easy to lookup using the audience claim of the JIT tokens that are issued.

See example below:

# Governing policy for requests coming from AD extension
extension/ad:
    extension_policy: true # Should not be mapped to a user
    reserved: true
    cluster_permissions:
        - 'cluster_monitor'
    index_permissions:
        - index_patterns:
            - 'logs-*'
        allowed_actions:
            - 'indices_monitor'
            - 'indices:admin/aliases/get'
            - 'indices:admin/mappings/get'
  1. Roles the extension would like to register should be defined in the extensions configuration folder and registered into security plugin on bootstrap/installation.

See example below of roles that HelloWorld extension could like to add:

# these can be mapped to users to grant access to use extensions
extension_hw_greet:
  reserved: true
  extension_permissions:
    - 'extension:hw/greet'

extension_hw_full:
  reserved: true
  extension_permissions:
    - 'extension:hw/greet'
    - 'extension:hw/greet_with_adjective'
    - 'extension:hw/great_with_name'
    - 'extension:hw/goodbye'
  1. I'm not sure I understand the question.

@stephen-crawford
Copy link
Contributor Author

Closing with decisions:

How do extensions get starting permissions?

Starting permissions are parsed from the extension's configuration file during installation.

How should extensions register ‘predefined’ roles?

Custom roles are read from the configuration file during extension installation.

How does an admin allow/disallow optional permissions for an extension?

There are no optional permissions during an extension's installation process. Additional permissions can be granted by modifying the configuration file or using the internal users API.

How and where does the security plugin enforce extension permissions?

Enforce extension permissions after the request leaves the extension and returns to the trust zone

@github-project-automation github-project-automation bot moved this from Awaiting Review to Done in Security for Extensions Apr 10, 2023
@Gececi665
Copy link

$\textcolor{magenta}{\textsf{Should be magenta}}$

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request triaged Issues labeled as 'Triaged' have been reviewed and are deemed actionable.
Projects
Status: Done
Development

No branches or pull requests

4 participants