Jaeger Distributed Tracing Security Testing: Unauthenticated UI, Trace Injection, and Sampling Manipulation

Jaeger is one of the most widely deployed open-source distributed tracing systems, used to monitor request flow across microservices. Its default configuration is optimized for developer convenience over security: the Jaeger UI on port 16686 requires no authentication and exposes a complete service dependency graph, all stored traces with their span attributes, and internal hostnames and database query strings; the Jaeger collector on port 14268 accepts HTTP trace submission from any source without validation — enabling an attacker to inject forged spans into existing traces; the gRPC query API on port 16685 exposes the full trace query interface without mTLS; and Elasticsearch or Cassandra backend credentials stored in Jaeger deployment configuration expose all historical trace data if the Jaeger deployment configuration is accessible. This guide covers systematic Jaeger security assessment.

Table of Contents

  1. Jaeger Port Discovery
  2. Unauthenticated UI and API Testing
  3. Trace and Span Injection
  4. Sampling Rate Manipulation
  5. Sensitive Data in Span Attributes
  6. Jaeger Security Hardening

Jaeger Port Discovery

# Jaeger component ports:
# 16686 — Jaeger UI and HTTP query API
# 16685 — gRPC query API
# 14268 — Jaeger collector HTTP (accepts Thrift spans)
# 14250 — Jaeger collector gRPC
# 6831  — Jaeger agent UDP (compact Thrift)
# 6832  — Jaeger agent UDP (binary Thrift)
# 5778  — Jaeger agent HTTP (sampling config, metrics)
# 9411  — Zipkin compatible endpoint

# Scan for exposed Jaeger ports
nmap -p 16686,14268,14250,5778,6831 target.example.com 2>/dev/null | grep -E "open|filtered"

# Check Jaeger UI availability (no auth needed in default deployments)
curl -s http://jaeger.example.com:16686/ 2>/dev/null | grep -i "jaeger" | head -3

# List all services tracked by Jaeger (no auth)
curl -s "http://jaeger.example.com:16686/api/services" 2>/dev/null | python3 -c "
import json,sys
data = json.load(sys.stdin)
services = data.get('data', [])
print(f'Services: {len(services)}')
for s in services:
    print(f'  {s}')
" 2>/dev/null | head -20

Unauthenticated UI and API Testing

# Query traces for a specific service (reveals internal call patterns)
curl -s "http://jaeger.example.com:16686/api/traces?service=payment-service&limit=20" 2>/dev/null | \
  python3 -c "
import json,sys
data = json.load(sys.stdin)
traces = data.get('data', [])
print(f'Traces found: {len(traces)}')
for t in traces[:3]:
    spans = t.get('spans', [])
    print(f'  TraceID: {t[\"traceID\"]} — {len(spans)} spans')
    for s in spans[:2]:
        tags = {tag[\"key\"]: tag[\"value\"] for tag in s.get(\"tags\", [])}
        print(f'    {s.get(\"operationName\",\"?\")} tags: {list(tags.keys())}')
" 2>/dev/null

# Get service dependency graph (full internal architecture map)
curl -s "http://jaeger.example.com:16686/api/dependencies?endTs=$(date +%s)000&lookback=604800000" \
  2>/dev/null | python3 -c "
import json,sys
data = json.load(sys.stdin)
deps = data.get('data', [])
print(f'Service dependencies: {len(deps)}')
for d in deps[:10]:
    print(f\"  {d.get('parent','?')} → {d.get('child','?')} ({d.get('callCount',0)} calls)\")
" 2>/dev/null

Jaeger Security Hardening

Jaeger Security Hardening Checklist:
Security TestMethodRisk
Unauthenticated UI exposes service topologyBrowse :16686 — lists all services and dependency graphHigh
Collector port accepts forged span injectionPOST forged spans to :14268/api/traces — injects spans into existing tracesHigh
Span tags contain sensitive dataQuery traces — check tags for auth headers, DB queries, user IDsHigh
Sampling config endpoint exposedGET :5778/sampling?service=foo — reveals sampling strategy without authMedium
gRPC query API accessible without mTLSConnect to :16685 without client cert — returns full trace dataMedium
Backend (ES/Cassandra) credentials in configRead Jaeger deployment env vars or ConfigMap — exposes DB credentialsHigh

Automate Jaeger Security Testing

Ironimo tests Jaeger distributed tracing deployments for unauthenticated UI access exposing complete service dependency graphs and all stored traces, collector port accepting forged span injection into existing traces, span tag sensitive data leakage including auth tokens and database queries, sampling configuration endpoint exposure, gRPC query API access without mutual TLS, and backend Elasticsearch/Cassandra credential exposure in Jaeger deployment configuration.

Start free scan