New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Groovy as a scripting language, add groovy sandboxing #6233
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ | |
import java.io.FileInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.util.Locale; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.concurrent.ConcurrentMap; | ||
|
@@ -54,6 +55,10 @@ | |
*/ | ||
public class ScriptService extends AbstractComponent { | ||
|
||
public static final String DEFAULT_SCRIPTING_LANGUAGE_SETTING = "script.default_lang"; | ||
public static final String DISABLE_DYNAMIC_SCRIPTING_SETTING = "script.disable_dynamic"; | ||
public static final String DISABLE_DYNAMIC_SCRIPTING_DEFAULT = "sandbox"; | ||
|
||
private final String defaultLang; | ||
|
||
private final ImmutableMap<String, ScriptEngineService> scriptEngines; | ||
|
@@ -63,7 +68,38 @@ public class ScriptService extends AbstractComponent { | |
private final Cache<CacheKey, CompiledScript> cache; | ||
private final File scriptsDirectory; | ||
|
||
private final boolean disableDynamic; | ||
private final DynamicScriptDisabling dynamicScriptingDisabled; | ||
|
||
/** | ||
* Enum defining the different dynamic settings for scripting, either | ||
* ONLY_DISK_ALLOWED (scripts must be placed on disk), EVERYTHING_ALLOWED | ||
* (all dynamic scripting is enabled), or SANDBOXED_ONLY (only sandboxed | ||
* scripting languages are allowed) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. any chance we can make this a constant? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I will make everything a constant that can be for this. |
||
*/ | ||
enum DynamicScriptDisabling { | ||
EVERYTHING_ALLOWED, | ||
ONLY_DISK_ALLOWED, | ||
SANDBOXED_ONLY; | ||
|
||
public static final DynamicScriptDisabling parse(String s) { | ||
switch (s.toLowerCase(Locale.ROOT)) { | ||
// true for "disable_dynamic" means only on-disk scripts are enabled | ||
case "true": | ||
case "all": | ||
return ONLY_DISK_ALLOWED; | ||
// false for "disable_dynamic" means all scripts are enabled | ||
case "false": | ||
case "none": | ||
return EVERYTHING_ALLOWED; | ||
// only sandboxed scripting is enabled | ||
case "sandbox": | ||
case "sandboxed": | ||
return SANDBOXED_ONLY; | ||
default: | ||
throw new ElasticsearchIllegalArgumentException("Unrecognized script allowance setting: [" + s + "]"); | ||
} | ||
} | ||
} | ||
|
||
@Inject | ||
public ScriptService(Settings settings, Environment env, Set<ScriptEngineService> scriptEngines, | ||
|
@@ -74,8 +110,8 @@ public ScriptService(Settings settings, Environment env, Set<ScriptEngineService | |
TimeValue cacheExpire = componentSettings.getAsTime("cache.expire", null); | ||
logger.debug("using script cache with max_size [{}], expire [{}]", cacheMaxSize, cacheExpire); | ||
|
||
this.defaultLang = componentSettings.get("default_lang", "mvel"); | ||
this.disableDynamic = componentSettings.getAsBoolean("disable_dynamic", true); | ||
this.defaultLang = settings.get(DEFAULT_SCRIPTING_LANGUAGE_SETTING, "mvel"); | ||
this.dynamicScriptingDisabled = DynamicScriptDisabling.parse(settings.get(DISABLE_DYNAMIC_SCRIPTING_SETTING, DISABLE_DYNAMIC_SCRIPTING_DEFAULT)); | ||
|
||
CacheBuilder cacheBuilder = CacheBuilder.newBuilder(); | ||
if (cacheMaxSize >= 0) { | ||
|
@@ -130,7 +166,7 @@ public CompiledScript compile(String lang, String script) { | |
lang = defaultLang; | ||
} | ||
if (!dynamicScriptEnabled(lang)) { | ||
throw new ScriptException("dynamic scripting disabled"); | ||
throw new ScriptException("dynamic scripting for [" + lang + "] disabled"); | ||
} | ||
CacheKey cacheKey = new CacheKey(lang, script); | ||
compiled = cache.getIfPresent(cacheKey); | ||
|
@@ -180,12 +216,16 @@ private boolean dynamicScriptEnabled(String lang) { | |
if (service == null) { | ||
throw new ElasticsearchIllegalArgumentException("script_lang not supported [" + lang + "]"); | ||
} | ||
// Templating languages and native scripts are always allowed | ||
// "native" executions are registered through plugins | ||
if (service.sandboxed() || "native".equals(lang)) { | ||
|
||
// Templating languages (mustache) and native scripts are always | ||
// allowed, "native" executions are registered through plugins | ||
if (this.dynamicScriptingDisabled == DynamicScriptDisabling.EVERYTHING_ALLOWED || "native".equals(lang) || "mustache".equals(lang)) { | ||
return true; | ||
} else if (this.dynamicScriptingDisabled == DynamicScriptDisabling.ONLY_DISK_ALLOWED) { | ||
return false; | ||
} else { | ||
return service.sandboxed(); | ||
} | ||
return !disableDynamic; | ||
} | ||
|
||
private class ScriptChangesListener extends FileChangesListener { | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we want to shade groovy as we did with mvel? and remove mvel?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure what shading benefits us? Is there a reason to shade groovy?
As for removing mvel, I think that should be a PR from this one so we can discuss whether it goes to 1.3 or just 2.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe @kimchy can comment on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dakrone If it's not shaded, and a Groovy client uses any of the popular Elasticsearch Groovy clients (only use Groovy with Gradle myself, so I am not aware of the popular ones), then will it cause a conflict/collision?
I'd expect not unless the Groovy scripts were interpreted on the client, which would be odd. Assuming it's not needed in some way by the client, then shading seems unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this'd still happen. The JVM normally loads each class only once [1] so you'll just get whichever groovy it finds first. Shading fixes this by copying the class to a new name.
[1]: technically its one per class loader and class loaders are hierarchical....
So, yeah, I'm +1 for shading.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nik9000 Just to add: The JVM is very good about loading classes from the same jar only once. However, it is possible to get into situations where different versions of the same jar(s) are on the classpath that cause
ClassNotFound
issues (because of a lack of isolation, which can be solved with class loaders) because the different versions cannot coexist. My major point that I was trying to get at is that I believe this is a server side dependency only, and that should mean that there will never be a chance for collision except in the embedded use case (ignoring any potential forked projects that use Groovy, which should just drop whichever one they want to drop).I think the embedded concern is what makes me push +1 for shading as well, although I think it would be preferable if we could avoid shading because it would be lighter weight given its intended-to-be optional nature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me. Well put.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this and decided not to shade groovy as it negates the "optional" nature of it and it will reduce the jar size for clients using only the transport client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good! I also wonder if we want to remove mvel from shading as well and mark it optional as well?