1

Especially when using mods that I just download from the internet, I'd like a bit more security on my MC install -- I don't like that the MC instance can access any file on my system. Fortunately, I'm using MultiMC, so I can add custom arguments for the Java VM. So, I simply added -Djava.security.manager -Djava.security.policy=../security.policy to the custom Java options and I created a security policy file in the MultiMC instance directory. To write my policy file, I launched MC and added the appropriate security policy to deal with any errors that came up.

This worked up until I had the following policy file:

// -Djava.security.manager -Djava.security.policy=../security.policy
grant {
  // Property permissions
  permission java.util.PropertyPermission "user.dir", "read"; // Allow MC to know user home dir
  permission java.util.PropertyPermission "java.class.path", "read"; // Get the path definition of a class
  permission java.util.PropertyPermission "fabric.*", "read"; // FabricMC properties
  permission java.util.PropertyPermission "log4j2.formatMsgNoLookups", "read, write"; // Allow MC to change log4j properties

  // Runtime permissions
  permission java.lang.RuntimePermission "setDefaultUncaughtExceptionHandler"; // Allow MC to override the JVM's exception handler (to catch errors)
  permission java.lang.RuntimePermission "getProtectionDomain";
  permission java.lang.RuntimePermission "shutdownHooks"; // Allow MC to do stuff before the application quits
  permission java.lang.RuntimePermission "setContextClassLoader";
  
  // File grants -- Each of these grants access to certain MultiMC files
  // The working directory here is the .minecraft folder in the instance folder
  // So we need to hop up 3 levels to get to the MultiMC directory
  permission java.io.FilePermission "${user.dir}/../../../bin/-", "read, execute";
  permission java.io.FilePermission "${user.dir}/../../../libraries/-", "read, execute";
  permission java.io.FilePermission "${user.dir}/-", "read, write, delete, execute"; // Allow MC to do anything in the .minecraft directory
};

Running with this security policy gave the following error:

Failed to start Minecraft:
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.multimc.onesix.OneSixLauncher.launchWithMainClass(OneSixLauncher.java:210)
    at org.multimc.onesix.OneSixLauncher.launch(OneSixLauncher.java:245)
    at org.multimc.EntryPoint.listen(EntryPoint.java:143)
    at org.multimc.EntryPoint.main(EntryPoint.java:34)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.logging.log4j.util.PropertiesUtil
    at org.apache.logging.log4j.status.StatusLogger.<clinit>(StatusLogger.java:78)
    at org.apache.logging.log4j.LogManager.<clinit>(LogManager.java:61)
    at net.fabricmc.loader.impl.game.minecraft.Log4jLogHandler.getLogger(Log4jLogHandler.java:59)
    at net.fabricmc.loader.impl.game.minecraft.Log4jLogHandler.shouldLog(Log4jLogHandler.java:45)
    at net.fabricmc.loader.impl.util.log.Log.logFormat(Log.java:113)
    at net.fabricmc.loader.impl.util.log.Log.info(Log.java:66)
    at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:126)
    at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:71)
    at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23)
    ... 8 more

Which doesn't give me any clear permissions to fix. This would seem to be an issue with Java's ability to access the log4j JAR file, but I explicitly gave it read and execute access to this file. Opening up the file permissions further to allow full access to anything on my system didn't help.

Furthermore, I decided to try opening up permissions completely just as a test:

// -Djava.security.manager -Djava.security.policy=../security.policy
grant {
  permission java.security.AllPermission;
};

This generated another error:

