Security¶
This page describes the operational security posture of tailscale2otel: what data it
handles, where that data goes, and the configuration levers and footguns operators should
be aware of.
Telemetry payload sensitivity¶
tailscale2otel exports network metadata about your tailnet. Flow logs and audit logs
carry, among other things:
- source and destination IP addresses and ports,
- device names and hostnames,
- user identities (e.g. the actor on an audit event).
All of this is exported over OTLP to the configured backend (for example Grafana Cloud). Treat the OTLP backend as a trusted data sink — anyone with read access to it can see this metadata. Scope backend credentials accordingly.
Levers to reduce what leaves the tailnet (all under the cardinality: block in
config.example.yaml):
cardinality.flow.source_port/cardinality.flow.destination_port(both defaultfalse) — keep ports off flow metrics. Ports are always present on flow logs regardless of these settings.cardinality.flow.collapse_external(defaulttrue) — buckets unresolved IPs asexternal/unknownrather than emitting them as distinct series/labels.cardinality.flow.node_dims(defaulttrue) — setfalseto omit src/dst device names from flow metrics.
Disabling devices does not remove IPs
Disabling the devices collector does not remove IP addresses from the exported
payload — it only degrades IP→name enrichment, so flow/audit records fall back to
unknown/external for names while the raw addresses are still exported.
Receiver authentication footguns¶
The optional streaming (Splunk-HEC) and webhook receivers accept inbound POSTs. Their
authentication is opt-in by presence of a secret, so an empty value silently disables
it (these behaviours are also noted in
config.example.yaml):
- Leaving
webhook.secretempty skips HMAC verification entirely — unauthenticated POSTs are accepted. - Leaving
streaming.tokenempty disables receiver authentication.
Always set credentials before exposing a receiver
Always set these when exposing a receiver, especially on a wildcard/all-interfaces bind
or without TLS. Tailscale requires HTTPS for the streaming sink; a tailscale cert
works for private tailnet endpoints.
Mistyped environment variable names silently leave auth disabled
Any field can be set via a TS2OTEL_* environment variable (the env layer overrides the
file), and an empty credential silently disables auth — for example a mistyped
variable name (TS2OTEL_WEBHOOK__SECRT) leaves the secret empty rather than failing
loudly. The startup log WARNs on a TS2OTEL_* variable that matches no config key, but
double-check that auth credentials are actually set.
streaming.auto_configure footgun¶
auto_configure overwrites your existing log-streaming sink
When streaming.auto_configure: true, the service registers this receiver as the
tailnet's log-streaming sink on startup, and overwrites any existing sink configured
for the tailnet. Never enable it against a tailnet whose streaming configuration you do
not intend to replace. It is off by default.
Secrets handling¶
- Keep secrets in
TS2OTEL_*environment variables (the env layer overrides the file), never as literal values in YAML.config.local.yaml,.env.local, and.secrets/are gitignored for this reason. - The admin status page redacts secret values — it emits only
*Setbooleans (e.g.webhook_secret_set) and OTLP header key names, never the values themselves. - Secrets are never logged.
pprof requires admin auth
Enabling profiling.pprof.enabled requires both admin.enabled and
admin.auth.token, because heap and goroutine dumps can expose in-memory secrets.
See configuration.md for the full config reference and
env-vars.md for the complete list of TS2OTEL_* environment variables.