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