StatusType

bayerncloud elasticsearch opensearch

Document Level Security

Kurzantwort: DLS/FLS in OpenSearch/Elasticsearch ist möglich - aber in Multi‑Tenant‑APIs mit viel Volltext, Geo & Aggregationen oft ein Performance‑, Komplexitäts‑ und Risiko‑Magnet. Für Los 1 (read‑only API, Lizenz “Open/Closed Data”, starke Filter) und Los 4 (Widgets mit vielen Suchen/Aggregationen) ist das meist die falsche Stellschraube.

Warum DLS/FLS oft schiefgeht

  1. Performance & Caching
  • Entscheidungen passieren pro Request & Dokument → teure Filter‑Wrapper, deutlich weniger/kein Query‑Caching, hohe Latenzen unter Last.
  • Besonders schmerzhaft bei Aggregationen, Geo‑Queries, Nested/Parent‑Child und _msearch.
  • Effekt: schöne Dev‑Tests, aber plötzliche 99‑Percentile‑Spikes im Betrieb (kontraproduktiv zur Uptime/Performanz‑Vorgabe).
  1. Korrektheit von Ergebnissen
  • Aggregationen (Counts, Buckets, Cardinality, Percentiles) können “komisch” wirken, weil die Sicht pro Nutzer gefiltert ist - Business erwartet “globale” Zahlen.
  • Highlighting/Suggest/Terms‑Lookups u. ä. können “Spuren” verdeckter Felder/Docs hinterlassen, wenn nicht alles konsequent gehärtet ist.
  • Scroll/PIT: Rechtewechsel während eines laufenden Scrolls/PIT → inkonsistente Ergebnisse oder Sicherheitslöcher, wenn IDs geteilt werden.
  1. Leak‑Risiken durch Nebenendpunkte
  • _cat/*, _field_caps, Explain/Profile, Mapping‑Blicke, Source/Stored Fields, Plugin‑APIs - ein vergessenes Loch reicht.
  • In JSON‑LD/@graph können verlinkte Objekte (z. B. “Place” im “Event”) ungewollt “mitrutschen”, obwohl das Hauptdokument korrekt gefiltert ist. (Gerade relevant, wenn “alles hat Basis Thing”.)
  1. Betrieb & Weiterentwicklung
  • Policy‑Spaghetti (Rollen x Index x Felder x Abfragearten).
  • Debugging-Hölle: “Warum ist das Dokument weg?” - schwer reproduzierbar, weil nutzer- und kontextabhängig.
  • Upgrades/Testlast: Security‑Layer + Query‑DSL + Mappings + Plugins → hoher Test‑ und Pflegeaufwand; bei Releasewechseln gerne regressionsanfällig.
  1. FLS mit evolvierenden Schemata
  • Euer Output ist schema.org/ODTA in JSON‑LD; Felder wachsen/ändern sich. FLS braucht allow‑/deny‑Listen - neue Properties sind dann per Default offen (oder zu restriktiv) → beides riskant/ärgerlich für ein Read‑only API mit Lizenzgrenzen.

Typische Symptome (“das hat bei uns nicht gut funktioniert”)

  • Plötzliche Latenzsprünge bei Aggregations‑lastigen Widgets (Heatmaps, Kalender, Facetten).
  • Unterschiedliche Zahlen zwischen API, Dashboard und Backoffice wegen gefilterter Sicht.
  • Kibana/OpenSearch Dashboards wirken “kaputt”, weil Saved Searches/Aggs unerwartet leer sind.
  • Edge‑Case‑Leaks über Nebenendpunkte oder falsch gehandhabte _msearch/Scroll‑IDs.

Was stattdessen besser passt (für Los 1/3/4)

  1. Harte Partitionierung statt DLS
  • Index‑pro‑Mandant/Org und/oder pro Lizenz (OPEN/CLOSED); Zugriff über Aliases/Routing steuern.
  • Vorteile: bessere Cache‑Treffer, sauberes Monitoring, simple mentale Modelle, deutlich weniger Leck‑Risiko.
  • Passt direkt zur geforderten Lizenz‑Trennung Open vs. Closed (Los 1, 2.2).
  1. Policy‑Durchsetzung im PEP (Gateway/Middleware)
  • Wie zuletzt skizziert: OPA/Rego im Gateway oder in der Middleware → Query‑Rewrite (DLS als Filter), _source_includes (FLS), Index‑Gating vor OpenSearch.
  • Vorteil: einheitlicher Schutz auch für Dashboards, Widgets, Scripte - und Auditing an einer Stelle. (Auch hilfreich für Los 3/4, die dieselben Regeln konsumieren.)
  1. Redaktierte Materialisierungen
  • Für OPEN‑Daten einen abgeleiteten “Open‑View”‑Index (nur erlaubte Felder/Dokumente), der für Public/Widgets genutzt wird; CLOSED über gesicherte Wege.
  • Ergebnis: sehr schnell, sehr schwer zu “verklicken”, simple Governance (passt zu Widgets‑Performancezielen).
  1. Gefilterte Aliases (leichtgewichtig)
  • Wenn echte DLS zu heavy ist: Alias mit festem Filter (z. B. license:OPEN) und nur diese Aliases exponieren.
  • Kein per‑User‑Kontext - aber robust für die Lizenz‑Trennung, die ihr explizit braucht.

Wenn ihr DLS/FLS dennoch einsetzen müsst - Checkliste

  • Explizite Allow‑Listen für Endpunkte; alles andere 403.
  • _cat/*, _field_caps, Explain/Profile, Mappings, Tasks, Snapshot‑APIs: hart beschränken.
  • Scroll/PIT nutzergebunden, kurze TTL, keine Cross‑User‑Weitergabe.
  • _msearch: Sub‑Requests per Policy einzeln prüfen (keine Misch‑Tenants).
  • Aggregationen kritisch testen (Counts/Facetten) gegen “Ground Truth” ohne DLS.
  • Observability: Policy‑ID & Constraints in Access‑Logs mitschreiben (Korrelation von Latenzen/Denies).
  • Loadtests mit realistischen Queries (Geo, Nested, Aggs, msearch), Ziel‑P99 ans SLA koppeln.

Fazit

Für eure Anforderungen (Los 1: read‑only, Lizenz‑basiert, performant; Los 4: schnelle Widgets) sind Index‑Partitionierung + PEP (OPA) + ggf. redaktierte Views in der Praxis robuster und schneller als “alles über DLS/FLS lösen”. DLS taugt als Ergänzung für wenige Spezialfälle - aber nicht als Hauptmechanismus eurer API‑Sicherheit und Mandantentrennung.

Wenn du magst, skizziere ich dir das konkrete Schnittstellen‑Design (Index‑Layout + Aliases + OPA‑Input/Output‑Schema + Query‑Rewrite‑Regeln) passend zu euren Datentypen (Events/POIs/Touren) - sofort umsetzbar für Los 1 und anschlussfähig an Los 3/4.


OPA/rego-Integration

1) Gateway/Proxy vor OpenSearch (empfohlen)

Setup:
Client → API‑Gateway/Proxy (Envoy, NGINX, Kong, Traefik) → OPA (Sidecar/Service) → OpenSearch

  • Der Proxy fängt alle OpenSearch-HTTP-Requests ab (auch _search, _msearch, _bulk, _cat, _plugins/_security etc.).
  • Vor dem Durchlassen ruft der Proxy OPA an (z. B. Envoy ext_authz), übergibt JWT + Request (Methode, Pfad, Indizes, Query-DSL).
  • OPA (Rego) entscheidet: allow/deny und kann Constraints zurückgeben (z. B. erlaubte Indizes, zusätzliche Filter, erlaubte Felder).
  • Der Proxy setzt die Entscheidung durch:
    • Block verbotener Requests sofort.
    • Query-Rewrite: fügt vom OPA gelieferte Filter (DLS) in den Query-Body ein.
    • Source-Filtering: setzt _source_includes für FLS.
    • Index-Gating: limitiert auf erlaubte Index-Patterns.

Beispiel-Entscheidung in Rego (vereinfacht):

package opensearch.authz
 
default allow = false
 
# Aus Token (verifiziert im Gateway) übergeben:
# input.user = { sub, roles, org, licenses }
# input.req  = { method, path, indices, body }
# indices: ["tourism-de-places", "tourism-de-events"]
 
# Indexfreigabe (Mandanten & Lizenzmodell)
allowed_index(idx) {
  startswith(idx, sprintf("tourism-%s-", [input.user.org]))
}
 
# Feldfreigaben je Lizenz
allowed_fields := {
  "OPEN": ["name","geo","category","summary","startDate","endDate"],
  "CLOSED": ["name","geo","category","summary","startDate","endDate","contact","internalNotes"]
}[license] {
  some license
  license := (input.user.licenses[_])
}
 
# Dokument-Constraints (DLS) aus Token ableiten
dls_filter := {
  "bool": {
    "filter": [
      {"terms": {"license": input.user.licenses}},
      {"term":  {"org": input.user.org}}
    ]
  }
}
 
# Erlaubnisregel: read-only, Indizes erlaubt
allow {
  input.req.method == "GET"                       # read-only API
  every i in input.req.indices { allowed_index(i) }
}
 
# Entscheidung + Nebenprodukte zurückgeben
decision := {
  "allow": allow,
  "constraints": {
    "dls": dls_filter,
    "_source_includes": allowed_fields
  }
}

Gateway setzt um (Beispiele):

  • Bei _search/_msearch:
    • Body mit bool.filter ∪= constraints.dls.
    • _source{ "includes": constraints._source_includes }.
  • Bei Pfaden wie /{index}/_search: Indizes gegen allowed_index prüfen; ansonsten 403.
  • _cat- und Metadaten-Endpunkte: nur whitelisten oder per Org-Filter einschränken.
  • Audit: Proxy loggt OPA-Decision (allow/deny + Constraints + Subjekt).

Pluspunkte:

  • Zentral durchsetzbar (auch für Tools wie Dashboards, curl, Beats).
  • Kein OpenSearch-Plugin nötig.
  • Rego-Policies versionierbar, testbar, Hot‑Reload.

Worauf achten?

  • _msearch: jede Sub‑Query separat remappen/prüfen.
  • Scroll & PIT: Tokenbindung prüfen (Scroll-ID/PIT nur für erlaubte Indizes ausgeben).
  • Bulk: pro Action Item durchsetzen (Index‑ und Doc‑Prüfung).
  • Aggregationen: DLS wirkt, aber Denormalisierung beachten (z. B. Terms-Aggs über gefilterten Korpus).

2) Los 1 (Middleware) als PEP

Wenn die Middleware alle Zugriffe auf OpenSearch kapselt (typisch in Los 1), kannst du OPA direkt in der Middleware aufrufen:

  • Middleware validiert JWT, baut einen konformen Query‑Plan (z. B. aus REST-Parametern/GraphQL).
  • Middleware ruft OPA mit input = { user, intent, indices, query } auf.
  • Rückgabe wie oben (allow, constraints), Middleware injiziert Filter in den Query‑DSL, setzt _source_includes, limitiert Indizes.
  • Vorteil: Kein zusätzlicher Netz-Hopf; Policies können domänennah (z. B. „Redakteur darf nur events mit org=… und license ∈ …“) beschrieben werden.
  • Für externe Tools (Kibana/OpenSearch Dashboards) brauchst du trotzdem Gateway-Absicherung, sonst umgehen sie die Middleware.

3) Rollen-Sync in OpenSearch Security (ergänzend)

OpenSearch Security (Plugin) bietet Rollen, DLS/FLS und Role Mappings. Du kannst OPA periodisch/even‑tgetrieben nutzen, um:

  • Role Mappings aus Claims zu schreiben,
  • DLS/FLS pro Rolle zu pflegen (statisch).

Das ist gut für grobe Policies (z. B. „Org‑Admin“), aber nicht für hochdynamische, requestkontextsensitive Regeln (z. B. „lizenzabhängige Feldliste je Benutzer“ pro Request). Daher meist als Baseline, kombiniert mit Pattern 1/2.

Konkrete Umsetzungsschritte

  1. Identitäten & Claims

    • Identity Provider (Keycloak/OIDC) → JWT mit sub, roles, org, licenses (Open/Closed), evtl. purpose/consent.
    • Los 3 (DMS-Frontend) & Widgets (Los 4) erhalten Tokens über denselben Trust.
  2. OPA Deployment

    • Als Sidecar zum Gateway (Envoy + ext_authz) oder als Service.
    • Bundle-/Policy-Distribution via OCI/Bundle-Server, Signaturen, Unit‑Tests der Rego‑Pakete.
  3. PEP (Gateway oder Middleware)

    • Schema‑Valider für OpenSearch‑Bodies (minimiert Angriffsfläche).
    • Query-Rewriter (Merge von bool.filter, Setzen von _source_includes).
    • Index‑Resolver für Wildcards (expandieren, dann prüfen).
    • Safe defaults: deny‑by‑default, allow nur mit vollständiger OPA‑Entscheidung.
  4. Edge Cases absichern

    • _cat/* nur freigeben, wenn unkritisch oder gefiltert.
    • _cluster/*, Snapshots, Settings: i. d. R. sperren.
    • Rate Limits pro Subjekt/Route.
    • Scroll/PIT an Identität binden (metadaten im Server, TTLs).
    • Bulk/Reindex: Optional nur internen Diensten erlauben.
  5. Auditing & Monitoring

    • Jede OPA‑Entscheidung (Input/Outcome, redacted) auditieren.
    • Dashboards: Zulassungsquote, Deny‑Ursachen, Latenz von OPA‑Calls.
    • Chaos‑Tests: OPA nicht erreichbar → fail‑closed.

Mini‑Beispiele

Envoy (ext_authz) → OPA (skizziert)

http_filters:
- name: envoy.filters.http.ext_authz
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
    grpc_service:
      envoy_grpc:
        cluster_name: opa
    include_peer_certificate: false
    with_request_body:
      max_request_bytes: 8192
      allow_partial_message: true
    failure_mode_allow: false  # fail-closed

NGINX (lua) → OPA (HTTP)

  • access_by_lua liest JWT + Request, POST an http://opa/v1/data/opensearch/authz, wertet decision aus, modifiziert ggf. Body (per ngx.req.set_body_data).

Rego-Unit‑Test (kurz)

test_allow_search_for_org() {
  input := {
    "user": {"org":"de","licenses":["OPEN"]},
    "req":  {"method":"GET","indices":["tourism-de-events"],"body":{}}
  }
  out := data.opensearch.authz.decision with input as input
  out.allow
  out.constraints._source_includes == ["name","geo","category","summary","startDate","endDate"]
}

Wie passt das zu den Losen?

  • Los 1 (Middleware): Ideal als PEP mit Rego‑Policies für Index‑/Doku‑/Feldebene, Query‑Rewrite, Audit.
  • Los 3 (DMS-Frontend): Nutzt dieselben Claims; UI kann Felder dynamisch ausblenden (Defense‑in‑Depth).
  • Los 2 (Datenintegration): Service‑Account‑Routen (Bulk/Index) getrennt und enger gefasst.
  • Los 4 (Widgets): Leichtgewichtige Clients konsumieren abgesicherte Endpunkte, kein Geheimnis im Frontend.

TL;DR

  • OPA + Gateway/Middleware ist der robuste Weg, Rego‑Policies per Request für OpenSearch durchzusetzen.
  • Nutze Query‑Rewrite (DLS), _source‑Filtering (FLS) und Index‑Gating aus den OPA‑Constraints.
  • Ergänzend kann OpenSearch Security für grobe Rollenzuordnung dienen, die feingranulare Logik bleibt in Rego.