Java security policies

Overview

Deephaven users are allowed to use Groovy statements from their code, operating as the dbquery worker processing their job. This means they can perform some actions that could be hazardous to Deephaven itself or outside of other corporate policies. However, the dbquery worker does need to be able to do some of these operations. Therefore, the system needs to have security policies in place that can restrict end users while permitting normal system operation.

Important

This documentation specifically applies to actions within the control of the Java Virtual Machine (JVM). Groovy-based user consoles are within the JVM. Python is outside the control of Java security policies. Any client allowing access to Python must independently ensure their filesystem’s security.

This is unrelated to pre-built scripts run on the Merge server, in which case the administrator who deploys those scripts must understand and approve of their functionality. Scripts entered as persistent queries by non-admin end users are run via query workers, and are therefore subject to the same security policies as queries entered directly from the console.

Only the query workers need to have security policies applied; the other elements of the Deephaven system are considered trusted (and do not run user-generated code), so applying security policies universally is not required or recommended.

DeniedPermission and GuaranteedPermission

Java’s standard security policy, when enabled, is "That which is not permitted is forbidden." In other words, only operations specifically allowed by the security policy are permitted, and there is no way to un-permit an operation once permission has been granted. This makes it difficult to grant permissions to the query worker without also granting those permissions to user-initiated Groovy scripts. Deephaven-written code must be able to access certain resources without allowing code initiated from a Groovy script.

Deephaven uses a Java security policy that enables a DeniedPermission to be created, which explicitly introduces the concept of a "deny" action. Any prohibited actions can be listed in a standard Java policy file (with a DeniedPermission permission line specifying what is prohibited). As this can also be somewhat inflexible, a corresponding GuaranteedPermission is also established, allowing for exceptions to a DeniedPermission. This creates the following fixed hierarchy of permissions: Nothing is allowed, unless granted, unless denied, unless guaranteed.

Deephaven manages a list of permissions denied to users and permit all other operations. Any clients who wish to add additional limitations may modify the policy file accordingly. See Available security permissions below.

JVM blacklist

Users can still set system properties at the time the query worker is invoked by using the -D flag. Since Java security policies do not take effect until the JVM has started up, no security policy can prevent users from setting system properties that way; the only way to prevent this is to have an explicit blacklist to check for when creating a console with user-entered JVM arguments. See Properties below. In Deephaven, this blacklist specifically refuses to accept the java.security.manager or java.security.policy properties as user-entered JVM arguments. If there are other configuration properties that should not be user-editable, those properties should be marked as final in the relevant Deephaven property file on the server (which will cause an error to be generated when the system attempts to replace the final property with the specified value).

Enabling security

Standard installation

On a simple installation, copy the worker.policy file from /usr/illumon/latest/bin/worker.policy to /etc/sysconfig/illumon.d/resources/worker.policy, then restart the db_query_server. Deephaven will automatically pick up the file and apply it to all user activity on the worker.

Kubernetes installation

The Java security policy file format has limitations on its pattern-matching. When used in a Kubernetes environment, the worker.policy file must be modified to specify each remote query dispatcher by name in the grant lines that normally say dbquery/db_query_server. That is because there could be multiple query servers, and each one can have a different name (such as rqd-0). The file must have a copy of the grant section for db_query_server made for each Remote Query Dispatcher pod, and each such copied section should specify which RQD pod it applies to, even if the section is a complete duplicate of another.

Copy the worker.policy file to /etc/sysconfig/illumon.d/resources/worker.policy. Edit the worker.policy file as specified above. On the Remote Query Dispatcher node, also edit iris-common to include worker.jvm.security.enabled (and, optionally, worker.jvm.security.path if you choose to use a non-standard path). Then restart the RQD pod.

Security test

To perform a simple test to confirm security has been enabled, open a GUI console and attempt to run the following line:

FileWriter writer = new FileWriter("/db/TempFiles/dbquery/temp.txt")

If security has been enabled, this should throw an exception, noting that write permissions have not been granted for the /db/TempFiles/dbquery directory.

Example permissions

permission com.illumon.iris.security.GuaranteedPermission "java.io.FilePermission:/tmp/-:read,write,delete";

This will guarantee read, write, and delete access to the /tmp/ directory and all files within it.

