web-cat / code-workout

CodeWorkout: a programming practice and self-study web site
https://codeworkout.cs.vt.edu
227 stars 82 forks source link

Development set up AccessControlException: Access denied #169

Open 1-alex98 opened 4 years ago

1-alex98 commented 4 years ago

Screenshot from 2020-08-07 11-13-50 Setup: Followed steps in the read me When executing tests on a development set up: AccessControlException: Access denied ("java.lang.RuntimePermission" "getProtectionDomain") is thrown

s-edwards commented 4 years ago

This error looks like it is coming from the java security policy used for sandboxing student answers. It looks like we need to fix that in the development setup.

For a dev setup, the quickest workaround is to remove the security policy enforcement. Of course, don't do that for a publicly available instance, but it is ok for a local dev instance that is not externally accessible. Can you try this to see if it fixes the problem?

Look in the usr/resources/config.rb file, and find the CMD[java][cmd] variable, and comment out/remove the two lines for -Djava.security.manager and -Djava.security.policy (lines 30-31 in the master branch here on github). Then try again and see if this error goes away.

If that fixes the problem, re-enabling the security policy likely means looking at usr/resources/Java/java.policy and figuring out which item needs fixing. You can look at the raw Java exception trace in the output (look in your usr/attempts folder and find the folder for your failed attempt and look at the raw files there). That exception trace should give you an indication of which code caused the security violation. I'm guessing it was a standard Java library, though. From there, look at the grant clauses on lines 22-35 in usr/resources/Java/java.policy that cover the jvm and revise/add the grants to cover where the JVM standard libraries are installed in your dev setup. I think that is probably the issue (not sure, so confirmation is useful). Ayaan, we need to fix this in the committed version of java.policy so that this error doesn't occur in the docker container.

1-alex98 commented 4 years ago
====================
Testsuite: FactorialTest
--------------------
Testcase: test_13 took 0.004 sec
    Caused an ERROR
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getProtectionDomain")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:886)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.Class.getProtectionDomain(Class.java:2299)
    at org.apache.tools.ant.AntClassLoader.defineClassFromData(AntClassLoader.java:1141)
    at org.apache.tools.ant.AntClassLoader.getClassFromStream(AntClassLoader.java:1318)
    at org.apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.java:1374)
    at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1335)
    at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1090)
    at org.apache.tools.ant.util.SplitClassLoader.loadClass(SplitClassLoader.java:58)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at FactorialTest.setUp(FactorialTest.java:13)
    at junit.framework.TestCase.runBare(TestCase.java:139)
    at junit.framework.TestResult$1.protect(TestResult.java:122)
    at junit.framework.TestResult.runProtected(TestResult.java:142)
    at junit.framework.TestResult.run(TestResult.java:125)
    at junit.framework.TestCase.run(TestCase.java:129)
    at junit.framework.TestSuite.runTest(TestSuite.java:252)
    at junit.framework.TestSuite.run(TestSuite.java:247)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:535)

Testcase: test_14 took 0.001 sec
    Caused an ERROR
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getProtectionDomain")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:886)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.Class.getProtectionDomain(Class.java:2299)
    at org.apache.tools.ant.AntClassLoader.defineClassFromData(AntClassLoader.java:1141)
    at org.apache.tools.ant.AntClassLoader.getClassFromStream(AntClassLoader.java:1318)
    at org.apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.java:1374)
    at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1335)
    at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1090)
    at org.apache.tools.ant.util.SplitClassLoader.loadClass(SplitClassLoader.java:58)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at FactorialTest.setUp(FactorialTest.java:13)
    at junit.framework.TestCase.runBare(TestCase.java:139)
    at junit.framework.TestResult$1.protect(TestResult.java:122)
    at junit.framework.TestResult.runProtected(TestResult.java:142)
    at junit.framework.TestResult.run(TestResult.java:125)
    at junit.framework.TestCase.run(TestCase.java:129)
    at junit.framework.TestSuite.runTest(TestSuite.java:252)
    at junit.framework.TestSuite.run(TestSuite.java:247)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:535)

