Skip to content
Immortius edited this page Nov 28, 2019 · 3 revisions

Module Sandboxing

gestalt-module loads module code within a sandbox, restricting their access to only approved classes, packages and permissions. This is intended to provide a layer of protection for users running downloaded modules, particularly modules automatically downloaded when connecting to a server.

This system should be used to prevent modules gaining uncontrolled access to a user's filesystem, network or other resources that need to be protected. It does not inherently protect against modules that may crash the application or starve it of resource - a module could still infinitely loop consuming memory until the application dies.

API Sandboxing

API sandboxing restricts the classes that a module can import in its code. This allows you to prevent a module from being able to use classes that may allow it to undertake undesirable actions.

API sandboxing is automatically applied based on the PermissionProviderFactory registered with the ModuleEnvironment.

Permissions

Restricting permissions requires the installation of a SecurityManager and SecurityPolicy.

ModuleSecurityManager securityManager = new ModuleSecurityManager();
System.setSecurityManager(moduleSecurityManager);
Policy.setPolicy(new ModuleSecurityPolicy());

This enables the enforcement of Permissions, determining which module (if any) was ultimately responsible for a permission check and checking whether that module has access to the permission.

Sometimes you have some code that is part of the API that requires a permission that modules are not generally allowed, but is fine if they trigger the permission check through this code - for instance, loading a saved game through a controlled method. There are a few ways to handle this. The best way is to use Java's AccessController.doPrivileged() method (see AccessController Javadoc). This allows you to run a section of code with the privilege of the declaring class, rather than the caller.

InputStream stream = AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() {
    @Override
    public InputStream run() {
        return url.openStream();
    }
});

PermissionProvider and PermissionProviderFactory.

PermissionProvider is the interface for the class that determines what classes and permissions a module has access to. The PermissionProvider is registered with the classloader that is associated with the module, and is used to control access for all classes loaded by that classloader.

PermissionProviders are generated from a PermissionProviderFactory that is registered with the ModuleEnvironment when constructing it.

new ModuleEnvironment(modules, permissionProviderFactory, codeInjectors);

StandardPermissionProviderFactory

StandardPermissionProviderFactory supports a whitelist approach to class and permission access, revolving around a number of PermissionSets. The base permission set provides the whitelist of classes and permissions all modules have access to. Additional permission sets can be registered - these correspond to the "requiredPermissions" from the module metadata. This allows for modules to request additional permissions such as IO or Networking.

The base permission set can be accessed by:

standardPermissionProviderFactory.getBasePermissionSet();

while additional permission sets can be created

standardPermissionProviderFactory.addPermissionSet("io", new PermissionSet());

and accessed by:

standardPermissionProviderFactory.getPermissionSet("io");

Permissions

You can grant permissions to the PermissionSet either by class or by instance:

permissionSet.grantPermission(ReflectPermission.class);
permissionSet.grantPermission(new RuntimePermission("eat.waffles")); 

You can also grant permissions to specific classes or packages, if you are unable to use the AccessController.doPrivileged() technique as described below.

Note that granting permissions requires the ModuleSecurityPermission.UPDATE_ALLOWED_PERMISSIONS permission.

API Classes

API classes determine the classes available for import by modules from outside of the module environment. Classes within modules do not need to be API classes - this is particularly true for package modules. However it can be desirable to have code outside of the module system available to modules - particularly core java classes like collections and String.

You can grant access to classes either by package, or by class:

moduleSecurityManager.getBasePermissionSet().addAPIClass(List.class);
moduleSecurityManager.getBasePermissionSet().addAPIPackage("java.lang");

Note that adding a package does not add subpackages. Also adding API classes requires the ModuleSecurityPermission.UPDATE_API_CLASSES permission.

WarnOnlyPermissionProviderFactory

WarnOnlyPermissionProviderFactory is a wrapper that can be placed around any other PermissionProviderFactory. This wrapper causes the permission checks not to be enforced - instead a warning is logged. This can allow for developing without worrying about permissions, but still gaining information about what classes and permissions are required.

API Annotation

gestalt-module also introduces an API annotation, that can be used to mark classes and packages as API:

@API
public interface MyPublicInterface {
}

For packages, use package-info.java

@API
package com.example.package;

import org.terasology.module.sandbox.API;

Then you can use the APIScanner to add all the API annotated classes and packages from a reflections manifest:

new APIScanner(standardPermissionProviderFactory).scan(reflectionsManifest);

If you wish an class to be available through a PermissionSet other than the base PermissionSet, this can be listed in the annotation:

@API(permissionSet = {"io", "network"})
public interface MyPublicInterface {
}

The API scanner will automatically created the necessary permission sets.

Common API classes and packages

Here is a brief list of some common classes and packages to make available to modules

// These are all core Java types and static helper classes
moduleSecurityManager.addAPIPackage("java.lang");
moduleSecurityManager.addAPIPackage("java.lang.ref");
moduleSecurityManager.addAPIPackage("java.math");
moduleSecurityManager.addAPIPackage("java.util");
moduleSecurityManager.addAPIPackage("java.util.regex");

// A couple of exception you might throw from API code
moduleSecurityManager.addAPIClass(IOException.class);
moduleSecurityManager.addAPIClass(InvocationTargetException.class);

// If you use yourkit for profiling, classes from this package will be accessed from module code while profiling
moduleSecurityManager.addAPIPackage("com.yourkit.runtime");

Turning Security Off

gestalt-module is designed to force you to make security decisions, and with a default-secure approach - you must provide ModuleEnvironments with an PermissionProviderFactory, and the StandardModulePermissionFactory uses a whitelist rather than blacklist approach.

If you are not using gestalt-module to support runtime modding and thus do not require sandboxing, you may instead:

  1. Create an PermissionProviderFactory implementation that produces PermissionProviders that allows everything.
  2. Do not install the ModuleSecurityModule or ModuleSecurityPolicy.