The - after the path acts as a wildcard character; otherwise, you would need to explicitly specify a file.

permission com.illumon.iris.security.GuaranteedPermission "java.io.FilePermission:/tmp/example.txt:read,write,delete";

This only gives access to the /tmp/example.txt file and restricts access to everything else in the directory.

Performance considerations

Activating the Java Security Manager requires the server to do additional work to verify the various restricted actions (such as filesystem access) are allowed or denied. This penalty is inherent, to a certain extent, to the way the Java Virtual Machine handles these calls. One potential optimization is to explicitly create one or more Remote Query Dispatchers with restricted access, then do not enable security policies on those systems. For example, if there are automated processes that are considered fully trusted, those could be allowed to connect to dispatchers without a Java security policy set, for improved performance. In this case, you are explicitly handling security at a higher level and must ensure those dispatchers are not accessible to end users.

To restrict which groups are allowed to use a given dispatcher, use the following Deephaven configuration property:

RemoteQueryDispatcher.allowedGroups

Properties

  • flyway.location - Flyway path designation. As part of the ACL DB updater, this property is used to specify the location of database schema upgrade files.
  • permissions.denying.logging.separate - (Boolean) When set to true, logging for permissions-related items will be sent to a separate stand-alone file. This can be useful when determining what permissions are needed for custom operations, particularly when the calling object does not need detailed logging or produces large amounts of log messages. Otherwise, failed permissions checks will be recorded to the normal log at an INFO level.
  • permissions.denying.logging.location - File path. When using separate permissions logging, this indicates the location of the log file, defaulting to /tmp/permlog.txt.
  • permissions.denying.logging.level - Log level, defaulting to INFO. When using separate permissions logging, this indicates the log level to use.
    • INFO should result in logging any denied permission attempts.
    • TRACE should result in logging all permission requests, including granted requests. Note: Using TRACE level rapidly generates very large files.
  • jvm.blacklist - A list of system properties that users are not permitted to explicitly set when creating an interactive console. Normally users can use Advanced Options in the console connection to specify additional JVM args. Since that could allow users to specify a nonexistent or overpermissive policy file, it is necessary to be able to prevent certain JVM arguments from being supplied. If specified, this property will declare an array of names of properties that users may not control. If not specified, this defaults to java.security.manager and java.security.policy.
  • worker.jvm.security.enabled - Only relevant in a Kubernetes or Docker installation. When set to true, this property tells the Remote Query Dispatcher to enable Java security for all query workers. Those environments may not have access to the locations where the worker.policy file would be stored, so must have this explicit parameter set.
  • worker.jvm.security.path - (Optional) Only relevant in a Kubernetes or Docker installation. The path to the security policy file to use. Defaults to /etc/sysconfig/illumon.d/resources/worker.policy.

Available Security Permissions

Standard permissions

This documentation discusses some permissions that are specifically related to Deephaven.

Note

See: The standard permissions are documented at: https://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html.

java.security.AllPermission

Grants all permissions. By granting this to everything, then using DeniedPermissions, we create the "everything is permitted except that which is forbidden" usage model.

java.lang.RuntimePermission

These permissions relate to the runtime environment in general.

  • setSecurityManager - Controls access to setting the SecurityManager, which, if replaced by a new SecurityManager, could disable all other security.
  • accessClassInPackage.sun - Controls access to classes in the sun package, which grants access to various internals of the language that could be used maliciously.
  • createClassLoader - Allows code to create its own class loader. Deephaven uses custom class loaders internally, so this must be permitted to the query worker itself, not users.
  • setContextClassLoader - Allows code to change the class loader for the running thread, which could allow loading in malicious code.
  • enableContextClassLoaderOverride - Allows for the creation of a subclass of Thread that can override the behavior of getting or setting the class loader, which in turn would allow loading in malicious code.
  • modifyThread - Allows modification (e.g., interrupt, stop) of any thread in the system. If Groovy is allowed to manipulate threads, this will have to be allowed.
  • stopThread - Similar to modifyThread.
  • exitVM.{status} - Allows code to call System.Exit(x) on the worker. This only affects the client’s own worker, so is not as severe an issue. This is automatically granted to all code added from the classpath so that they can disable themselves, and the deny permission does not override this. In the worst case, a user can stop their own worker, which does not affect other workers.
  • shutdownHooks - Allows code to access the current shutdownHooks associated with the worker, which should be outside the scope of what users are allowed to work with. Once the worker is shutting down, the user’s jobs should be either handled elsewhere or complete.

