This commit is contained in:
Stavros
2025-12-29 22:44:23 +02:00
parent 986ac88e14
commit 333b854533
3 changed files with 55 additions and 128 deletions

View File

@@ -57,7 +57,7 @@ func (app *BootstrapApp) initServices() (Services, error) {
services.dockerService = dockerService services.dockerService = dockerService
accessControlsService := service.NewAccessControlsService(dockerService) accessControlsService := service.NewAccessControlsService(dockerService, app.config.Apps)
err = accessControlsService.Init() err = accessControlsService.Init()

View File

@@ -15,6 +15,7 @@ var RedirectCookieName = "tinyauth-redirect"
// Main app config // Main app config
type Config struct { type Config struct {
Apps
AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"` AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"`
LogLevel string `description:"Log level (trace, debug, info, warn, error)." yaml:"logLevel"` LogLevel string `description:"Log level (trace, debug, info, warn, error)." yaml:"logLevel"`
ResourcesDir string `description:"The directory where resources are stored." yaml:"resourcesDir"` ResourcesDir string `description:"The directory where resources are stored." yaml:"resourcesDir"`
@@ -156,61 +157,55 @@ type RedirectQuery struct {
RedirectURI string `url:"redirect_uri"` RedirectURI string `url:"redirect_uri"`
} }
// Labels // ACLs
type Apps struct { type Apps struct {
Apps map[string]App Apps map[string]App `description:"App ACLs configuration." yaml:"apps"`
} }
type App struct { type App struct {
Config AppConfig Config AppConfig `description:"App configuration." yaml:"config"`
Users AppUsers Users AppUsers `description:"User access configuration." yaml:"users"`
OAuth AppOAuth OAuth AppOAuth `description:"OAuth access configuration." yaml:"oauth"`
IP AppIP IP AppIP `description:"IP access configuration." yaml:"ip"`
Response AppResponse Response AppResponse `description:"Response customization." yaml:"response"`
Path AppPath Path AppPath `description:"Path access configuration." yaml:"path"`
} }
type AppConfig struct { type AppConfig struct {
Domain string Domain string `description:"The domain of the app." yaml:"domain"`
} }
type AppUsers struct { type AppUsers struct {
Allow string Allow string `description:"Comma-separated list of allowed users." yaml:"allow"`
Block string Block string `description:"Comma-separated list of blocked users." yaml:"block"`
} }
type AppOAuth struct { type AppOAuth struct {
Whitelist string Whitelist string `description:"Comma-separated list of allowed OAuth groups." yaml:"whitelist"`
Groups string Groups string `description:"Comma-separated list of required OAuth groups." yaml:"groups"`
} }
type AppIP struct { type AppIP struct {
Allow []string Allow []string `description:"List of allowed IPs or CIDR ranges." yaml:"allow"`
Block []string Block []string `description:"List of blocked IPs or CIDR ranges." yaml:"block"`
Bypass []string Bypass []string `description:"List of IPs or CIDR ranges that bypass authentication." yaml:"bypass"`
} }
type AppResponse struct { type AppResponse struct {
Headers []string Headers []string `description:"Custom headers to add to the response." yaml:"headers"`
BasicAuth AppBasicAuth BasicAuth AppBasicAuth `description:"Basic authentication for the app." yaml:"basicAuth"`
} }
type AppBasicAuth struct { type AppBasicAuth struct {
Username string Username string `description:"Basic auth username." yaml:"username"`
Password string Password string `description:"Basic auth password." yaml:"password"`
PasswordFile string PasswordFile string `description:"Path to the file containing the basic auth password." yaml:"passwordFile"`
} }
type AppPath struct { type AppPath struct {
Allow string Allow string `description:"Comma-separated list of allowed paths." yaml:"allow"`
Block string Block string `description:"Comma-separated list of blocked paths." yaml:"block"`
}
// Flags
type Providers struct {
Providers map[string]OAuthServiceConfig
} }
// API server // API server

View File

@@ -1,122 +1,54 @@
package service package service
import ( import (
"errors"
"strings"
"github.com/rs/zerolog/log"
"github.com/steveiliop56/tinyauth/internal/config" "github.com/steveiliop56/tinyauth/internal/config"
) )
/*
Environment variable/flag based ACLs are disabled until v5 due to a technical challenge
with the current parsing logic.
The current parser works for simple OAuth provider configs like:
- PROVIDERS_MY_AMAZING_PROVIDER_CLIENT_ID
However, it breaks down when handling nested structs required for ACLs. The custom parsing
solution that worked for v4 OAuth providers is incompatible with the ACL parsing logic,
making the codebase unmaintainable and fragile.
A solution is being considered for v5 that would standardize the format to something like:
- TINYAUTH_PROVIDERS_GOOGLE_CLIENTSECRET
- TINYAUTH_APPS_MYAPP_CONFIG_DOMAIN
This would allow the Traefik parser to handle everything consistently, but requires a
config migration. Until this is resolved, environment-based ACLs are disabled and only
Docker label-based ACLs are supported.
See: https://discord.com/channels/1337450123600465984/1337459086270271538/1434986689935179838 for more information
*/
type AccessControlsService struct { type AccessControlsService struct {
docker *DockerService docker *DockerService
// envACLs config.Apps config config.Apps
} }
func NewAccessControlsService(docker *DockerService) *AccessControlsService { func NewAccessControlsService(docker *DockerService, config config.Apps) *AccessControlsService {
return &AccessControlsService{ return &AccessControlsService{
docker: docker, docker: docker,
config: config,
} }
} }
func (acls *AccessControlsService) Init() error { func (acls *AccessControlsService) Init() error {
// acls.envACLs = config.Apps{} return nil // No initialization needed
// env := os.Environ()
// appEnvVars := []string{}
// for _, e := range env {
// if strings.HasPrefix(e, "TINYAUTH_APPS_") {
// appEnvVars = append(appEnvVars, e)
// }
// }
// err := acls.loadEnvACLs(appEnvVars)
// if err != nil {
// return err
// }
// return nil
return nil
} }
// func (acls *AccessControlsService) loadEnvACLs(appEnvVars []string) error { func (acls *AccessControlsService) lookupConfigACLs(domain string) (config.App, error) {
// if len(appEnvVars) == 0 { for app, config := range acls.config.Apps {
// return nil if config.Config.Domain == domain {
// } log.Debug().Str("name", app).Msg("Found matching container by domain")
return config, nil
}
// envAcls := map[string]string{} if strings.SplitN(domain, ".", 2)[0] == app {
log.Debug().Str("name", app).Msg("Found matching container by app name")
return config, nil
}
}
return config.App{}, errors.New("no results")
}
// for _, e := range appEnvVars { func (acls *AccessControlsService) GetAccessControls(domain string) (config.App, error) {
// parts := strings.SplitN(e, "=", 2) // First check in the static config
// if len(parts) != 2 { app, err := acls.lookupConfigACLs(domain)
// continue
// }
// key := parts[0] if err == nil {
// key = strings.ToLower(key) log.Debug().Msg("Using ACls from static configuration")
// key = strings.ReplaceAll(key, "_", ".") return app, nil
// value := parts[1] }
// envAcls[key] = value
// }
// apps, err := decoders.DecodeLabels(envAcls)
// if err != nil {
// return err
// }
// acls.envACLs = apps
// return nil
// }
// func (acls *AccessControlsService) lookupEnvACLs(appDomain string) *config.App {
// if len(acls.envACLs.Apps) == 0 {
// return nil
// }
// for appName, appACLs := range acls.envACLs.Apps {
// if appACLs.Config.Domain == appDomain {
// return &appACLs
// }
// if strings.SplitN(appDomain, ".", 2)[0] == appName {
// return &appACLs
// }
// }
// return nil
// }
func (acls *AccessControlsService) GetAccessControls(appDomain string) (config.App, error) {
// First check environment variables
// envACLs := acls.lookupEnvACLs(appDomain)
// if envACLs != nil {
// log.Debug().Str("domain", appDomain).Msg("Found matching access controls in environment variables")
// return *envACLs, nil
// }
// Fallback to Docker labels // Fallback to Docker labels
return acls.docker.GetLabels(appDomain) log.Debug().Msg("Falling back to Docker labels for ACLs")
return acls.docker.GetLabels(domain)
} }