Scripting and security in Painless
Serverless Stack
As part of its core design, Painless provides secure scripting capabilities across Elasticsearch.
Introduced in Elasticsearch 5.0 as a replacement for Groovy, Painless is purpose-built for Elasticsearch, enabling native performance while preventing unauthorized access to system resources.
By operating in a controlled sandbox environment, Painless ensures that you won’t get compromised when using it. Painless only allows pre-approved operations through fine-grained allowlists. Scripts cannot access file systems, networks, or other system resources that could compromise your cluster while still providing the flexibility you need for search scoring, data processing, and operational automation.
The fine-grained allowlist operates as the first security layer. Anything that is not part of the allowlist will result in an error.
As another layer of security, Elasticsearch uses Seccomp in Linux, Seatbelt in macOS, and ActiveProcessLimit on Windows to prevent Elasticsearch from forking or running other processes.
Finally, scripts used in scripted metrics aggregations can be restricted to a defined list of scripts or forbidden altogether. This can prevent users from running particularly slow or resource-intensive aggregation queries.
You can modify the allowed script types setting to restrict the type of scripts that are allowed to run and control the available contexts that scripts can run in. As well, you can use the Elasticsearch security features to enhance your defence strategy.
Elasticsearch supports two script types: inline and stored. By default, Elasticsearch is configured to run both types of scripts. To limit what type of scripts are run, set script.allowed_types to inline or stored. To prevent any scripts from running, set script.allowed_types to none. If you use Kibana, set script.allowed_types to both or just inline. Some Kibana features rely on inline scripts and do not function as expected if Elasticsearch does not allow inline scripts.
For example, to run inline scripts but not stored scripts:
script.allowed_types: inline
By default, all script contexts are permitted. Use the script.allowed_contexts setting to specify the contexts that are allowed. To specify that no contexts are allowed, set script.allowed_contexts to none. For example, to allow scripts to run only in scoring and update contexts:
script.allowed_contexts: score, update
By default, all scripts are permitted in scripted metrics aggregations. To restrict the set of allowed scripts, set search.aggs.only_allowed_metric_scripts to true and provide the allowed scripts using search.aggs.allowed_inline_metric_scripts and/or search.aggs.allowed_stored_metric_scripts.
To disallow certain script types, omit the corresponding script list (search.aggs.allowed_inline_metric_scripts or search.aggs.allowed_stored_metric_scripts) or set it to an empty array. When both script lists are not empty, the given stored scripts and the given inline scripts will be allowed.
search.aggs.only_allowed_metric_scripts: true
search.aggs.allowed_inline_metric_scripts: []
search.aggs.allowed_stored_metric_scripts:
- script_id_1
- script_id_2
- script_id_3
- script_id_4
Conversely, the next example allows specific inline scripts but no stored scripts:
search.aggs.only_allowed_metric_scripts: true
search.aggs.allowed_inline_metric_scripts:
- 'state.transactions = []'
- 'state.transactions.add(doc.some_field.value)'
- 'long sum = 0; for (t in state.transactions) { sum += t } return sum'
- 'long sum = 0; for (a in states) { sum += a } return sum'
search.aggs.allowed_stored_metric_scripts: []