Skip to content
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

Disable dynamic scripting by default #5943

Merged
merged 1 commit into from Apr 25, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 68 additions & 31 deletions docs/reference/modules/scripting.asciidoc
Expand Up @@ -19,6 +19,58 @@ All places where a `script` parameter can be used, a `lang` parameter
script. The `lang` options are `mvel`, `js`, `groovy`, `python`, and
`native`.

added[1.2.0, Dynamic scripting is disabled by default since version 1.2.0]

To increase security, Elasticsearch does not allow you to specify scripts with a
request. Instead, scripts must be placed in the `scripts` directory inside the
configuration directory (the directory where elasticsearch.yml is). Scripts
placed into this directory will automatically be picked up and be available to
be used. Once a script has been placed in this directory, it can be referenced
by name. For example, a script called `calculate-score.mvel` can be referenced
in a request like this:

[source]
--------------------------------------------------
$ tree config
config
├── elasticsearch.yml
├── logging.yml
└── scripts
└── calculate-score.mvel
$ cat config/scripts/calculate-score.mvel
Math.log(_score * 2) + my_modifier
--------------------------------------------------

[source,js]
--------------------------------------------------
curl -XPOST localhost:9200/_search -d '{
"query": {
"function_score": {
"query": {
"match": {
"body": "foo"
}
},
"functions": [
{
"script_score": {
"script": "calculate-score",
"params": {
"my_modifier": 8
}
}
}
]
}
}
}'
--------------------------------------------------

The name of the script is derived from the hierarchy of directories it
exists under, and the file name without the lang extension. For example,
a script placed under `config/scripts/group1/group2/test.py` will be
named `group1_group2_test`.

[float]
=== Default Scripting Language

Expand All @@ -27,46 +79,31 @@ provided) is `mvel`. In order to change it set the `script.default_lang`
to the appropriate language.

[float]
=== Preloaded Scripts

Scripts can always be provided as part of the relevant API, but they can
also be preloaded by placing them under `config/scripts` and then
referencing them by the script name (instead of providing the full
script). This helps reduce the amount of data passed between the client
and the nodes.

The name of the script is derived from the hierarchy of directories it
exists under, and the file name without the lang extension. For example,
a script placed under `config/scripts/group1/group2/test.py` will be
named `group1_group2_test`.

[float]
=== Disabling dynamic scripts
=== Enabling dynamic scripting

We recommend running Elasticsearch behind an application or proxy,
which protects Elasticsearch from the outside world. If users are
allowed to run dynamic scripts (even in a search request), then they
have the same access to your box as the user that Elasticsearch is
running as.

First, you should not run Elasticsearch as the `root` user, as this
would allow a script to access or do *anything* on your server, without
limitations. Second, you should not expose Elasticsearch directly to
users, but instead have a proxy application inbetween. If you *do*
intend to expose Elasticsearch directly to your users, then you have
to decide whether you trust them enough to run scripts on your box or
not. If not, then even if you have a proxy which only allows `GET`
requests, you should disable dynamic scripting by adding the following
setting to the `config/elasticsearch.yml` file on every node:
running as. For this reason dynamic scripting is disabled by default.

First, you should not run Elasticsearch as the `root` user, as this would allow
a script to access or do *anything* on your server, without limitations. Second,
you should not expose Elasticsearch directly to users, but instead have a proxy
application inbetween. If you *do* intend to expose Elasticsearch directly to
your users, then you have to decide whether you trust them enough to run scripts
on your box or not. If you do, you can enable dynamic scripting by adding the
following setting to the `config/elasticsearch.yml` file on every node:

[source,yaml]
-----------------------------------
script.disable_dynamic: true
script.disable_dynamic: false
-----------------------------------

This will still allow execution of named scripts provided in the config, or
_native_ Java scripts registered through plugins, however it will prevent
users from running arbitrary scripts via the API.
While this still allows execution of named scripts provided in the config, or
_native_ Java scripts registered through plugins, it also allows users to run
arbitrary scripts via the API. Instead of sending the name of the file as the
script, the body of the script can be sent instead.