Testcase: test_15 took 0 sec
    Caused an ERROR
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getProtectionDomain")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:886)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.Class.getProtectionDomain(Class.java:2299)
    at org.apache.tools.ant.AntClassLoader.defineClassFromData(AntClassLoader.java:1141)
    at org.apache.tools.ant.AntClassLoader.getClassFromStream(AntClassLoader.java:1318)
    at org.apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.java:1374)
    at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1335)
    at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1090)
    at org.apache.tools.ant.util.SplitClassLoader.loadClass(SplitClassLoader.java:58)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at FactorialTest.setUp(FactorialTest.java:13)
    at junit.framework.TestCase.runBare(TestCase.java:139)
    at junit.framework.TestResult$1.protect(TestResult.java:122)
    at junit.framework.TestResult.runProtected(TestResult.java:142)
    at junit.framework.TestResult.run(TestResult.java:125)
    at junit.framework.TestCase.run(TestCase.java:129)
    at junit.framework.TestSuite.runTest(TestSuite.java:252)
    at junit.framework.TestSuite.run(TestSuite.java:247)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:535)

Testcase: test_16 took 0.001 sec
    Caused an ERROR
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getProtectionDomain")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:886)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.Class.getProtectionDomain(Class.java:2299)
    at org.apache.tools.ant.AntClassLoader.defineClassFromData(AntClassLoader.java:1141)
    at org.apache.tools.ant.AntClassLoader.getClassFromStream(AntClassLoader.java:1318)
    at org.apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.java:1374)
    at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1335)
    at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1090)
    at org.apache.tools.ant.util.SplitClassLoader.loadClass(SplitClassLoader.java:58)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at FactorialTest.setUp(FactorialTest.java:13)
    at junit.framework.TestCase.runBare(TestCase.java:139)
    at junit.framework.TestResult$1.protect(TestResult.java:122)
    at junit.framework.TestResult.runProtected(TestResult.java:142)
    at junit.framework.TestResult.run(TestResult.java:125)
    at junit.framework.TestCase.run(TestCase.java:129)
    at junit.framework.TestSuite.runTest(TestSuite.java:252)
    at junit.framework.TestSuite.run(TestSuite.java:247)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:535)

Testcase: test_17 took 0.001 sec
    Caused an ERROR
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getProtectionDomain")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:886)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.Class.getProtectionDomain(Class.java:2299)
    at org.apache.tools.ant.AntClassLoader.defineClassFromData(AntClassLoader.java:1141)
    at org.apache.tools.ant.AntClassLoader.getClassFromStream(AntClassLoader.java:1318)
    at org.apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.java:1374)
    at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1335)
    at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1090)
    at org.apache.tools.ant.util.SplitClassLoader.loadClass(SplitClassLoader.java:58)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at FactorialTest.setUp(FactorialTest.java:13)
    at junit.framework.TestCase.runBare(TestCase.java:139)
    at junit.framework.TestResult$1.protect(TestResult.java:122)
    at junit.framework.TestResult.runProtected(TestResult.java:142)
    at junit.framework.TestResult.run(TestResult.java:125)
    at junit.framework.TestCase.run(TestCase.java:129)
    at junit.framework.TestSuite.runTest(TestSuite.java:252)
    at junit.framework.TestSuite.run(TestSuite.java:247)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:535)

