Skip to main content
RunBook Academy

← All checklists in Observability

Before deploymenttelemetry-security

Telemetry Security Review

24 items ·20 critical ·3 warn ·1 info

Run this before the platform takes production telemetry, and re-run it quarterly and after any change to the network topology, the identity model, or the set of teams pushing data into it.

It overlaps the Grafana readiness gate deliberately and asks a different question. That gate covers who can log into the dashboard and what they can see once they are in. This review covers everything behind it: what each component listens on, who can call it directly without going through Grafana at all, and what the stored data would be worth to somebody who got there.

The two questions underneath

The first is exposure: the telemetry platform is a system with a network listener on every host in the estate and a copy of what those hosts are doing. Its defaults are permissive because upstream optimises for a first install that works, and every one of those defaults is a decision somebody has to make again for production.

The second is content. Telemetry is a data store, and it is the one data store nobody classified — the logs, spans and labels arrive from a hundred call sites written by people who were thinking about a bug, not about a retention policy. The first question decides how hard the platform is to reach. The second decides what the reach is worth.

Where the numbers come from

Most items are one shell command on a component host, one call to a config or metrics endpoint, or one object-store API call. Substitute your own addresses and bucket names; the ones below are the shapes used throughout this course, not an estate that exists.

Four items are attested rather than measured: the data classification, the source-redaction discipline, the leak runbook and the cross-tenant policy. Those are the four an auditor will ask about, and a command cannot answer any of them.

Access this needs

Shell access on the component hosts for the bind and firewall items, read access to each component’s HTTP API, read-only credentials for the object store, and read access to the repository holding the configuration and the runbooks. The scan items need a host on the network you are scanning from. Nothing on this list writes.

Sign-off

  • Reviewer: ________________ Date: ___________
  • Platform owner: ___________ Date: ___________
  • Security owner: ___________ Date: ___________

Critical20 items

  1. ss -tlnp | grep -E ':9090|:9091|:9093|:9100|:3100|:9095|:9096|:3200|:4317|:4318|:12345|:3000'
  2. docker ps --format '{{.Names}}\t{{.Ports}}'; kubectl get svc -A -o wide | grep -iE 'loadbalancer|nodeport'
  3. PUBLIC_ADDR=203.0.113.10; nmap -p 9090,9091,9093,9100,3100,3200,4317,4318,12345,3000 "$PUBLIC_ADDR"
  4. iptables -L INPUT -n -v --line-numbers | head -30
  5. MONITORED_HOST=192.0.2.20; curl -fsS --max-time 3 "http://$MONITORED_HOST:9100/metrics" | grep -c '^node_filesystem_size_bytes'
  6. curl -s -o /dev/null -w '%{http_code}\n' http://loki:3100/loki/api/v1/labels; curl -s -o /dev/null -w '%{http_code}\n' http://prometheus:9090/api/v1/query?query=up
  7. curl -s -o /dev/null -w 'admin=%{http_code}\n' -X POST http://127.0.0.1:9090/api/v1/admin/tsdb/snapshot; curl -s -o /dev/null -w 'reload=%{http_code}\n' -X PUT http://127.0.0.1:9090/-/reload
  8. echo | openssl s_client -connect loki.example.internal:3100 2>/dev/null | grep -E 'Protocol|Cipher|Verify return code'
  9. for h in prometheus.example.internal:9090 loki.example.internal:3100 tempo.example.internal:3200; do echo "== $h"; echo | openssl s_client -connect "$h" 2>/dev/null | openssl x509 -noout -dates; done
  10. git grep -nEi '(bearer|password|secret|token|access_key)[[:space:]]*[:=][[:space:]]*[^$#{[:space:]]' -- '*.yml' '*.yaml' '*.ini' '*.json'
  11. otelcol validate --config=/etc/otelcol/config.yaml; sed -n '/^processors:/,/^exporters:/p' /etc/otelcol/config.yaml
  12. gitleaks detect --no-git --source /var/exports/telemetry-24h --report-format json --report-path /var/exports/leak-report.json
  13. curl -s http://loki:3100/config | yq '.auth_enabled'; curl -s -o /dev/null -w '%{http_code}\n' -X POST http://loki-distributor:3100/loki/api/v1/push -H 'Content-Type: application/json' -d '{"streams":[{"stream":{"job":"probe"},"values":[["1700000000000000000","probe"]]}]}'
  14. curl -s -o /dev/null -w '%{http_code}\n' http://tempo:3200/api/search?tags=; curl -s http://tempo:3200/config | grep -m1 auth_enabled
  15. for b in prod-loki-chunks prod-tempo-traces; do echo "== $b"; aws s3api get-bucket-policy --bucket "$b" | jq -r '.Policy' | jq '.Statement[] | {Action, Resource, Principal}'; done
  16. for b in prod-loki-chunks prod-tempo-traces; do echo "== $b"; aws s3api get-bucket-encryption --bucket "$b"; done

Warning3 items

  1. stat -c '%y %n' /etc/loki/s3-credentials; ps -o lstart= -p "$(pgrep -f 'loki -config.file' | head -1)"

Info1 item