mirror of
https://github.com/tinyauthapp/tinyauth.git
synced 2026-09-24 03:33:31 +08:00
The app name fallback matches any domain that starts with the app name, so an app named myapp served on myapp.example.com also defined the ACLs of myapp.evil.com. Behind a proxy with a catch-all route, a request can be authorized against the wrong app that way. Label providers now receive the domain being authorized. The Kubernetes provider keeps the hosts of every Ingress, HTTPRoute and GRPCRoute it watches and withholds the apps of the resources that do not route the domain, which bounds the name fallback to the hosts a resource actually serves. Wildcard hostnames keep matching as a suffix, so nested subdomains stay resolvable by app name. Container labels carry no routing information, so the Docker provider cannot narrow its results down and keeps yielding every app. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
117 lines
2.8 KiB
Go
117 lines
2.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/steveiliop56/ding"
|
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
|
"github.com/tinyauthapp/tinyauth/internal/utils/decoders"
|
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
|
"go.uber.org/dig"
|
|
|
|
container "github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/client"
|
|
)
|
|
|
|
type DockerService struct {
|
|
log *logger.Logger
|
|
client *client.Client
|
|
context context.Context
|
|
|
|
isConnected bool
|
|
}
|
|
|
|
type DockerServiceInput struct {
|
|
dig.In
|
|
|
|
Log *logger.Logger
|
|
Ctx context.Context
|
|
Ding *ding.Ding
|
|
}
|
|
|
|
func NewDockerService(i DockerServiceInput) (*DockerService, error) {
|
|
client, err := client.NewClientWithOpts(client.FromEnv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client.NegotiateAPIVersion(i.Ctx)
|
|
|
|
_, err = client.Ping(i.Ctx)
|
|
|
|
if err != nil {
|
|
i.Log.App.Debug().Err(err).Msg("Docker not connected")
|
|
return nil, nil
|
|
}
|
|
|
|
service := &DockerService{
|
|
log: i.Log,
|
|
client: client,
|
|
context: i.Ctx,
|
|
}
|
|
|
|
service.isConnected = true
|
|
service.log.App.Debug().Msg("Docker connected successfully")
|
|
|
|
i.Ding.Go(service.watchAndClose, ding.RingMajor)
|
|
|
|
return service, nil
|
|
}
|
|
|
|
func (docker *DockerService) getContainers() ([]container.Summary, error) {
|
|
return docker.client.ContainerList(docker.context, container.ListOptions{})
|
|
}
|
|
|
|
func (docker *DockerService) inspectContainer(containerId string) (container.InspectResponse, error) {
|
|
return docker.client.ContainerInspect(docker.context, containerId)
|
|
}
|
|
|
|
// Lookup yields every app labelled on a running container. Container labels
|
|
// carry no routing information, so the domain cannot be used to narrow the
|
|
// results down and the caller is left to match them.
|
|
func (docker *DockerService) Lookup(_ string, locator func(name string, app *model.App) bool) error {
|
|
if !docker.isConnected {
|
|
docker.log.App.Debug().Msg("Docker service not connected, returning empty labels")
|
|
return nil
|
|
}
|
|
|
|
containers, err := docker.getContainers()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get containers: %w", err)
|
|
}
|
|
|
|
for _, ctr := range containers {
|
|
inspect, err := docker.inspectContainer(ctr.ID)
|
|
if err != nil {
|
|
docker.log.App.Error().Err(err).Msgf("Failed to inspect container %s", ctr.ID)
|
|
continue
|
|
}
|
|
|
|
labels, err := decoders.DecodeLabels[model.Apps](inspect.Config.Labels, "apps")
|
|
if err != nil {
|
|
docker.log.App.Warn().Err(err).Msgf("Failed to decode labels for container %s", ctr.ID)
|
|
continue
|
|
}
|
|
|
|
for app, config := range labels.Apps {
|
|
if ok := locator(app, &config); ok {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (docker *DockerService) watchAndClose(ctx context.Context) {
|
|
<-ctx.Done()
|
|
docker.log.App.Debug().Msg("Closing Docker client")
|
|
if docker.client != nil {
|
|
err := docker.client.Close()
|
|
if err != nil {
|
|
docker.log.App.Error().Err(err).Msg("Error closing Docker client")
|
|
}
|
|
}
|
|
}
|