Testcase: test_18 took 0.001 sec
    Caused an ERROR
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getProtectionDomain")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:886)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.Class.getProtectionDomain(Class.java:2299)
    at org.apache.tools.ant.AntClassLoader.defineClassFromData(AntClassLoader.java:1141)
    at org.apache.tools.ant.AntClassLoader.getClassFromStream(AntClassLoader.java:1318)
    at org.apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.java:1374)
    at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1335)
    at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1090)
    at org.apache.tools.ant.util.SplitClassLoader.loadClass(SplitClassLoader.java:58)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at FactorialTest.setUp(FactorialTest.java:13)
    at junit.framework.TestCase.runBare(TestCase.java:139)
    at junit.framework.TestResult$1.protect(TestResult.java:122)
    at junit.framework.TestResult.runProtected(TestResult.java:142)
    at junit.framework.TestResult.run(TestResult.java:125)
    at junit.framework.TestCase.run(TestCase.java:129)
    at junit.framework.TestSuite.runTest(TestSuite.java:252)
    at junit.framework.TestSuite.run(TestSuite.java:247)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:535)

--------------------
Tests run: 6, Failures: 0, Errors: 6, Skipped: 0, Time elapsed: 0.012 sec
====================
1-alex98 commented 4 years ago
cd "usr/attempts/active/8" ; ANT_OPTS="-ea -Dant.home=/usr -Dresource_dir=/code-workout/usr/resources/Java -Dwork_dir=/code-workout/usr/attempts/active/8 -Djava.security.manager -Djava.security.policy==file:/code-workout/usr/resources/Java/java.policy" ant -Dattempt_dir=usr/attempts/active/8 -Dbasedir=. -l ant.log -f ../../../../usr/resources/Java/build.xml

The command looks ok

1-alex98 commented 4 years ago
at FactorialTest.setUp(FactorialTest.java:13)

has no special grants and it seems creating a new instance : new Factorial() needs the getProtectionDomain Permission which let's the calling code know what policy restrictions apply to a class.

So what should I do add the getProtectionDomain Permission ? How is it solved in your set up? I don't yet see what it has to do with docker:thinking: But maybe I don't understand good enough what is going on.

1-alex98 commented 4 years ago

I went ahead and added that permission and now it works at least for the Factorial Exercise. Is adding the permission the intended solution? Should it be applied to the staging branch?

s-edwards commented 4 years ago

That grant should not be necessary--it should be covered by one of the other grants that is already in the java.policy file. I'm still thinking that the problem is one of the existing grants in java.policy that uses variable expansion is not working as intended (which is why there are a number of grants that use hard-coded path names instead), and that the JVM install location in your instance isn't covered by one of those because the path is wrong.

No questions should ever need specific grants of the nature you've added in this workaround, and indeed they don't on our production instance, or staging instance, or my dev instance.

1-alex98 commented 4 years ago

The jdk is found there:

alex@Codeworkout:/opt/codeworkout/code-workout$ docker-compose run web bash
Starting code-workout_db_dev_1  ... 
Starting code-workout_db_dev_1  ... done
root@a186590aaedd:/code-workout# which java
/usr/bin/java
root@a186590aaedd:/code-workout# ls -al /usr/java/bin
ls: cannot access '/usr/java/bin': No such file or directory
root@a186590aaedd:/code-workout# ls -al /usr/bin/java
lrwxrwxrwx 1 root root 22 Aug  7 13:00 /usr/bin/java -> /etc/alternatives/java
root@a186590aaedd:/code-workout# ls -al /etc/alternatives/java
lrwxrwxrwx 1 root root 46 Aug  7 13:00 /etc/alternatives/java -> /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
root@a186590aaedd:/code-workout# ls -al /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
-rwxr-xr-x 1 root root 6456 Apr 24 09:11 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java

And there is a

grant codeBase "file:/usr/lib/jvm-" {
    permission java.security.AllPermission;
};

But anyways if my understanding of the permission system is correct then the Class that has the lowest permissions on the stack trace is the one that decides on whether an action is legal. Hence the jdk location should be irrelevant. And the permissions on FactorialTest relevant.

The question for me is why does the AntClassLoader call Class.getProtectionDomain on loading the class, he does not do that in your setup (Otherwise it should fail there as well) then it seems so maybe it is cause I got a newer Ant version or java version :thinking:

1-alex98 commented 4 years ago
root@a186590aaedd:/code-workout# ant -version
Apache Ant(TM) version 1.9.9 compiled on July 22 2018