java.security.SecurityPermission

These permissions relate to the security manager itself.

  • getPolicy - Allows retrieving the Policy object currently in force.
  • setPolicy - Allows replacing the Policy currently in force. This could permit a new Policy to be injected that would disable Deephaven’s ability to use permission-denial functionality.
  • createAccessControlContext - Allows creating an AccessControlContext, which could lead to attempting to get invalid privilege.
  • insertProvider - Allows injecting a new Provider for certain operations, such as encryption. This could mean, for example, that a user could create a crypto provider that would export private keys.
  • setProperty.package.access - Allows changing the access levels of fields and methods, potentially exposing private functionality.

java.lang.reflect.ReflectPermission

These permissions control reflection into other libraries.

  • suppressAccessChecks - Groovy 2.3.6 allows access to all fields and methods as if they were public, so this has no effect. Groovy 2.5.0 honors this permission, so private and protected fields cannot be accessed.
  • newProxyInPackage.{package name} - Controls whether the user may create a proxy for classes in the specified package. Users should not normally be able to create proxies for classes in the Deephaven JARs.

java.util.PropertyPermission

These permissions control access to the properties for the VM. Each individual property may be specified by name (with * as a wildcard), along with read, write, or read, write to specify which actions are being granted (or, in this case, denied). With the current default policy file, Groovy is prevented from being able to write any property values, though it can read them.

java.io.FilePermission

