OpenSearch access governance — every query reviewed before it runs
Last updated
AccessFlow governs OpenSearch through the same submit, analyse, approve, execute pipeline as any other engine. OpenSearch speaks the same REST API and Query DSL as Elasticsearch for everything AccessFlow governs, so it is served by the same engine plugin — the two differ only in the HTTP client stack underneath.
- Family
- Search
- Query language
- JSON Query DSL
- Runs as
- Engine plugin (shared with Elasticsearch)
- Default port
9200- Default SSL mode
REQUIRE- Install
- One-click from the connector catalog
How AccessFlow connects
OpenSearch and Elasticsearch share one plugin JAR, which registers two engine providers. They genuinely need separate code at exactly one layer: Elasticsearch ships on one major version of the Apache HTTP client and OpenSearch on the next, so the JAR bundles both stacks, each relocated separately, behind a small transport abstraction. Parsing, row security, masking and introspection are shared code.
The base URL is built from host and port (9200 by default) with the scheme from the SSL mode, or supplied verbatim. Authentication is HTTP basic or an API key held in its own encrypted field.
What AccessFlow understands
The same JSON envelope as Elasticsearch: the first recognised command key names the operation and its value is the target index. search and count are reads, with get and mget lowered into a search so there is one row-security path; index and bulk are inserts; update_by_query and delete_by_query are updates and deletes; index and mapping management is DDL.
What it refuses
Scripting is refused anywhere in the request tree — script, script_fields, script_score, scripted_metric and runtime_mappings — along with cluster-level APIs and any index name beginning with an underscore or a dot. A bulk request may carry only index actions, so a submission classifies as exactly one kind of write.
Row-level security and masking
The user's query is wrapped rather than merged: it becomes the must clause of a new boolean query whose filter holds the policy clauses, which is what makes the rewrite provably non-widening. An empty value list becomes a negated match-all and therefore matches nothing.
A write into a policied index is rejected. As on Elasticsearch, term clauses match exact keyword fields, so a policy on an analysed text field matches tokens rather than the literal value — check the field type introspection reports before writing the policy. Masking recurses by dot-path into nested objects.
What the rewrite actually does
A policy restricting the orders index to the caller's region wraps the submitted query rather than merging into it:
// submitted
{ "search": "orders", "query": { "match": { "status": "OPEN" } } }
// executed — the original query becomes must, the policy becomes filter
{ "search": "orders",
"query": { "bool": { "must": [ { "match": { "status": "OPEN" } } ],
"filter": [ { "term": { "region": "EU" } } ] } } }
Wrapping rather than merging is what makes the rewrite provably non-widening. An empty policy value list becomes a negated match-all and therefore matches nothing.
Introspection
The connection test issues a cluster-info request, and introspection reads index mappings into tables and dot-path columns with a synthetic id primary key. Searches page to one more than the row cap for truncation detection, and a timed-out search or a failed bulk becomes an execution error rather than a partial success.