[11:57:21] [main/INFO]: Loading Minecraft 1.18.1 with Fabric Loader 0.12.12
[11:57:21] [main/ERROR]: Incompatible mod set!
net.fabricmc.loader.impl.FormattedException: net.fabricmc.loader.impl.discovery.ModResolutionException: Mod discovery failed!
    at net.fabricmc.loader.impl.FabricLoaderImpl.load(FabricLoaderImpl.java:189) ~[fabric-loader-0.12.12.jar:?]
    at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:142) ~[fabric-loader-0.12.12.jar:?]
    at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:71) [fabric-loader-0.12.12.jar:?]
    at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23) [fabric-loader-0.12.12.jar:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
    at org.multimc.onesix.OneSixLauncher.launchWithMainClass(OneSixLauncher.java:210) [NewLaunch.jar:?]
    at org.multimc.onesix.OneSixLauncher.launch(OneSixLauncher.java:245) [NewLaunch.jar:?]
    at org.multimc.EntryPoint.listen(EntryPoint.java:143) [NewLaunch.jar:?]
    at org.multimc.EntryPoint.main(EntryPoint.java:34) [NewLaunch.jar:?]
Caused by: net.fabricmc.loader.impl.discovery.ModResolutionException: Mod discovery failed!
    at net.fabricmc.loader.impl.discovery.ModDiscoverer.lambda$discoverMods$1(ModDiscoverer.java:122) ~[fabric-loader-0.12.12.jar:?]
    at net.fabricmc.loader.impl.util.ExceptionUtil.gatherExceptions(ExceptionUtil.java:33) ~[fabric-loader-0.12.12.jar:?]
    at net.fabricmc.loader.impl.discovery.ModDiscoverer.discoverMods(ModDiscoverer.java:122) ~[fabric-loader-0.12.12.jar:?]
    at net.fabricmc.loader.impl.FabricLoaderImpl.setup(FabricLoaderImpl.java:204) ~[fabric-loader-0.12.12.jar:?]
    at net.fabricmc.loader.impl.FabricLoaderImpl.load(FabricLoaderImpl.java:187) ~[fabric-loader-0.12.12.jar:?]
    ... 11 more
    Suppressed: java.lang.RuntimeException: java.lang.RuntimeException: Error analyzing /home/user/Documents/MultiMC/instances/Fabric 1.18.1 (copy)/.minecraft/mods/optifabric-1.12.10.jar: java.security.AccessControlException: access denied ("java.io.FilePermission" "/home/user/Documents/MultiMC/instances/Fabric 1.18.1 (copy)/.minecraft/mods/optifabric-1.12.10.jar" "read")
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?]
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[?:?]
        at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?]
        at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?]
        at java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[?:?]
        at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:562) ~[?:?]
        at java.util.concurrent.ForkJoinTask.reportExecutionException(ForkJoinTask.java:604) ~[?:?]
        at java.util.concurrent.ForkJoinTask.get(ForkJoinTask.java:981) ~[?:?]
        at net.fabricmc.loader.impl.discovery.ModDiscoverer.discoverMods(ModDiscoverer.java:119) ~[fabric-loader-0.12.12.jar:?]
        at net.fabricmc.loader.impl.FabricLoaderImpl.setup(FabricLoaderImpl.java:204) ~[fabric-loader-0.12.12.jar:?]
        at net.fabricmc.loader.impl.FabricLoaderImpl.load(FabricLoaderImpl.java:187) ~[fabric-loader-0.12.12.jar:?]
        at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:142) ~[fabric-loader-0.12.12.jar:?]
        at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:71) [fabric-loader-0.12.12.jar:?]
        at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23) [fabric-loader-0.12.12.jar:?]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
        at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
        at org.multimc.onesix.OneSixLauncher.launchWithMainClass(OneSixLauncher.java:210) [NewLaunch.jar:?]
        at org.multimc.onesix.OneSixLauncher.launch(OneSixLauncher.java:245) [NewLaunch.jar:?]
        at org.multimc.EntryPoint.listen(EntryPoint.java:143) [NewLaunch.jar:?]
        at org.multimc.EntryPoint.main(EntryPoint.java:34) [NewLaunch.jar:?]
...

I truncated the stack trace because it just repeats on and on for each mod. What that boils down to is a lot of file access denied errors. What confuses me is that I pretty explicitly granted access to everything. So this makes me think that permissions that I grant are not being properly applied in the first place, which could explain my earlier permissions error. I should note that MC runs perfectly without any security policy.

Are these errors related? And how could I go about resolving this?

(Also perhaps this question would be better for StackOverflow? The fact that it was rather specific to the Minecraft codebase made it seem more appropriate for here)

3
  • 1
    Yeah, this question would be better on StackOverflow. But I would still leave this question up in case someone with modding knowledge could help.
    – BunnyMerz
    Jan 10 at 17:22
  • 1
    Strictly speaking this is technical support for modded Minecraft, but the support is independent of the (playability of) mods or Minecraft. The problem in this question is not caused by modding. It would be a better fit for SO, but please use the appropriate reason in that case.
    – Joachim
    Jan 15 at 10:43
  • I am aware that this is not caused by modding, but it is specific to the Fabric codebase, so I thought it made the most sense to ask somewhere where people might be familiar. In the event that somebody has the same issue as me and finds this post, the closest thing I have to a solution is below.
    – somebody0
    Jan 15 at 16:18

1 Answer 1

0

It occurred to me that I should probably get my policy file working with Vanilla minecraft rather than supporting all the mods right from the get-go. I also found the -Djava.security.debug="access,failure" option, which helped debug quite a bit by printing out essentially every access attempt (success and failure) in addition to detailed reports on the execution context of a failure. I managed to get the following policy to work with Vanilla MC:

