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-commo
n 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 totrue
, 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 anINFO
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 toINFO
. 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: UsingTRACE
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 tojava.security.manager
andjava.security.policy
.worker.jvm.security.enabled
- Only relevant in a Kubernetes or Docker installation. When set totrue
, 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 here.
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 theSecurityManager
, 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 tomodifyThread
.exitVM.{status}
- Allows code to callSystem.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 thedeny
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 currentshutdownHooks
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 anAccessControlContext
, 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 allowCreateTable
, add aDeephavenPermission
with the parameterCreateTable
to the security policy file. If theCreateTable
permission is allowed, users may invoke theCreateTable
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.