2

I am running an application with Drools in a WildFly 10.1.0-Final Application Server. As the code executed by Drools is untrusted, I decided to build a custom sandbox to allow for secure execution.

Since no external modules may be added to the Application Server, I decided that, to effectively use this sandbox, I'd have to create a fitting environment.

So, in the standalone.xml of the server, I defined a maximum-set and a minimum-set of permissions for all wars, which I set to java.permission.AllPermission, thereby generally allowing all code to be executed.

I then added the -secmgr flag to the server, to activate the SecurityManager. I was able to use the application as intended, and several calls to AccessController.doPrivileged() did indeed check the permissions and allowed the desired actions.

I then created a Service class, which would create a Sandbox-context to check the access against.

It explicitly creates an AccessControlContext with a CodeBase of null (meaning ALL code) and a ProtectionDomain with three necessary Permissions for Drools - Runtime.getClassLoader, Runtime.accessDeclaredMembers and Reflect.suppressAccessChecks. I then call AccessController.doPrivileged with the desired call (StatefulKnowledgeSession.fireAllRules) and the Sandbox Context.

On my development system, when a drools rule tries to do anything not permitted (say, creating a File), a ConsequenceException is thrown with an AccessControlException as cause, due do denied Access to File.write.

On our production system, the same configuration and deployment is used, yet all calls fail due to an AccessControlException which states that Runtime.getClassLoader is not permitted.

I debugged the live system, when calling sandboxContext.checkPermission(new RuntimePermission("getClassLoader")), no exception is thrown, which tells me that the context itself allows it.

Still, our production machine denies the permission

The only difference in the systems is the OS-Version, as I am using an Ubuntu machine and the production server is a CentOS machine.

UPDATE

Debugging the server found, that there is another ProtectionDomain that is unsigned and for no code base, and that one is missing the Runtime.getClassLoader permission. However, that particular ProtectionDomain is not present on my development machine, and I have no way of finding where that one comes from.

UPDATE 2

More information: When the exception is thrown, the AccessControlStack contains a privileged context (the one I instantiated and expected) and 5 extra ProtectionDomains. One of these (the offending one) has, as said before, a CodeSource of (null <no signer certificates>), has no Principals, has a null PermissionCollection and uses the org.drools.util.CompositeClassLoader.

The Permissions granted by this Domain (taken from domain.toString()):

("java.net.SocketPermission" "localhost:0" "listen,resolve")
("java.net.SocketPermission" "localhost:0" "listen,resolve")
("javax.security.jacc.WebResourcePermission" "/")
("java.util.PropertyPermission" "java.specification.version" "read")
("java.util.PropertyPermission" "java.version" "read")
("java.util.PropertyPermission" "os.arch" "read")
("java.util.PropertyPermission" "java.specification.vendor" "read")
("java.util.PropertyPermission" "java.vm.specification.name" "read")
("java.util.PropertyPermission" "java.vm.vendor" "read")
("java.util.PropertyPermission" "path.separator" "read")
("java.util.PropertyPermission" "os.version" "read")
("java.util.PropertyPermission" "file.separator" "read")
("java.util.PropertyPermission" "line.separator" "read")
("java.util.PropertyPermission" "java.vm.specification.vendor" "read")
("java.util.PropertyPermission" "java.specification.name" "read")
("java.util.PropertyPermission" "java.vendor.url" "read")
("java.util.PropertyPermission" "java.vendor" "read")
("java.util.PropertyPermission" "java.vm.version" "read")
("java.util.PropertyPermission" "java.vm.name" "read")
("java.util.PropertyPermission" "java.vm.specification.version" "read")
("java.util.PropertyPermission" "os.name" "read")
("java.util.PropertyPermission" "java.class.version" "read")
("java.lang.RuntimePermission" "stopThread")
("javax.security.jacc.WebRoleRefPermission" "" "**")
("javax.security.jacc.WebRoleRefPermission" "Faces Servlet" "**")
("javax.security.jacc.WebRoleRefPermission" "metaform" "**")
("javax.security.jacc.WebUserDataPermission" "/")

This clearly does not have Runtime.getClassLoader, however, according to the docs I read, since I grant that permission via doPrivileged, that should not matter at all.

I see that the ApplicationServer uses Wildfly-Elytron 1.0.2.Final for its SecurityManager - is it possible that Elytron does not implement this behavior?

UPDATE 3:

A third developer machine was used to check, and that one fails, too. As this machine also uses Ubuntu, we can safely say that CentOS is not the cause here.

UPDATE 4:

As you can see, the granted permissions are roughly the same as the default java.policy permissions, which should not even be used anymore, according do Wildfly Docs.

We found that the org.jboss.security.jacc.DelegatingPolicy seems to delegate to the Permissions as defined in java.policy instead of standalone.xml.

  • `doPrivileged` can't be used to elevate *future* callers' privileges, only past ones'. Alternative: Package the library autonomously server-side, and use whatever WildFly-specific config to grant the necessary permissions to just that JAR's domain. But: a) The lib may need to be security-aware, i.e., use `doPrivileged` itself when necessary. b) If the AS employs a `SecurityManager` that doesn't unconditionally delegate permission checking to threads' ACCs (which seems to be the case with `WildFlySecurityManager`), then the lib may be forced to use its proprietary equivalent of `doPrivileged`. – Uux Jan 24 '18 at 08:17
  • Side note: `RuntimePermission("accessDeclaredMembers")` and `ReflectPermission("suppressAccessChecks")` are, at least under a standard `SecurityManager`, considered *sandbox-defeating* permissions. Think twice before granting them. – Uux Jan 24 '18 at 08:19
  • Thank you for these insights, they do help in understanding the whole security architecture. For the side note, I don't think I can remove those, as they are needed by drools, I think it's just the classloader though. Do you have any ideas how to grant those explicitly to the classloader, while disallowing for everyone else? – Stefan Helmerichs Jan 25 '18 at 12:58
  • I have no idea whatsoever what Drools is and how it works. Initially I understood that you aim to sandbox Drools itself, while you had stated explicitly that your goal is to sandbox code executed *by* Drools. Silly me. Ignore my comment suggesting that Drools be relocated outside the application archive. – Uux Jan 25 '18 at 17:19
  • 1
    Have you seen ["How to define a KIE Policy"](https://docs.jboss.org/drools/release/7.5.0.Final/drools-docs/html_single/index.html#_howtodefinekiepolicy)? Does it apply to your setup? – Uux Jan 25 '18 at 17:20
  • Generally speaking (and always assuming that WildFly's security manager doesn't do something radically different), permissions are per-domain, not per-class. Thus, to assign different permissions to different classes, a necessary first step is to ensure that they are assigned non-equivalent domains (where equivalence is determined based on their corresponding `CodeSource`s). One way to achieve that is through separate packaging, hence the initial suggestion. Another is by employing different class loaders, or a single custom one that is capable of assigning multiple domains. – Uux Jan 25 '18 at 17:46

0 Answers0