-
Notifications
You must be signed in to change notification settings - Fork 213
[Feature] Add PPL rest command
#5599
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
Merged
noCharger
merged 14 commits into
opensearch-project:main
from
noCharger:feature/ppl-rest-command
Jul 15, 2026
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
24730dc
[Feature] Add PPL `rest` command (Calcite system row source)
noCharger a7a8a20
Align rest '/_cluster/settings' redaction with native endpoint; CI fi…
noCharger eeb4e5b
Address rest command bot-review findings; register rest doctest
noCharger 8b0a147
Harden decodeRestSpec: reject non-rest-source tokens with a clear error
noCharger 343e93a
Fix rest explain IT (JSON payload) and harden cluster-settings/state …
noCharger 42f77a5
Rest command: analytics-engine coexistence IT + hardened source-token…
noCharger 2db942f
[Bugfix] Keep rest command on Calcite path under cluster-composite
noCharger 0819158
[Refactor] Unify system-index and rest scans behind one catalog table
noCharger 95f0618
[Feature] Add rest command response redaction and endpoint allow-list
noCharger 06ff820
[Refactor] Remove unused REST/TIMEOUT rules from shared language grammar
noCharger 7d9df84
[Test] Add rest command security integration tests
noCharger aef4663
[Bugfix] Make rest redaction and allow-list settings node-level
noCharger 8e4b565
Enhance redaction logic
noCharger 03d21c3
[Change] Disable all rest endpoints by default (empty allow-list)
noCharger File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
25 changes: 25 additions & 0 deletions
25
core/src/main/java/org/opensearch/sql/ast/tree/RestRelation.java
This file contains hidden or 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 |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /* | ||
| * Copyright OpenSearch Contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package org.opensearch.sql.ast.tree; | ||
|
|
||
| import java.util.Collections; | ||
| import lombok.EqualsAndHashCode; | ||
| import lombok.ToString; | ||
| import org.opensearch.sql.ast.expression.UnresolvedExpression; | ||
|
|
||
| /** | ||
| * Extend Relation to mark a {@code rest} leading command. The single table name is a reserved, | ||
| * encoded token (produced by {@link org.opensearch.sql.utils.SystemIndexUtils#restTable}) that | ||
| * carries the validated REST endpoint spec; it resolves through the storage engine to a REST source | ||
| * table on the Calcite path, exactly as {@link DescribeRelation} resolves to a system index. | ||
| */ | ||
| @ToString | ||
| @EqualsAndHashCode(callSuper = false) | ||
| public class RestRelation extends Relation { | ||
| public RestRelation(UnresolvedExpression tableName) { | ||
| super(Collections.singletonList(tableName)); | ||
| } | ||
| } |
This file contains hidden or 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 hidden or 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 hidden or 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 |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| # rest | ||
|
|
||
| The `rest` command is a leading command that reads an allow-listed, read-only in-cluster management endpoint (cluster/cat/nodes) and emits the response as PPL rows. Its rows come from the endpoint dispatch, not from an index, so `rest` appears at the start of a query. | ||
|
|
||
| > **Note**: The `rest` command is supported only on the Calcite query engine (`plugins.calcite.enabled=true`). Each endpoint has a fixed output schema, and the dispatch runs under the caller's security context, so a user who cannot call an endpoint directly cannot call it through `rest`. The command is read-only; mutating and non-allow-listed endpoints are rejected. Each endpoint requires the same cluster-monitor privilege as calling it natively, so `rest` grants no extra access. Some allow-listed endpoints surface operational metadata (for example `/_cat/nodes` exposes node addresses and resource utilization, `/_cat/plugins` the installed plugin inventory, and `/_cluster/state` cluster-state identifiers); this is a deliberate, read-only, monitor-privileged trade-off. `/_cluster/settings` is redacted with the node's setting filter so `Property.Filtered` keys are not surfaced. | ||
|
|
||
| ## Enabling the command | ||
|
|
||
| The `rest` command is **disabled by default**: `plugins.ppl.rest.allowed_endpoints` defaults to an empty list, so every endpoint is rejected until a deployment explicitly opts in. Enable specific endpoints by setting the allow-list (a node-level setting, so it is applied at node startup and cannot be changed at runtime): | ||
|
|
||
| ```yaml | ||
| plugins.ppl.rest.allowed_endpoints: ["/_cluster/health", "/_cat/nodes"] | ||
| ``` | ||
|
|
||
| Use `["*"]` to allow every endpoint in the curated list below. An empty list (the default) disables the command entirely. | ||
|
|
||
| The `rest` command also supports optional response redaction of network identifiers (IPv4/IPv6 addresses, `inet[...]` forms, EC2-style host names, and availability-zone names) in `/_cat/*` and `/_cluster/settings` cell values, controlled by `plugins.ppl.rest.redaction.enabled` (a node-level setting, default `false`). Managed deployments that must not expose host topology should set it to `true`. | ||
|
|
||
| ## Syntax | ||
|
|
||
| The `rest` command has the following syntax: | ||
|
|
||
| ```syntax | ||
| rest <endpoint-path> [count=<int>] [timeout=<duration>] [<get-arg>=<value> ...] | ||
| ``` | ||
|
|
||
| ## Parameters | ||
|
|
||
| The `rest` command supports the following parameters. | ||
|
|
||
| | Parameter | Required/Optional | Description | | ||
| | --- | --- | --- | | ||
| | `<endpoint-path>` | Required | An allow-listed, read-only endpoint path (see the allow-list below), for example `/_cluster/health`. | | ||
| | `count=<int>` | Optional | Caps the number of emitted rows. | | ||
| | `timeout=<duration>` | Optional | Reserved for forward compatibility. It is currently rejected with a clear error, because a single uniform timeout does not map cleanly across the different endpoints. | | ||
| | `<get-arg>=<value>` | Optional | Endpoint query arguments, validated per endpoint by both key and value (for example `local=true` for `/_cluster/health`, `health=green` for `/_cat/indices`, `expand_wildcards=open` for `/_resolve/index`). | | ||
|
|
||
| ## Allow-list | ||
|
|
||
| `rest` resolves only an explicit, curated set of read-only endpoints. Anything outside the list, including any mutating endpoint, is rejected with a clear error. | ||
|
|
||
| | Endpoint | Output columns | Accepted args | | ||
| | --- | --- | --- | | ||
| | `/_cluster/health` | `cluster_name` (string), `status` (string), `number_of_nodes` (integer), `number_of_data_nodes` (integer), `active_primary_shards` (integer), `active_shards` (integer), `relocating_shards` (integer), `initializing_shards` (integer), `unassigned_shards` (integer), `timed_out` (boolean) | `local` | | ||
| | `/_cluster/state` | `cluster_name` (string), `state_uuid` (string), `version` (long), `cluster_manager_node` (string) | (none) | | ||
| | `/_cluster/settings` | `setting` (string), `value` (string), `tier` (string) | (none) | | ||
| | `/_cat/indices` | `index` (string), `health` (string), `pri` (integer), `rep` (integer), `active_shards` (integer) | `health` | | ||
| | `/_cat/nodes` | `name` (string), `ip` (string), `node_role` (string), `heap_percent` (integer), `ram_percent` (integer), `cpu` (integer) | (none) | | ||
| | `/_cat/cluster_manager` | `id` (string), `host` (string), `ip` (string), `node` (string) | (none) | | ||
| | `/_cat/plugins` | `name` (string), `component` (string), `version` (string) | (none) | | ||
| | `/_cat/shards` | `index` (string), `shard` (integer), `prirep` (string), `state` (string), `node` (string) | (none) | | ||
| | `/_resolve/index` | `name` (string), `type` (string) | `expand_wildcards` | | ||
|
|
||
| ## Example 1: Counting the nodes in the cluster | ||
|
|
||
| The following query reads cluster health and projects a column that is deterministic on a single-node cluster: | ||
|
|
||
| ```ppl | ||
| | rest '/_cluster/health' | fields number_of_nodes | ||
| ``` | ||
|
|
||
| The query returns the following results: | ||
|
|
||
| ```text | ||
| fetched rows / total rows = 1/1 | ||
| +-----------------+ | ||
| | number_of_nodes | | ||
| |-----------------| | ||
| | 1 | | ||
| +-----------------+ | ||
| ``` | ||
|
|
||
| `/_cluster/health` also exposes `status`, `active_shards`, and the other columns listed in the allow-list, which you can project and filter the same way. | ||
|
|
||
| ## Example 2: Composing downstream commands over a cat endpoint | ||
|
|
||
| The `rest` row source composes with downstream `where`, `sort`, `stats`, and `fields` exactly like an index scan. The following query reads `/_cat/cluster_manager` and counts the rows: | ||
|
|
||
| ```ppl | ||
| | rest '/_cat/cluster_manager' | stats count() as managers | ||
| ``` | ||
|
|
||
| The query returns the following results: | ||
|
|
||
| ```text | ||
| fetched rows / total rows = 1/1 | ||
| +----------+ | ||
| | managers | | ||
| |----------| | ||
| | 1 | | ||
| +----------+ | ||
| ``` | ||
|
|
||
| For example, `| rest '/_cat/indices' | where health = 'green' | sort index | fields index, health, pri` lists green indexes; the projected columns come from the endpoint's fixed schema. | ||
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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
Oops, something went wrong.
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.
I only see
RestResponseRedactorfocus on IP address redaction. But in my test (Dev Tools in AOS domain), I can see many other masked values, e.g., version in _cat/plugins, cluster.routing.allocation.awareness.force.zone in _cluster/settings, host in _cat/cluster_manager. Are all these redacted automatically? If not, any better way to avoid such risk, like sending what's in REST command go through the exact same process as a REST request sent to the domain by users?Uh oh!
There was an error while loading. Please reload this page.
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.
The redaction replicates the AOS-side logic exactly. As called out in the PR description, it masks more than IPv4 addresses — IPv6, inet[/…], EC2-style hostnames (ip-a-b-c-d), and availability-zone names are all covered. Every case is asserted in RestResponseRedactorTest.