grant {
  // To start, the not great stuff:
  permission java.lang.RuntimePermission "getenv.*"; // Required by log4j
  permission java.lang.RuntimePermission "getStackWalkerWithClassReference"; // Required by log4j. Can get objects up the chain of calling classes
  permission java.lang.RuntimePermission "accessDeclaredMembers"; // Required by log4j. Can only read function names, not an attack vector (since these are public domain)
  
  permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; // Minecraft needs this, which isn't great. This basically allows it to call any function discarding qualifiers
  
  // Ok, so this looks really dangerous: Loading native libraries which have no permission checks
  // However, by the permissions below, MC can't modify the library search path. So it can only load the allowed natives from the allowed folders
  // Which, if everything works properly, it shouldn't be able to modify.
  permission java.lang.RuntimePermission "loadLibrary.lwjgl";
  permission java.lang.RuntimePermission "loadLibrary.lwjgl_opengl";
  permission java.lang.RuntimePermission "loadLibrary.lwjgl_stb";
  
  // Now, the more reasonable permissions:
  // Property permissions
  permission java.util.PropertyPermission "user.dir", "read"; // Allow MC to know user home dir
  permission java.util.PropertyPermission "java.class.path", "read"; // Get the path definition of a class
  permission java.util.PropertyPermission "log4j2.formatMsgNoLookups", "read, write"; // Allow MC to change log4j properties
  permission java.util.PropertyPermission "max.bg.threads", "read"; // https://www.minecraft.net/en-us/article/caves---cliffs--part-ii-out-today-java
  permission java.util.PropertyPermission "java.awt.headless", "read, write"; // Let MC tell Java to connect to the window manager
  permission java.util.PropertyPermission "java.vm.info", "read"; // Allow MC to get java info
  permission java.util.PropertyPermission "org.lwjgl.*", "read"; // Allow LWJGL properties
  permission java.util.PropertyPermission "java.library.path", "read"; // Allow shared library discovery
  permission java.util.PropertyPermission "minecraft.*", "read"; // Allow all minecraft associated properties
  permission java.util.PropertyPermission "sun.arch.data.model", "read"; // Needed to know if running on 32 or 64 bit
  permission java.util.PropertyPermission "SuffixArray.*", "read"; // Seems like a library property. Weird
  permission java.util.PropertyPermission "realms.*", "read"; // Realms stuff

  // Runtime permissions
  permission java.lang.RuntimePermission "setDefaultUncaughtExceptionHandler"; // Allow MC to override the JVM's exception handler (to catch errors)
  permission java.lang.RuntimePermission "getProtectionDomain"; // Not sure why MC uses this... But it's safe. Could expose local file names, but this can already be obtained
  permission java.lang.RuntimePermission "shutdownHooks"; // Allow MC to do stuff before the application quits
  permission java.lang.RuntimePermission "modifyThread"; // Modify threads. Could stop and mess up things, but can't do anything to the security manager
  permission java.lang.RuntimePermission "setIO"; // Allow override of stdout/stderr/stdin to log
  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc"; // Compromises security of sun.misc, but no serious implications
  permission java.lang.RuntimePermission "accessClassInPackage.jdk.jfr.internal.handlers"; // Compromises the security of the flight recorder, but no serious implications
  
  // Class loader stuff. Needed for dynamic loading.
  permission java.lang.RuntimePermission "setContextClassLoader";
  permission java.lang.RuntimePermission "createClassLoader";
  permission java.lang.RuntimePermission "getClassLoader";
  
  // File grants -- Each of these grants access to certain MultiMC files
  // The working directory here is the .minecraft folder in the instance folder
  // So we need to hop up 3 levels to get to the MultiMC directory
  permission java.io.FilePermission "${user.dir}/../../../bin/-", "read, execute";
  permission java.io.FilePermission "${user.dir}/../../../libraries/-", "read, execute";
  permission java.io.FilePermission "${user.dir}/../../../assets/-", "read";
  permission java.io.FilePermission "${user.dir}/../natives/-", "read, execute";
  permission java.io.FilePermission "${user.dir}/-", "read, write, delete"; // Allow MC to do anything in the .minecraft directory
  
  permission java.net.SocketPermission "api.minecraftservices.com:443", "connect,resolve"; // MC needs to connect to this :(
  
  permission jdk.jfr.FlightRecorderPermission "registerEvent"; // Used during game runtime
  permission jdk.jfr.FlightRecorderPermission "accessFlightRecorder"; // Used during game runtime
};

This worked for version 1.8.1 running in MultiMC where the policy was placed in the instance directory as security.policy and -Djava.security.manager -Djava.security.policy=../security.policy were added as Java options. These are the bare minimum permissions required to start a singleplayer world. Multiplayer will require additional permissions. Additionally, a lot of the logging and diagnostic functionality is broken because it isn't granted permissions that it needs, but the game seems to run fine without it. If you do use my policy file above, it comes with no warranty ;).

I tried to get the same to work with Fabric, but I got a weird issue where several file accesses to my mods would succeed, then suddenly start failing. This causes the problem I mentioned above. Around these I noticed:

access: domain that failed ProtectionDomain  null
 null
 <no principals>
 java.security.Permissions@311b7e8c (
 ("java.lang.RuntimePermission" "getClassLoader")
 ("java.lang.RuntimePermission" "setContextClassLoader")
)

That's basically saying that the file error was encountered in a protection domain with no policy, which wouldn't exist unless it was deliberately created. In addition, I noticed MC using several permissions related to the permission manager, so it looks like Fabric/MC already has some form of sandboxing code. It's something I need to look into more, but it seems the core of my question was resolved.

Not the answer you're looking for? Browse other questions tagged .