[float]
=== Automatic Script Reloading
Expand Down Expand Up @@ -113,7 +150,7 @@ doc score using `doc.score`.
[float]
=== Computing scores based on terms in scripts

see <<modules-advanced-scripting, advanced scripting documentation>>
see <<modules-advanced-scripting, advanced scripting documentation>>

[float]
=== Document Fields
Expand Down
Expand Up @@ -52,6 +52,11 @@ public String[] extensions() {
return new String[0];
}

@Override
public boolean sandboxed() {
return false;
}

@Override
public Object compile(String script) {
NativeScriptFactory scriptFactory = scripts.get(script);
Expand Down
Expand Up @@ -33,6 +33,8 @@ public interface ScriptEngineService {

String[] extensions();

boolean sandboxed();

Object compile(String script);

ExecutableScript executable(Object compiledScript, @Nullable Map<String, Object> vars);
Expand Down
19 changes: 12 additions & 7 deletions src/main/java/org/elasticsearch/script/ScriptService.java
Expand Up @@ -75,7 +75,7 @@ public ScriptService(Settings settings, Environment env, Set<ScriptEngineService
logger.debug("using script cache with max_size [{}], expire [{}]", cacheMaxSize, cacheExpire);

this.defaultLang = componentSettings.get("default_lang", "mvel");
this.disableDynamic = componentSettings.getAsBoolean("disable_dynamic", false);
this.disableDynamic = componentSettings.getAsBoolean("disable_dynamic", true);

CacheBuilder cacheBuilder = CacheBuilder.newBuilder();
if (cacheMaxSize >= 0) {
Expand Down Expand Up @@ -129,7 +129,7 @@ public CompiledScript compile(String lang, String script) {
if (lang == null) {
lang = defaultLang;
}
if (dynamicScriptDisabled(lang)) {
if (!dynamicScriptEnabled(lang)) {
throw new ScriptException("dynamic scripting disabled");
}
CacheKey cacheKey = new CacheKey(lang, script);
Expand Down Expand Up @@ -175,12 +175,17 @@ public void clear() {
cache.invalidateAll();
}

private boolean dynamicScriptDisabled(String lang) {
if (!disableDynamic) {
return false;
private boolean dynamicScriptEnabled(String lang) {
ScriptEngineService service = scriptEngines.get(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)) {
return true;
}
// we allow "native" executions since they register through plugins, so they are "allowed"
return !"native".equals(lang);
return !disableDynamic;
}

private class ScriptChangesListener extends FileChangesListener {
Expand Down
Expand Up @@ -120,6 +120,11 @@ public String[] extensions() {
return new String[] {"mustache"};
}

@Override
public boolean sandboxed() {
return true;
}

@Override
public ExecutableScript executable(Object mustache,
@Nullable Map<String, Object> vars) {
Expand Down
Expand Up @@ -79,6 +79,11 @@ public String[] extensions() {
return new String[]{"mvel"};
}

@Override
public boolean sandboxed() {
return false;
}

@Override
public Object compile(String script) {
return MVEL.compileExpression(script.trim(), new ParserContext(parserConfiguration));
Expand Down
1 change: 1 addition & 0 deletions src/test/java/org/elasticsearch/test/TestCluster.java
Expand Up @@ -200,6 +200,7 @@ public TestCluster(long clusterSeed, int minNumNodes, int maxNumNodes, String cl
builder.put("path.data", dataPath.toString());
}
}
builder.put("script.disable_dynamic", false);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be set to true to disable scripts by default? like the pull request states?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since scripting is disabled by default, we re-enable it back when we start each node in our test infrastructure, just because we have quite some tests that need it on. We are thinking about re-enabling it only for the tests that rely on it though...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@javanna thank you that helps a lot.

defaultSettings = builder.build();
executor = EsExecutors.newCached(1, TimeUnit.MINUTES, EsExecutors.daemonThreadFactory("test_" + clusterName));
}
Expand Down