Kubernetes Integration#
Tinyauth integrates with the Kubernetes Ingress API (networking.k8s.io/v1) to dynamically read per-app access control configuration from ingress annotations. This is one of three label-provider modes β docker, kubernetes, and none β selected at startup via the LabelProvider config value . In auto mode, Tinyauth selects Kubernetes when the KUBERNETES_SERVICE_HOST environment variable is set .
The integration was refactored as part of v5.1.0 (released 2026-07-15), which migrated all services and controllers to Uber's dig dependency-injection container .
Architecture Overview#
Bootstrap
βββ getLabelProvider()
βββ NewKubernetesService(KubernetesServiceInput) [dig-injected]
βββ Validate in-cluster config & probe Ingress API
βββ Register watchGVR goroutine (via ding)
βββ Returns *KubernetesService implementing LabelProvider
βββ GetLabels(domain) β *model.App
KubernetesService#
Entry point: internal/service/kubernetes_service.go
KubernetesService holds:
- A
dynamic.InterfaceKubernetes client - An
ingressAppsmap keyed by{namespace, name} - A
domainIndexandappNameIndexfor O(1) lookup
Initialization & Fail-Fast Probing#
NewKubernetesService uses a dig.In input struct and performs a 5-second timeout probe of the Ingress API at startup . If the probe fails, the constructor returns a non-nil error β this is the intentional fail-fast signal. The bootstrap layer catches that error and degrades gracefully: if the provider was set to kubernetes explicitly it logs a warning and continues without it; the service is simply disabled .
Watch Loop & Resync#
Once initialized, a background goroutine (managed by ding at RingMajor priority) runs watchGVR, which:
- Initial resync β lists all ingresses and calls
updateFromItemon each - Watch loop β streams
Added/Modified/Deletedevents viak8s.io/client-go/dynamic - Periodic resync β every 5 minutes via a ticker to catch missed events
- Auto-restart β if the watch channel closes, it waits 5 seconds and restarts; failed watch starts wait 10 seconds before retry
Label Parsing & Annotation Format#
updateFromItem reads ingress annotations via decoders.DecodeLabels[model.Apps] using the "apps" prefix key. The expected annotation format mirrors the Docker label pattern: tinyauth.apps.<app-name>.<field>. Apps with no matching Config.Domain or whose domain doesn't appear in the ingress spec.rules.host list are skipped with a warning .
A security warning is emitted if an ingress rule lacks a catch-all / path β a non-/ path could allow another ingress with a different path to bypass auth on the same host .
Label Lookup#
GetLabels(domain) implements the LabelProvider interface . It:
- Returns
nil, nilimmediately if the service didn't start - Looks up by exact domain via
domainIndex - Falls back to the first subdomain component as an app-name lookup via
appNameIndex
Dependency Injection (dig)#
The v5.1.0 refactor (PR #936) replaced manual constructor calls throughout the service layer with Uber's dig container . Key points:
- Every service now declares a
<Name>ServiceInputstruct embeddingdig.In, replacing positional constructor arguments . - Services registered via
dig.Provide(); any failure is a hard error β thefor _, provider := range serviceProvideForloop returns immediately on error . - Optional services (
LdapService,TailscaleService) use theoptional:"true"struct tag to allow absent providers without failing the container . - The
LabelProviderinterface is wired in as a concrete factory before other services are registered, ensuring all downstream services see the correct provider .
Fail-fast bootstrap: errors from Provide() or Invoke() propagate up through setupServices() β getLabelProvider() and abort application startup immediately, preventing partially-initialized states .
Key Files#
| File | Purpose |
|---|---|
internal/service/kubernetes_service.go | KubernetesService struct, watch loop, label parsing |
internal/bootstrap/service_bootstrap.go | dig wiring, label provider selection, fail-fast bootstrap |
internal/service/access_controls_service.go | LabelProvider interface definition |