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 ProtectionDomain
s. 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
.