You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Background

Geode allows querying data stored in the regions through the usage of a query language, namely OQL. This query language provides, between other things, the ability to invoke methods on the objects stored within the region as part of the query itself.

This particular ability was not limited to specific calls, but it allowed any method invocation on the objects, including setters (which, by definition, change the object itself) and, through reflection, internal Geode and JDK methods available on the classpath. Due to the range of methods that could be invoked, several security risks were identified that could impact the integrity of the data, the integrity of the region, and/or the integrity of the platform running Geode. As part of the effort to mitigate these security flaws, GEODE-3247 was created and fixed shortly after, so from that moment on the OQL engine now started working through an all or nothing approach, meaning that when a SecurityManager is enabled Geode throws a NotAuthorizedException whenever a method, not belonging to the following whitelist, is invoked:

  • String object: any method.
  • Boolean object: booleanValue.
  • Region.Entry object: getKey or getValue.
  • Any Object: equals, compareTo, or toString.
  • Number object: intValue, longValue, shortValue, etc.
  • Date or Timestamp object: after, before, getNanos, or getTime.
  • Map, Collection, or Region object: keySet, entrySet, values, containsKey or get.

This approach has proven to be way too restrictive (no getter methods can be invoked on deployed objects, as an example) and lacks the ability the customize the behavior, leaving the user stuck with the all or nothing configuration.

Objectives / Goals

- Enhance the OQL engine to support customizing the whitelist of allowed methods.
- Enable the cluster configuration service to store, delete and change the customized whitelist through gfsh commands.

Current Behavior

There's a single internal interface, MethodInvocationAuthorizer, responsible of verifying whether a method can be invoked on a certain object for a particular region. The authorizer is tied to the QueryService and accessed / invoked by the classes MethodDispatch and AttributeDescriptor every time the query engine needs to invoke a object's method as part of the OQL execution.

Geode instantiates the DefaultQueryService class every time the user requests a QueryService for executing OQL queries, within the initialization:

  • If the security manager is not enabled, or if the flag QueryService.allowUntrustedMethodInvocation is set as true, the MethodInvocationAuthorizer is configured as a no-op class.
  • If the security manager is enabled and the flag QueryService.allowUntrustedMethodInvocation is set as false (default), the RestrictedMethodInvocationAuthorizer is configured. Whenever invoked, this class first verifies that the method is included within the list of whitelisted methods and, if it is, it checks whether the user has the required privileges to execute queries on that particular region (DATA:READ:regionName).

Proposal

Query Engine

Split the MethodInvokationAuthorizer in two different interfaces:

  1. RegionInvocationAuthorizer: internal with a default implementation provided out of the box. It should just check whether the user can execute queries on the region (DATA:READ:regionName).
  2. MethodInvokationAuthorizer: public, the default implementation out of the box will be RestrictedMethodInvocationAuthorizer. This interface is intended to be implemented by users whenever they want a more fine grained control about which methods should be whitelisted, it can extend the default implementation or totally override it. The interface will have only one method and it should throw NotAuthorizedException (non-checked exception) whenever it detects that the user should not be allowed to execute that particular method during the OQL execution. The whole information about the method to invoke and the target class can be obtained from the Method class itself, so the signature won't have the region as part of the parameters.

      public interface MethodInvocationAuthorizer extends Declarable {

        /**
        * Checks whether the method is authorized to be executed or not.
        * The implementation should only consider the method and the object on which it will be executed,
        * region access has been verified earlier.
        *
        * @param method The method to authorize.
        * @throws NotAuthorizedException If the method can't be executed due to security restrictions.
        */
        void authorizeMethodInvocation(Method method) throws NotAuthorizedException;

            }

The DefaultQueryService will use an empty implementation of both interfaces if security is disabled or the flag QueryService.allowUntrustedMethodInvocation is set as true (as it works right now). If security is enabled the default RegionInvocationAuthorizer will be used, and the MethodInvokationAuthorizer will be set as the default (blacklists everything) or, if present, the custom implementation will be used. In runtime, the RegionInvocationAuthorizer will be always invoked first; if it succeeds and the user has privileges to query the region, the configured implementation of MethodInvokationAuthorizer will be executed.

Once the split is done, Geode could provide several implementations out of the box to match common use cases (like whitelisting methods specified by custom patterns), and even integrate some or all of those implementations with gfsh commands to manage the behavior in runtime.

Configuration

Add <query-service> as a new element to the schema definition.

The <query-service> element must be defined at the cache level and it will contain any configuration related to OQL, including the custom MethodInvokationAuthorizer. This will also allow further additions and configuration options in the future, or even replace the current system properties used to configure the QueryService with new child elements and/or attributes in the <query-service>. The element would look like the following (the properties are just examples):

<cache>
  <query-engine>
    <queryVerbose>true</queryVerbose>
    <queryHeterogeneousObjects>true</queryHeterogeneousObjects>
    <allowUntrustedMethodInvocation>false</allowUntrustedMethodInvocation>
    <method-authorizer>
      <class-name>com.customer.MyMethodAuthorizer</class-name>
        <parameter name="url">
          <string>jdbc:mysql://myHost/whitelistDatabase</string>
        </parameter>
    </method-authorizer>
  </query-engine>
<cache>

This new element and its properties should be stored as part of the cluster configuration service, and should also be modifiable through gfsh commands.

  • No labels