on the other hand I am not sure if the AntClassLoader comes from the ant.jar in the usr/resources/Java folder

s-edwards commented 4 years ago

The ant classes should be in your ANT_HOME's lib directory. The java.policy file grants permissions using the ${ant.home} system property, which normally would be set by ANT when it is running. It is plausible that the ${resource_dir} grant may be the issue instead, but I'm not sure.

To debug this, you can also use the alternative security manager, take a look at the alternate launch command shown in usr/resources/config.rb on lines 16-24. Pay attention to lines 18-19, which show an alternate security manager setup:

          "-Djava.security.manager=net.sf.webcat.plugins.javatddplugin.ProfilingSecurityManager " \
          "-DProfilingSecurityManager.output=security.txt " \

You can temporarily use that version of the Java command, and edit the output destination to the file name/location of your choice. It will replace the security manager with one that will actually not block permissions, but will dump the missing grants needed to make the permissions work. It's not perfect, but it can confirm what is missing a grant that matters in order to try to narrow the problem down.

I'd jump in and debug it for the docker launch, but this week there are two major conferences going on (ICER starts today, and L@S starts later this week) plus a conference paper deadline this weekend, plus the start of classes looming, so it will be a bit of time before I can try it myself. The profiling security manager will get you up and running, and allow you to fix the grant issue in parallel instead of making it a blocking issue.

1-alex98 commented 4 years ago

Don't worry I got a lot of time till the semester starts :D We start 1.November Yes I will try using the debug SecurityManager. Btw I verified that the ant_home is set correctly, so that should not be the issue.

1-alex98 commented 4 years ago

Also me adding

permission java.security.RuntimePermission "getProtectionDomain";

to the grant for all fixes it so it is not blocking. I just wanted to figure out what is actually wrong so I could potentially make a PR and rule that issue out.

1-alex98 commented 4 years ago

Using the debug set up I only get the following out put in the err.log file:

arg       : -l
quoted_arg: "-l"
arg       : usr/attempts/active/17/ant.log
quoted_arg: "usr/attempts/active/17/ant.log"
arg       : -f
quoted_arg: "-f"
arg       : usr/resources/Java/build.xml
quoted_arg: "usr/resources/Java/build.xml"
exec "$JAVACMD"  -classpath "$LOCALCLASSPATH" -Dant.home="$ANT_HOME" -Dant.library.dir="$ANT_LIB"  org.apache.tools.ant.launch.Launcher  -cp "$CLASSPATH"  "-logger" "org.apache.tools.ant.listener.ProfileLogger" "-Dattempt_dir=usr/attempts/active/17" "-Djava.security.manager=net.sf.webcat.plugins.javatddplugin.ProfilingSecurityManager" "-DProfilingSecurityManager.output=security.txt" "LOCALCLASSPATH=/code-workout/usr/resources/Java/JavaTddPluginSupport.jar" "-l" "usr/attempts/active/17/ant.log" "-f" "usr/resources/Java/build.xml"
Buildfile: /code-workout/usr/resources/Java/build.xml

No security.txt No ant.log nothing anywhere. Not even sure if this is really an error :sweat_smile:

s-edwards commented 4 years ago

Hmmm ... I see what you did. Yes, adding that to the main grant should be fine as a fix for this.

For the debugging security manager, the useful output would go in the specified output file (as written, that's security.txt in the CWD in which the process executes, but you can change that to use a fully qualified pathname to a location where you know permissions are ok--the default would go inside the docker image, if you're using a docker dev instance).

Also, I noticed the regular cmd value includes a -D for ant.home on line 27, and if ANT_HOME isn't defined as an env variable, that may cause the grant for ANT jars to fail to be meaningful. IIRC, the reason that -D is there is because ANT sets this property, but I believe the security manager takes effect at JVM launch, which means the java.policy file is processed before ANT begins running, so ant.home is not yet defined by ANT when the policy file is loaded.

Which JVM version are you using?

chelleyyy2207 commented 3 years ago

👌