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.
# 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
# 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
--admin.http.tls.enabled| Security Test | Method | Risk |
|---|---|---|
| Unauthenticated UI exposes service topology | Browse :16686 — lists all services and dependency graph | High |
| Collector port accepts forged span injection | POST forged spans to :14268/api/traces — injects spans into existing traces | High |
| Span tags contain sensitive data | Query traces — check tags for auth headers, DB queries, user IDs | High |
| Sampling config endpoint exposed | GET :5778/sampling?service=foo — reveals sampling strategy without auth | Medium |
| gRPC query API accessible without mTLS | Connect to :16685 without client cert — returns full trace data | Medium |
| Backend (ES/Cassandra) credentials in config | Read Jaeger deployment env vars or ConfigMap — exposes DB credentials | High |
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