From 6a779fc730c8e09258a3799005759604a92b5429 Mon Sep 17 00:00:00 2001 From: Chris Earle Date: Tue, 15 Sep 2015 10:56:33 -0400 Subject: [PATCH 1/5] Adding support for invokedynamic with Groovy scripts. --- .../groovy/GroovyScriptEngineService.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java b/core/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java index 9c3cf4f86cc..7b73cdbc5fd 100644 --- a/core/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java +++ b/core/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java @@ -57,19 +57,52 @@ import java.util.Map; */ public class GroovyScriptEngineService extends AbstractComponent implements ScriptEngineService { + /** + * The name of the scripting engine/language. + */ public static final String NAME = "groovy"; + /** + * The setting to enable or disable invokedynamic instruction support in Java 7+. + *

+ * This should only be used with Java 7u60 or later because of issues related to the instruction. + * The invokedynamic instruction allows near-Java performance from many of Groovy's + * dynamic features, which is why it is enabled by default. + *

+ * Note: If this is disabled because invokedynamic is causing issues, then the Groovy + * indy jar needs to be replaced by the non-indy variant of it on the classpath (e.g., + * groovy-all-2.4.4-indy.jar should be replaced by groovy-all-2.4.4.jar). + *

+ * Defaults to {@code true}. + */ + public static final String GROOVY_INDY_ENABLED = "script.groovy.indy"; + /** + * The name of the Groovy compiler setting to use associated with activating invokedynamic support. + */ + public static final String GROOVY_INDY_SETTING_NAME = "indy"; + private final GroovyClassLoader loader; @Inject public GroovyScriptEngineService(Settings settings) { super(settings); + ImportCustomizer imports = new ImportCustomizer(); imports.addStarImports("org.joda.time"); imports.addStaticStars("java.lang.Math"); + CompilerConfiguration config = new CompilerConfiguration(); + config.addCompilationCustomizers(imports); // Add BigDecimal -> Double transformer config.addCompilationCustomizers(new GroovyBigDecimalTransformer(CompilePhase.CONVERSION)); + + // Implicitly requires Java 7u60 or later to get valid support + if (settings.getAsBoolean(GROOVY_INDY_ENABLED, true)) { + // maintain any default optimizations + config.getOptimizationOptions().put(GROOVY_INDY_SETTING_NAME, true); + } + + // Groovy class loader to isolate Groovy-land code this.loader = new GroovyClassLoader(getClass().getClassLoader(), config); } From 7828460ef63645f78ceba4e7d82aeb2b241f0993 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Tue, 15 Sep 2015 12:00:49 -0400 Subject: [PATCH 2/5] Give groovy scripts read access to groovy.indy.logging, needed for IndyInterface bootstrap. --- .../java/org/elasticsearch/bootstrap/ESPolicy.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java b/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java index fdc0d4e4acd..55fae24296b 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java @@ -26,9 +26,11 @@ import java.net.URL; import java.security.CodeSource; import java.security.Permission; import java.security.PermissionCollection; +import java.security.Permissions; import java.security.Policy; import java.security.ProtectionDomain; import java.security.URIParameter; +import java.util.PropertyPermission; /** custom policy for union of static and dynamic permissions */ final class ESPolicy extends Policy { @@ -38,11 +40,15 @@ final class ESPolicy extends Policy { final Policy template; final PermissionCollection dynamic; + final PermissionCollection groovy; public ESPolicy(PermissionCollection dynamic) throws Exception { URI uri = getClass().getResource(POLICY_RESOURCE).toURI(); this.template = Policy.getInstance("JavaPolicy", new URIParameter(uri)); this.dynamic = dynamic; + this.groovy = new Permissions(); + // groovy IndyInterface bootstrap requires this property + groovy.add(new PropertyPermission("groovy.indy.logging", "read")); } @Override @SuppressForbidden(reason = "fast equals check is desired") @@ -54,9 +60,9 @@ final class ESPolicy extends Policy { // location can be null... ??? nobody knows // https://bugs.openjdk.java.net/browse/JDK-8129972 if (location != null) { - // run groovy scripts with no permissions + // run groovy scripts with no permissions (except logging property) if ("/groovy/script".equals(location.getFile())) { - return false; + return groovy.implies(permission); } } } From 3e626d0dd835a4025330ea369dfd3ab6817076f6 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Tue, 15 Sep 2015 12:03:33 -0400 Subject: [PATCH 3/5] add paranoia --- core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java b/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java index 55fae24296b..06e51b3a7ea 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java @@ -49,6 +49,7 @@ final class ESPolicy extends Policy { this.groovy = new Permissions(); // groovy IndyInterface bootstrap requires this property groovy.add(new PropertyPermission("groovy.indy.logging", "read")); + groovy.setReadOnly(); } @Override @SuppressForbidden(reason = "fast equals check is desired") From ffe50d50212ec636209f40cb54bcb11b04f2023a Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Tue, 15 Sep 2015 14:14:44 -0400 Subject: [PATCH 4/5] really get groovy indy working --- .../org/elasticsearch/bootstrap/ESPolicy.java | 16 +++++----- .../org/elasticsearch/bootstrap/groovy.policy | 31 +++++++++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 core/src/main/resources/org/elasticsearch/bootstrap/groovy.policy diff --git a/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java b/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java index 06e51b3a7ea..9db66ca9c14 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java @@ -37,19 +37,19 @@ final class ESPolicy extends Policy { /** template policy file, the one used in tests */ static final String POLICY_RESOURCE = "security.policy"; + /** limited policy for groovy scripts */ + static final String GROOVY_RESOURCE = "groovy.policy"; final Policy template; + final Policy groovy; final PermissionCollection dynamic; - final PermissionCollection groovy; public ESPolicy(PermissionCollection dynamic) throws Exception { - URI uri = getClass().getResource(POLICY_RESOURCE).toURI(); - this.template = Policy.getInstance("JavaPolicy", new URIParameter(uri)); + URI policyUri = getClass().getResource(POLICY_RESOURCE).toURI(); + URI groovyUri = getClass().getResource(GROOVY_RESOURCE).toURI(); + this.template = Policy.getInstance("JavaPolicy", new URIParameter(policyUri)); + this.groovy = Policy.getInstance("JavaPolicy", new URIParameter(groovyUri)); this.dynamic = dynamic; - this.groovy = new Permissions(); - // groovy IndyInterface bootstrap requires this property - groovy.add(new PropertyPermission("groovy.indy.logging", "read")); - groovy.setReadOnly(); } @Override @SuppressForbidden(reason = "fast equals check is desired") @@ -63,7 +63,7 @@ final class ESPolicy extends Policy { if (location != null) { // run groovy scripts with no permissions (except logging property) if ("/groovy/script".equals(location.getFile())) { - return groovy.implies(permission); + return groovy.implies(domain, permission); } } } diff --git a/core/src/main/resources/org/elasticsearch/bootstrap/groovy.policy b/core/src/main/resources/org/elasticsearch/bootstrap/groovy.policy new file mode 100644 index 00000000000..4e1275827d9 --- /dev/null +++ b/core/src/main/resources/org/elasticsearch/bootstrap/groovy.policy @@ -0,0 +1,31 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Limited security policy for groovy scripts. + * This is what is needed for its invokeDynamic functionality to work. + */ +grant { + + // groovy IndyInterface bootstrap requires this property for indy logging + permission java.util.PropertyPermission "groovy.indy.logging", "read"; + + // needed IndyInterface selectMethod (setCallSiteTarget) + permission java.lang.RuntimePermission "getClassLoader"; +}; From ce50269e0ac360a1b82b847c9076bf1d67b5c3b6 Mon Sep 17 00:00:00 2001 From: Chris Earle Date: Thu, 17 Sep 2015 16:43:22 -0400 Subject: [PATCH 5/5] Removing note about 7u60 from master, which is Java 8+ only --- .../script/groovy/GroovyScriptEngineService.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java b/core/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java index 7b73cdbc5fd..f128edcd9f0 100644 --- a/core/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java +++ b/core/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java @@ -64,10 +64,6 @@ public class GroovyScriptEngineService extends AbstractComponent implements Scri /** * The setting to enable or disable invokedynamic instruction support in Java 7+. *

- * This should only be used with Java 7u60 or later because of issues related to the instruction. - * The invokedynamic instruction allows near-Java performance from many of Groovy's - * dynamic features, which is why it is enabled by default. - *

* Note: If this is disabled because invokedynamic is causing issues, then the Groovy * indy jar needs to be replaced by the non-indy variant of it on the classpath (e.g., * groovy-all-2.4.4-indy.jar should be replaced by groovy-all-2.4.4.jar).