Docker Label-Based Service Discovery#
Overview#
Tinyauth can dynamically discover per-application ACL configuration from running Docker containers by reading labels in the tinyauth.apps.* namespace. This is one of three label-provider modes (docker, kubernetes, none) selected at startup via the LabelProvider config field . In auto mode (the default), Tinyauth uses Docker unless KUBERNETES_SERVICE_HOST is set .
The Docker socket must be accessible to Tinyauth β in the dev environment, /var/run/docker.sock is mounted directly into the container . The LabelProvider field in the AccessControlsService is injected as optional , so the service functions normally when no provider is configured.
How Discovery Works#
On each auth request, AccessControlsService.GetAccessControls(domain) first checks static YAML config, then falls back to the label provider. The Docker provider's Lookup method:
- Lists all running containers via the Docker API .
- Inspects each container for its full label map .
- Decodes labels into a typed
model.Appsstruct usingDecodeLabels, which delegates to thepaerserlibrary with the prefixtinyauth.apps. - For each decoded app, calls a
locatorcallback that tries two match strategies against the incoming domain :- Exact domain match β compares
tinyauth.apps.<name>.config.domainagainst the request host. - Name-prefix match β checks if the request host starts with
<app-name>.(e.g.,myapp.example.commatches an app keymyapp).
- Exact domain match β compares
Domain match takes priority; the locator returns immediately when a domain match is found. Container inspection errors are non-fatal β Tinyauth logs a warning and continues to the next container .
The LabelProvider interface is minimal: a single Lookup(locator func(name string, app *model.App) bool) error method, allowing Docker and Kubernetes providers to be interchangeable.
Label Reference#
All labels follow the pattern tinyauth.apps.<app-name>.<field>. All list values are comma-separated strings .
The App struct maps directly to the label hierarchy:
| Label | Sub-struct | Purpose |
|---|---|---|
tinyauth.apps.<name>.config.domain | AppConfig | Explicit domain binding |
tinyauth.apps.<name>.users.allow | AppUsers | Username allowlist |
tinyauth.apps.<name>.users.block | AppUsers | Username blocklist |
tinyauth.apps.<name>.oauth.whitelist | AppOAuth | OAuth email allowlist |
tinyauth.apps.<name>.oauth.groups | AppOAuth | Required OAuth groups |
tinyauth.apps.<name>.ldap.groups | AppLDAP | Required LDAP groups |
tinyauth.apps.<name>.ip.allow | AppIP | IP/CIDR allowlist |
tinyauth.apps.<name>.ip.block | AppIP | IP/CIDR blocklist |
tinyauth.apps.<name>.ip.bypass | AppIP | IPs that skip auth entirely |
tinyauth.apps.<name>.path.allow | AppPath | Regex paths that bypass auth |
tinyauth.apps.<name>.path.block | AppPath | Regex paths that require auth |
tinyauth.apps.<name>.response.headers | AppResponse | Key=Value headers to inject |
tinyauth.apps.<name>.response.basicauth.username | AppResponse | Upstream basic auth username |
tinyauth.apps.<name>.response.basicauth.password | AppResponse | Upstream basic auth password |
Key Behaviors and Caveats#
- Static config wins. If a matching app exists in the static YAML
Config.Appsmap, labels on Docker containers are never consulted for that domain . - No caching. The Docker API is called on every auth request; there is no in-memory cache of container labels.
- Graceful degradation. If Docker is not reachable at startup,
NewDockerServicereturnsniland Tinyauth logs a warning but continues running without label support . - IP filtering requires trusted proxies. Per-app
ip.*labels only take effect whenAuth.TrustedProxiesis configured; without it, IP rules are non-operational .
Key Files#
| File | Role |
|---|---|
internal/service/docker_service.go | Docker client, container listing, label extraction |
internal/utils/decoders/label_decoder.go | Generic DecodeLabels wrapper over paerser |
internal/service/access_controls_service.go | LabelProvider interface, GetAccessControls, domain matching |
internal/bootstrap/service_bootstrap.go | Provider selection logic (auto/docker/kubernetes/none) |
internal/model/config.go | App, Apps, AppConfig structs; LabelProvider config field |