Skip to content

Commit

Permalink
Disable dynamic scripting by default
Browse files Browse the repository at this point in the history
Closes #5853
  • Loading branch information
dakrone committed Apr 25, 2014
1 parent 051beb5 commit 81e83cc
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 38 deletions.
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);
defaultSettings = builder.build();
executor = EsExecutors.newCached(1, TimeUnit.MINUTES, EsExecutors.daemonThreadFactory("test_" + clusterName));
}
Expand Down

0 comments on commit 81e83cc

Please sign in to comment.