@@ -102,6 +102,7 @@ TIMESTAMP=$(date +%s)000000000
102102TRACE_ID=$( uuidgen | tr -d ' -' )
103103
104104LOKI_PUSH_RESPONSE=$( curl -s -X POST " $LOKI_ENDPOINT /loki/api/v1/push" \
105+ -H " X-Scope-OrgID: default" \
105106 -H " Content-Type: application/json" \
106107 -d ' {
107108 "streams": [
@@ -140,6 +141,7 @@ START_TIME_LOKI=$(($(date +%s) - 300))
140141for i in {1..6}; do
141142 sleep 5
142143 LOKI_QUERY_RESPONSE=$( curl -s -G " $LOKI_ENDPOINT /loki/api/v1/query_range" \
144+ -H " X-Scope-OrgID: default" \
143145 --data-urlencode ' query={job="smoke-test"}' \
144146 --data-urlencode " start=$START_TIME_LOKI " \
145147 --data-urlencode ' limit=10' || echo " FAILED" )
185187)
186188
187189MIMIR_PUSH_RESPONSE=$( echo " $MIMIR_PUSH " | curl -s -X POST " $MIMIR_ENDPOINT /api/v1/push" \
190+ -H " X-Scope-OrgID: default" \
188191 -H " Content-Type: application/x-protobuf" \
189192 -H " X-Prometheus-Remote-Write-Version: 0.1.0" \
190193 --data-binary @- || echo " FAILED" )
@@ -207,6 +210,7 @@ for i in {1..12}; do
207210 # Instead of a manual push which is hard with curl, we verify that Prometheus is successfully
208211 # remote-writing its own metrics to Mimir. We look for any metric starting with 'prometheus_'
209212 MIMIR_QUERY_RESPONSE=$( curl -s -G " $MIMIR_ENDPOINT /prometheus/api/v1/query" \
213+ -H " X-Scope-OrgID: default" \
210214 --data-urlencode ' query={__name__=~"prometheus_.*"}' || echo " FAILED" )
211215
212216 if [[ " $MIMIR_QUERY_RESPONSE " != " FAILED" ]] && echo " $MIMIR_QUERY_RESPONSE " | jq -e ' .data.result | length > 0' > /dev/null 2>&1 ; then
306310)
307311
308312TEMPO_PUSH_RESPONSE=$( echo " $TEMPO_TRACE " | curl -s -X POST " $TEMPO_INGEST_ENDPOINT /v1/traces" \
313+ -H " X-Scope-OrgID: default" \
309314 -H " Content-Type: application/json" \
310315 -d @- || echo " FAILED" )
311316
@@ -324,7 +329,7 @@ TEMPO_QUERY_SUCCESS=false
324329# Try for up to 60 seconds for Tempo as tracing can be slower to index
325330for i in {1..12}; do
326331 sleep 5
327- TEMPO_QUERY=$( curl -s " $TEMPO_QUERY_ENDPOINT /api/traces/${TRACE_ID} " || echo " FAILED" )
332+ TEMPO_QUERY=$( curl -s -H " X-Scope-OrgID: default " " $TEMPO_QUERY_ENDPOINT /api/traces/${TRACE_ID} " || echo " FAILED" )
328333
329334 if [[ " $TEMPO_QUERY " != " FAILED" ]] && echo " $TEMPO_QUERY " | jq -e ' .batches | length > 0' > /dev/null 2>&1 ; then
330335 record_test " tempo" " query_trace" " PASS" " Successfully retrieved trace"
@@ -395,6 +400,87 @@ done
395400
396401
397402
403+
404+ # =============================================================================
405+ # MULTI-TENANCY ISOLATION TESTS
406+ # Validates that tenant data is fully isolated — webank cannot see default
407+ # data, and default cannot see webank data.
408+ # Acceptance Criteria: "Team A cannot view Team B's logs in Loki/Mimir/Tempo"
409+ # =============================================================================
410+ echo " "
411+ echo " 🔒 Testing Multi-Tenancy Isolation..."
412+
413+ ISOLATION_OK=true
414+
415+ # --- Loki Isolation ---
416+ echo " 📝 [Loki] Pushing a secret log to 'webank' tenant..."
417+ ISOLATION_TIMESTAMP=$( date +%s) 000000000
418+ WEBANK_SECRET=" WEBANK-ONLY-SECRET-$( uuidgen) "
419+
420+ curl -s -X POST " $LOKI_ENDPOINT /loki/api/v1/push" \
421+ -H " X-Scope-OrgID: webank" \
422+ -H " Content-Type: application/json" \
423+ -d " {\" streams\" :[{\" stream\" :{\" job\" :\" isolation-test\" },\" values\" :[[\" $ISOLATION_TIMESTAMP \" ,\" $WEBANK_SECRET \" ]]}" \
424+ > /dev/null
425+
426+ sleep 6
427+
428+ # Query as 'default' — must NOT see the webank secret
429+ ISOLATION_AS_DEFAULT=$( curl -s -G " $LOKI_ENDPOINT /loki/api/v1/query_range" \
430+ -H " X-Scope-OrgID: default" \
431+ --data-urlencode ' query={job="isolation-test"}' \
432+ --data-urlencode " start=$(( $(date +% s) - 60 )) " \
433+ --data-urlencode ' limit=10' || echo " FAILED" )
434+
435+ if echo " $ISOLATION_AS_DEFAULT " | grep -q " $WEBANK_SECRET " ; then
436+ record_test " isolation" " loki_cross_tenant_leak" " FAIL" " CRITICAL: default tenant CAN see webank data — isolation is BROKEN"
437+ echo " ❌ CRITICAL: Loki isolation FAILED — default tenant sees webank data!"
438+ ISOLATION_OK=false
439+ else
440+ record_test " isolation" " loki_cross_tenant_leak" " PASS" " default tenant cannot see webank data"
441+ echo " ✅ Loki: default tenant cannot see webank data"
442+ fi
443+
444+ # Query as 'webank' — MUST see its own secret
445+ ISOLATION_AS_WEBANK=$( curl -s -G " $LOKI_ENDPOINT /loki/api/v1/query_range" \
446+ -H " X-Scope-OrgID: webank" \
447+ --data-urlencode ' query={job="isolation-test"}' \
448+ --data-urlencode " start=$(( $(date +% s) - 60 )) " \
449+ --data-urlencode ' limit=10' || echo " FAILED" )
450+
451+ if echo " $ISOLATION_AS_WEBANK " | grep -q " $WEBANK_SECRET " ; then
452+ record_test " isolation" " loki_tenant_reads_own" " PASS" " webank tenant can read its own logs"
453+ echo " ✅ Loki: webank tenant can read its own logs"
454+ else
455+ record_test " isolation" " loki_tenant_reads_own" " FAIL" " webank tenant cannot read its own logs"
456+ echo " ❌ Loki: webank tenant cannot read its own logs"
457+ ISOLATION_OK=false
458+ fi
459+
460+ # --- Mimir Isolation ---
461+ echo " 📊 [Mimir] Checking metric namespace isolation..."
462+
463+ # Query a metric as 'webank' — it must not see 'prometheus_*' metrics
464+ # (those are shipped by Prometheus under the 'default' tenant)
465+ WEBANK_SEES_DEFAULT=$( curl -s -G " $MIMIR_ENDPOINT /prometheus/api/v1/query" \
466+ -H " X-Scope-OrgID: webank" \
467+ --data-urlencode ' query={__name__=~"prometheus_.*"}' || echo " FAILED" )
468+
469+ if [[ " $WEBANK_SEES_DEFAULT " != " FAILED" ]] && echo " $WEBANK_SEES_DEFAULT " | jq -e ' .data.result | length > 0' > /dev/null 2>&1 ; then
470+ record_test " isolation" " mimir_cross_tenant_leak" " FAIL" " CRITICAL: webank tenant sees prometheus_* metrics from 'default' tenant"
471+ echo " ❌ CRITICAL: Mimir isolation FAILED — webank sees default tenant metrics!"
472+ ISOLATION_OK=false
473+ else
474+ record_test " isolation" " mimir_cross_tenant_leak" " PASS" " webank tenant cannot see default tenant metrics"
475+ echo " ✅ Mimir: webank tenant cannot see default tenant metrics"
476+ fi
477+
478+ if [ " $ISOLATION_OK " = true ]; then
479+ echo " 🎉 All isolation tests PASSED — multi-tenancy is working correctly"
480+ else
481+ echo " ❌ Isolation tests FAILED — multi-tenancy is NOT properly enforced"
482+ fi
483+
398484# =============================================================================
399485# INTEGRATION TEST
400486# =============================================================================
0 commit comments