These permissions control access to files or directories for the VM. Each individual property may be specified by the file path (with /* as a terminal wildcard to indicate all classes and JARs within the current directory, or a terminal /- as a wildcard to indicate all classes and JARs within the current directory or any subdirectories) along with any combination of read, write, delete, execute and readLink parameters.

At a minimum, user scripts should not be allowed to delete or write to the security policy file itself (which should be set with filesystem permissions to be read-only), nor to delete or write to any internal Deephaven directories. This includes deleting from or writing to /db/Intraday, /db/TempFiles, or deleting from /var/log/deephaven.

It should not be necessary to specify directories where dbquery has no unpermitted access in the first place. For example, /usr/illumon/ should not include any write access for dbquery, so it is not necessary to restrict Groovy access to this directory.

java.security.auth.AuthPermission

These permissions control security aspects of the system. Generally, users should not be able to modify the security on the server itself.

java.lang.management.ManagementPermission

These permissions control access to the runtime characteristics of the JVM (such as adjusting memory settings).

  • control - If specified, user may modify runtime settings of the JVM. This may be allowable in some situations, such as if Groovy scripts are to be allowed to adjust their memory settings on the fly.
  • monitor - If specified, user may retrieve runtime information about the JVM. As Deephaven provides human-readable stack traces to the screen, this should be permissible.

java.nio.file.LinkPermission

These permissions control the ability to create links in the filesystem on the query worker host. This could be used to, for example, create a hard link to a file that is intended to be protected by a FilePermission.

  • hard - Allows creating hard links.
  • symbolic - Allows creating symbolic links.

java.net.SocketPermission

These permissions control access to the network via sockets. Each permission specifies its own address and port range as needed. The asterisk character * may be used as a wildcard only in the leftmost character of the address, when that address is a DNS name rather than a numerical address. They also allow specification of accept/connect/listen/resolve actions.

If there are some machines or services on a local network that should not be accessible to Groovy workers, those machines could be specified using these permissions.

The default policy does not include SocketPermissions because user scripts have reasonable use cases for needing to access various sockets, which Deephaven cannot predict.

java.net.URLPermission

These permissions control access to specific URLs. Each permission specifies its own address and port range as needed. The character * may be used as a wildcard only in the leftmost character of the address, when that address is a DNS name rather than a numerical address. Each permission may also specify which headers or methods are being controlled.

If there are some locations on a local network that should not be accessible to Groovy workers, those machines could be specified using these permissions.

Custom permissions

com.illumon.iris.security.DeephavenPermission

DeephavenPermission controls access to Deephaven-specific functionality.

  • CreateTable - To allow CreateTable, add a DeephavenPermission with the parameter CreateTable to the security policy file. If the CreateTable permission is allowed, users may invoke the CreateTable statement directly. This is normally intended to be performed from within the system only.

com.illumon.iris.security.DeniedPermission

With this custom permission, any other permission can be inverted to deny that permission instead of granting that permission. Specify the permission to be invoked, then any parameters to that permission separated by colons, such as:

permission com.illumon.iris.db.v2.utils.DeniedPermission "Fully.Specified.PermissionClass:permissionName"

A DeniedPermission may not be used to deny another DeniedPermission or a GuaranteedPermission. It may, however, be used to invert the function of a FileACLPermission.

com.illumon.iris.db.v2.utils.GuaranteedPermission

With this custom permission, any other permission can be designated as an override to DeniedPermissions. Anything listed by a GuaranteedPermission is allowed, even if otherwise prevented by a DeniedPermission. Specify the permission to be invoked, then any parameters to that permission separated by colons, such as:

permission com.illumon.iris.db.v2.utils.GuaranteedPermission "Fully.Specified.PermissionClass:permissionName"

A GuaranteedPermission may not be used to guarantee a DeniedPermission or another GuaranteedPermission. It may, however, be used to guarantee the function of a FileACLPermission.

com.illumon.iris.db.v2.permissions.FileACLPermission

With this custom permission, Deephaven can be told to grant or deny permission to file paths based on System ACL entries. This permission takes a single String as a parameter, composed of {ACL property} to determine which ACL entries to check, such as:

permission com.illumon.iris.db.v2.permissions.FileACLPermission "foo"

This means that when a user attempts an action that would cause a file access, the system will check for a System ACL for that user for the property key foo. If there is an ACL for that key, and the filter text creates a FileACLFilterGenerator, then the FileACLFilter generated by that generator will be checked. In this case, * is not a "grant all" permission; a FileACLPermission explicitly will only work with a FileACLFilterGenerator and will grant no permissions otherwise.

The FileACLFilterGenerator follows the same conventions as a standard Java FilePermission, in terms of path and actions specified. The syntax for a FileACLFilterGenerator is:

new FileACLFilterGenerator("/path/to/file/", "actions")

This will then create a FileACLFilter, which will in turn dynamically generate a FilePermission and check whether that dynamically-generated FilePermission implies access to the requested resource.

For the sake of efficiency, FileACLPermissions only perform a database lookup of the ACLs at the time the permission is first checked within a given worker; after that, the known ACLs are cached. This means that any ACL changes after the query has first checked for filesystem access permissions will not take effect until a new worker is started.

To guarantee that a FileACLPermission grants or denies access you can combine it with GuaranteedPermission or DeniedPermission. Here is an example:

permission com.illumon.iris.security.GuaranteedPermission "com.illumon.iris.db.v2.permissions.FileACLPermission:exampleKey";

These are the values in the System ACLs tab of the ACL Editor:

  • Group: traders
  • Key: exampleKey
  • Filter: new FileACLFilterGenerator("/tmp/-","read,write,delete")

This will grant read, write, and delete access to all files in the /tmp/ directory to all members of the traders group.

Executing external programs

Java security handling has a subtlety to its handling of the execute operation that should be kept in mind if external programs will be initiated from Deephaven.

When checking whether a file may be executed, permission checking performs special handling. Any file that is not specified with an absolute path will only be checked against the <<ALL FILES>> permission, even if the actual absolute path to that file is specified in a separate DeniedPermission or GuaranteedPermission. In the default Deephaven worker policy file, <<ALL FILES>> is denied the execute permission. For example, invoking Exec on the string "touch /tmp/aFile.txt" would check against the <<ALL FILES>> permission only, since touch is not an absolute path. Even if a DeniedPermission to /usr/bin/touch was specified, that DeniedPermission would not be checked. Invoking "/usr/bin/touch /tmp/aFile.txt" would check for the specific filepath of /usr/bin/touch.

Any files that need to be executed via a user-entered script or pushed class should be specified via absolute filename and a specific GuaranteedPermission granted to that exact file path (or parent directory, as needed). Deephaven has no control over the behavior of external programs executed via this method; it is the system administrator’s responsibility to manage access to any external programs via execute permissions from Deephaven.