From ed8cd5520e3a433a86cb41c49b8e1353b4401c53 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Mon, 16 Dec 2024 15:00:55 -0500 Subject: [PATCH] [POC] [Security Manager Replacement] GraalVM sandboxing Signed-off-by: Andriy Redko --- libs/espresso-sm/build.gradle | 44 +++++++ .../opensearch/espresso/sandbox/Sandbox.java | 113 ++++++++++++++++++ .../identity/shiro/ShiroIdentityPlugin.java | 12 ++ .../opensearch/bootstrap/QuickBoostrap.java | 60 ++++++++++ 4 files changed, 229 insertions(+) create mode 100644 libs/espresso-sm/build.gradle create mode 100644 libs/espresso-sm/src/main/java/org/opensearch/espresso/sandbox/Sandbox.java create mode 100644 server/src/main/java/org/opensearch/bootstrap/QuickBoostrap.java diff --git a/libs/espresso-sm/build.gradle b/libs/espresso-sm/build.gradle new file mode 100644 index 0000000000000..726ff0b216a50 --- /dev/null +++ b/libs/espresso-sm/build.gradle @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import org.opensearch.gradle.info.BuildParams + +apply plugin: 'opensearch.publish' + +base { + archivesName = 'esspresso-sm' +} + +dependencies { + implementation "org.graalvm.polyglot:polyglot:24.1.1" + implementation "org.graalvm.sdk:nativeimage:24.1.1" + implementation "org.graalvm.sdk:collections:24.1.1" + implementation "org.graalvm.sdk:word:24.1.1" + implementation "org.graalvm.sdk:jniutils:24.1.1" + implementation "org.graalvm.llvm:llvm-api:24.1.1" + implementation "org.graalvm.llvm:llvm-language:24.1.1" + implementation "org.graalvm.llvm:llvm-language-native:24.1.1" + implementation "org.graalvm.llvm:llvm-language-native-resources:24.1.1" + implementation "org.graalvm.llvm:llvm-language-nfi:24.1.1" + implementation "org.graalvm.truffle:truffle-api:24.1.1" + implementation "org.graalvm.truffle:truffle-compiler:24.1.1" + implementation "org.graalvm.truffle:truffle-nfi:24.1.1" + implementation "org.graalvm.truffle:truffle-nfi-libffi:24.1.1" + implementation "org.graalvm.truffle:truffle-runtime:24.1.1" + implementation "org.graalvm.espresso:espresso-language:24.1.1" + implementation "org.graalvm.espresso:espresso-libs-resources-linux-amd64:24.1.1" + implementation "org.graalvm.espresso:espresso-runtime-resources-linux-amd64:24.1.1" + implementation project(":server") +} + +tasks.named('forbiddenApisMain').configure { + replaceSignatureFiles 'jdk-signatures' +} diff --git a/libs/espresso-sm/src/main/java/org/opensearch/espresso/sandbox/Sandbox.java b/libs/espresso-sm/src/main/java/org/opensearch/espresso/sandbox/Sandbox.java new file mode 100644 index 0000000000000..bfeccaac2be45 --- /dev/null +++ b/libs/espresso-sm/src/main/java/org/opensearch/espresso/sandbox/Sandbox.java @@ -0,0 +1,113 @@ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.espresso.sandbox; + +import org.opensearch.client.Client; +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.settings.Settings; +import org.opensearch.threadpool.ThreadPool; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Engine; +import org.graalvm.polyglot.HostAccess; +import org.graalvm.polyglot.PolyglotAccess; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.io.IOAccess; + +/** + * GraalVM Sandbox + */ +public class Sandbox { + /** + * GraalVM Sandbox runner + */ + public static void main(String[] args) throws IOException, InterruptedException { + final String opensearchHome = args[0]; + System.out.println("OpenSearch home: " + opensearchHome); + + final Engine engine = Engine.newBuilder().build(); + + System.out.println("Host JVM version: " + Runtime.version()); + final String plugin = loadPlugin(opensearchHome, engine); + System.out.println(plugin); + } + + private static String loadPlugin(String opensearchHome, Engine engine) throws IOException { + // See please: + // - https://github.com/oracle/graal/issues/10239 + // - + // https://github.com/oracle/graal/blob/master/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java + // - + // https://github.com/oracle/graal/blob/master/espresso/src/com.oracle.truffle.espresso.launcher/src/com/oracle/truffle/espresso/launcher/EspressoLauncher.java + final Context context = Context.newBuilder("java") + .option("java.JavaHome", "/usr/lib/jvm/java-21-openjdk-amd64/") + .option( + "java.Classpath", + ("${opensearchHome}/lib/lucene-core-9.12.0.jar:" + + "${opensearchHome}/lib/opensearch-cli-3.0.0-SNAPSHOT.jar:" + + "${opensearchHome}/lib/jackson-core-2.17.2.jar:" + + "${opensearchHome}/lib/jackson-dataformat-cbor-2.17.2.jar:" + + "${opensearchHome}/lib/jackson-dataformat-smile-2.17.2.jar:" + + "${opensearchHome}/lib/jackson-dataformat-yaml-2.17.2.jar:" + + "${opensearchHome}/lib/snakeyaml-2.1.jar:" + + "${opensearchHome}/lib/opensearch-3.0.0-SNAPSHOT.jar:" + + "${opensearchHome}/lib/opensearch-core-3.0.0-SNAPSHOT.jar:" + + "${opensearchHome}/lib/opensearch-common-3.0.0-SNAPSHOT.jar:" + + "${opensearchHome}/lib/opensearch-x-content-3.0.0-SNAPSHOT.jar:" + + "${opensearchHome}/lib/opensearch-secure-sm-3.0.0-SNAPSHOT.jar:" + + "${opensearchHome}/lib/log4j-core-2.21.0.jar:" + + "${opensearchHome}/lib/log4j-jul-2.21.0.jar:" + + "${opensearchHome}/lib/log4j-api-2.21.0.jar:" + + "${opensearchHome}/plugins/identity-shiro/slf4j-api-1.7.36.jar:" + + "${opensearchHome}/plugins/identity-shiro/passay-1.6.3.jar:" + + "${opensearchHome}/plugins/identity-shiro/identity-shiro-3.0.0-SNAPSHOT.jar:" + + "${opensearchHome}/plugins/identity-shiro/shiro-core-1.13.0.jar").replaceAll( + "[$][{]opensearchHome[}]", + opensearchHome + ) + ) + .option("java.Properties.java.security.manager", "allow") + .option("java.PolyglotInterfaceMappings", getInterfaceMappings()) + .option("java.Polyglot", "true") + .allowExperimentalOptions(true) + .allowNativeAccess(true) + .allowCreateThread(true) + .allowHostAccess(HostAccess.NONE) + .allowIO(IOAccess.NONE) + .allowPolyglotAccess(PolyglotAccess.newBuilder().allowBindingsAccess("java").build()) + .engine(engine) + .build(); + + final Value runtime = context.getBindings("java").getMember("java.lang.Runtime"); + System.out.println("Polyglot JVM version: " + runtime.invokeMember("version").toString()); + + final Path homePath = new File(opensearchHome).toPath(); + final Path configPath = new File(opensearchHome + "/config").toPath(); + context.getBindings("java") + .getMember("org.opensearch.bootstrap.QuickBoostrap") + .invokeMember("bootstrap", configPath.toString(), homePath.toString()); + + final Value securityManager = context.getBindings("java").getMember("java.lang.System").invokeMember("getSecurityManager"); + System.out.println("Security Manager? " + securityManager.toString()); + + final Value settings = context.getBindings("java").getMember("org.opensearch.common.settings.Settings").getMember("EMPTY"); + final Value result = context.getBindings("java").getMember("org.opensearch.identity.shiro.ShiroIdentityPlugin"); + final Value instance = result.newInstance(settings); + System.out.println("Shiro Plugin? " + instance.toString()); + + final Client client = new NodeClient(Settings.EMPTY, new ThreadPool(Settings.EMPTY)); + final Value socket = instance.invokeMember("getSocket", client); + return socket.toString(); + } + + private static String getInterfaceMappings() { + return "org.opensearch.client.Client;" + "org.opensearch.client.AdminClient;"; + } +} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java index 2da788242a745..d542b2aa4b2c3 100644 --- a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java @@ -39,6 +39,8 @@ import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; +import java.io.IOException; +import java.net.ServerSocket; import java.util.Collection; import java.util.Collections; import java.util.function.Supplier; @@ -138,6 +140,16 @@ public void handleRequest(RestRequest request, RestChannel channel, NodeClient c } } + /** + * Deliberately introducing the network access attempt to trigger SecurityException + */ + public ServerSocket getSocket(Client client) throws IOException { + System.out.println("Client? " + client); + System.out.println("AdminClient? " + client.admin()); + client.admin().cluster().prepareState().execute().actionGet(); + return new ServerSocket(0); + } + public PluginSubject getPluginSubject(Plugin plugin) { return new ShiroPluginSubject(threadPool); } diff --git a/server/src/main/java/org/opensearch/bootstrap/QuickBoostrap.java b/server/src/main/java/org/opensearch/bootstrap/QuickBoostrap.java new file mode 100644 index 0000000000000..923fb9e19c6eb --- /dev/null +++ b/server/src/main/java/org/opensearch/bootstrap/QuickBoostrap.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.bootstrap; + +import org.opensearch.cli.UserException; +import org.opensearch.common.logging.LogConfigurator; +import org.opensearch.common.settings.Settings; +import org.opensearch.env.Environment; +import org.opensearch.node.InternalSettingsPreparer; +import org.opensearch.node.Node; +import org.opensearch.node.NodeValidationException; + +import java.io.IOException; +import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; + +/** + * Quick bootstrap + */ +public class QuickBoostrap { + /** + * This method is invoked by {@link OpenSearch#main(String[])} to startup opensearch. + */ + public static void bootstrap(final String configPath, final String homePath) throws BootstrapException, NodeValidationException, + UserException { + + // force the class initializer for BootstrapInfo to run before + // the security manager is installed + BootstrapInfo.init(); + + Settings settings = Settings.builder().put(Environment.PATH_HOME_SETTING.getKey(), homePath).build(); + final Environment environment = InternalSettingsPreparer.prepareEnvironment( + settings, + Collections.emptyMap(), + Paths.get(configPath), + // HOSTNAME is set by opensearch-env and opensearch-env.bat so it is always available + () -> System.getenv("HOSTNAME") + ); + + LogConfigurator.setNodeName(Node.NODE_NAME_SETTING.get(environment.settings())); + try { + LogConfigurator.configure(environment); + } catch (IOException e) { + throw new BootstrapException(e); + } + + try { + Security.configure(environment, BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING.get(settings)); + } catch (IOException | NoSuchAlgorithmException e) { + throw new BootstrapException(e); + } + } +}