Files
Enterprise-Knowledge-Base/docs/dev/opensearch-integration-test.md
T
ochan.kwon 7b2bc8a6b8 fix: validate vector store connection addresses against SSRF policy
Vector store create and raw connection-test accepted user-supplied
connection addresses without SSRF validation, while every other
admin-entered endpoint (e.g. model BaseURL) already runs
ValidateURLForSSRF. An admin could point an engine address at an
internal host and use the connection probe as an SSRF oracle.

Add validateConnectionAddrSSRF, applied at the two user-input
boundaries only:

- CreateStore: validated before any network dial.
- TestStoreRaw: a new TestRawConnection wrapper runs an engine-type
  allowlist, required-field validation, and SSRF validation before
  delegating to TestConnection. TestConnection stays validation-free
  for trusted callers (env stores, stored configs already validated
  at create time).

Validation covers every address-bearing field a driver dials
(Addr, qdrant Host+Port, weaviate Host and GrpcAddress) and
fails closed for unmapped engine types. The Elasticsearch probe no
longer follows redirects, closing a 302-to-internal bypass.

docker-compose whitelists the bundled vector-store service hostnames
(qdrant, milvus, weaviate, doris-fe) via SSRF_WHITELIST_EXTRA so the
out-of-box experience is preserved; postgres is intentionally not
whitelisted. .env.example and the OpenSearch dev guide document how
to whitelist external or local-dev addresses.
2026-06-04 15:26:56 +08:00

124 lines
4.2 KiB
Markdown

# OpenSearch k-NN driver — local integration test
This guide brings up a single-node OpenSearch cluster and exercises the
OpenSearch retrieve engine end to end. The driver lives in
`internal/application/repository/retriever/opensearch/`.
## 1. Start a dev cluster
```bash
docker compose -f docker-compose.dev.yml --profile opensearch up -d
```
This starts:
- `opensearch` on `http://localhost:9200` — single-node, **security plugin
disabled** (plain HTTP, no auth/TLS). The image bundles the
`opensearch-knn` plugin.
> **OpenSearch Dashboards is optional** and lives in a separate
> `opensearch-ui` profile, so it is *not* started by `--profile opensearch`.
> The whole integration test below is curl-verifiable against `:9200`. If you
> want the web UI (Dev Tools console / visual index inspection), start it on
> demand:
>
> ```bash
> docker compose -f docker-compose.dev.yml --profile opensearch-ui up -d
> # opensearch-dashboards on http://localhost:5601 (depends_on pulls the cluster in)
> ```
Verify:
```bash
curl -s localhost:9200 | jq '.version.distribution, .version.number'
# "opensearch" "3.3.2"
curl -s 'localhost:9200/_cat/plugins?format=json' | jq -r '.[].component' | grep opensearch-knn
```
> Production clusters must enable the security plugin (TLS + auth). The dev
> profile disables it only to keep local setup trivial. When connecting to a
> secured cluster, set `username` / `password` and — for self-signed certs in
> dev only — `insecure_skip_verify=true`.
## 2. Register the store
### Option A — DB store (UI / API)
> **SSRF whitelist (dev).** `CreateStore` and the raw connection test validate
> the user-supplied `addr` against the SSRF policy. `http://localhost:9200`
> is rejected by default — `localhost` is a restricted hostname and `9200` is
> a blocked port. When the backend runs on the host (`go run`), add `localhost`
> to the whitelist in your `.env` before registering:
>
> ```bash
> SSRF_WHITELIST=localhost
> ```
>
> The containerised compose deployment whitelists the bundled vector-store
> service names automatically (`SSRF_WHITELIST_EXTRA`), so this step is
> dev-only. The env-store path (Option B) is not affected.
`POST /api/v1/vector-stores`:
```json
{
"name": "opensearch-local",
"engine_type": "opensearch",
"connection_config": { "addr": "http://localhost:9200" },
"index_config": {
"number_of_shards": 1,
"number_of_replicas": 0,
"hnsw_m": 16,
"hnsw_ef_construction": 100,
"knn_engine": "lucene"
}
}
```
`CreateStore` runs the connection probe (version + k-NN plugin) before
persisting; a bad address / unsupported version / missing plugin is rejected
with `400`.
### Option B — env store
```bash
export RETRIEVE_DRIVER=opensearch
export OPENSEARCH_ADDR=http://localhost:9200
# export OPENSEARCH_USERNAME / OPENSEARCH_PASSWORD for a secured cluster
# export OPENSEARCH_INSECURE_SKIP_VERIFY=true # self-signed dev TLS only
```
## 3. Single-node note (important)
On a single-node cluster, any index created with `number_of_replicas >= 1`
leaves its replica shard **unassigned**, so the index health goes **Yellow**.
Yellow does **not** block reads or writes — it is safe for local testing — but
to keep the cluster Green set **`number_of_replicas: 0`** at store
registration (as in the Option A example above). The driver default is `1`
(it assumes a ≥2-node cluster).
## 4. Exercise the flow
1. Bind a knowledge base to the store and ingest a few documents.
2. Confirm the per-dimension index appears:
`curl -s 'localhost:9200/_cat/indices?v' | grep weknora`
(e.g. `weknora_<storeprefix>_768` + alias, plus `weknora_<storeprefix>_keywords`).
3. Run a retrieval query against the bound KB and confirm hits come back.
4. Copy the KB to another KB and confirm the docs are reindexed
(`opensearch.reindex_executed` audit event).
5. Toggle chunk enabled-status / tag and confirm `_update_by_query` applies it.
## 5. Tear down
```bash
docker compose -f docker-compose.dev.yml --profile opensearch down -v
```
## Scope notes
- Large-batch async reindex / delete (task polling) is a follow-up; the sync
paths handle typical KB sizes (pagination is bounded by `max_result_window`,
default 10000).
- Native `hybrid` query + search pipeline is out of scope — fusion stays at the
service layer (RRF).