From a6c716c4e297411bdf44a3f8d09876d180513662 Mon Sep 17 00:00:00 2001 From: Stavros Date: Wed, 1 Jul 2026 16:12:46 +0300 Subject: [PATCH] fix: ensure data paths are set correctly in docker, fixes #958 (#959) --- Dockerfile | 12 +++++--- Dockerfile.distroless | 14 +++++---- Makefile | 13 ++++++-- cmd/tinyauth/config.go | 26 ++++++++++++++++ cmd/tinyauth/tinyauth.go | 9 +++++- gen/gen_env.go | 2 +- gen/gen_md.go | 2 +- internal/model/config.go | 66 +++++++++++++++++++++++++++++----------- 8 files changed, 109 insertions(+), 35 deletions(-) create mode 100644 cmd/tinyauth/config.go diff --git a/Dockerfile b/Dockerfile index ed091586..3e748920 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,15 +52,17 @@ WORKDIR /tinyauth COPY --from=builder /tinyauth/tinyauth ./ -RUN mkdir -p /data - EXPOSE 3000 +# Make the data directory with a non-root user +RUN addgroup tinyauth && adduser -DH tinyauth -G tinyauth +RUN mkdir -p /data/resources /data/oidc /data/tailscale +RUN chown -R tinyauth:tinyauth /data + VOLUME ["/data"] -ENV TINYAUTH_DATABASE_PATH=/data/tinyauth.db - -ENV TINYAUTH_RESOURCES_PATH=/data/resources +# Tell tinyauth that it's running in a container and where to find the data directory +ENV RUNTIME_ENV=docker ENV PATH=$PATH:/tinyauth diff --git a/Dockerfile.distroless b/Dockerfile.distroless index 64035fe7..da282b69 100644 --- a/Dockerfile.distroless +++ b/Dockerfile.distroless @@ -40,13 +40,16 @@ COPY ./cmd ./cmd COPY ./internal ./internal COPY --from=frontend-builder /frontend/dist ./internal/assets/dist -RUN mkdir -p data - RUN CGO_ENABLED=0 go build -ldflags "${LDFLAGS} \ -X github.com/tinyauthapp/tinyauth/internal/model.Version=${VERSION} \ -X github.com/tinyauthapp/tinyauth/internal/model.CommitHash=${COMMIT_HASH} \ -X github.com/tinyauthapp/tinyauth/internal/model.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth +# Make the data directory with a non-root user +RUN addgroup tinyauth && adduser -DH tinyauth -G tinyauth +RUN mkdir -p /data/resources /data/oidc /data/tailscale +RUN chown -R tinyauth:tinyauth /data + # Runner FROM gcr.io/distroless/static-debian12:latest AS runner @@ -55,15 +58,14 @@ WORKDIR /tinyauth COPY --from=builder /tinyauth/tinyauth ./ # Since it's distroless, we need to copy the data directory from the builder stage -COPY --from=builder /tinyauth/data /data +COPY --from=builder /data /data EXPOSE 3000 VOLUME ["/data"] -ENV TINYAUTH_DATABASE_PATH=/data/tinyauth.db - -ENV TINYAUTH_RESOURCES_PATH=/data/resources +# Tell tinyauth that it's running in a container and where to find the data directory +ENV RUNTIME_ENV=docker ENV PATH=$PATH:/tinyauth diff --git a/Makefile b/Makefile index b50b7bec..ca59093c 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,8 @@ PROD_COMPOSE := $(shell test -f "docker-compose.test.prod.yml" && echo "docker-c .DEFAULT_GOAL := binary +.PHONY: deps clean-data clean-webui webui binary binary-linux-amd64 binary-linux-arm64 test vet test-race dev dev-infisical prod prod-infisical sql generate docker docker-distroless + # Deps deps: cd frontend && pnpm ci @@ -58,12 +60,10 @@ binary-linux-arm64: $(MAKE) binary # Go test -.PHONY: test test: go test -v ./... # Go vet -.PHONY: vet vet: go vet ./... @@ -88,7 +88,6 @@ prod-infisical: infisical run --env=dev -- docker compose -f $(PROD_COMPOSE) up --force-recreate --pull=always --remove-orphans # SQL -.PHONY: sql sql: sqlc generate @@ -96,3 +95,11 @@ sql: generate: go run ./gen go generate ./internal/repository/... + +# Docker image +docker: + docker buildx build -t tinyauthapp/tinyauth:dev --build-arg=VERSION=$(TAG_NAME) --build-arg=COMMIT_HASH=$(COMMIT_HASH) --build-arg=BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) -f Dockerfile . + +# Docker image distroless +docker-distroless: + docker buildx build -t tinyauthapp/tinyauth:dev-distroless --build-arg=VERSION=$(TAG_NAME) --build-arg=COMMIT_HASH=$(COMMIT_HASH) --build-arg=BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) -f Dockerfile.distroless . diff --git a/cmd/tinyauth/config.go b/cmd/tinyauth/config.go new file mode 100644 index 00000000..3c6be1b3 --- /dev/null +++ b/cmd/tinyauth/config.go @@ -0,0 +1,26 @@ +package main + +import ( + "encoding/json" + "fmt" + + "github.com/tinyauthapp/paerser/cli" + "github.com/tinyauthapp/tinyauth/internal/model" +) + +func configCmd(tconfig *model.Config, loaders []cli.ResourceLoader) *cli.Command { + return &cli.Command{ + Name: "config", + Description: "Print the configuration of Tinyauth", + Configuration: tconfig, + Resources: loaders, + Run: func(_ []string) error { + jsonBytes, err := json.MarshalIndent(tconfig, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal configuration: %w", err) + } + fmt.Println(string(jsonBytes)) + return nil + }, + } +} diff --git a/cmd/tinyauth/tinyauth.go b/cmd/tinyauth/tinyauth.go index b6293718..bf84bf4c 100644 --- a/cmd/tinyauth/tinyauth.go +++ b/cmd/tinyauth/tinyauth.go @@ -13,7 +13,8 @@ import ( ) func main() { - tConfig := model.NewDefaultConfiguration() + env := model.DetectRuntimeEnv() + tConfig := model.NewDefaultConfiguration(env) loaders := []cli.ResourceLoader{ &loaders.FileLoader{}, @@ -52,6 +53,12 @@ func main() { log.Fatal().Err(err).Msg("Failed to add version command") } + err = cmdTinyauth.AddCommand(configCmd(tConfig, loaders)) + + if err != nil { + log.Fatal().Err(err).Msg("Failed to add config command") + } + err = cmdUser.AddCommand(verifyUserCmd()) if err != nil { diff --git a/gen/gen_env.go b/gen/gen_env.go index 36354fff..041b43cf 100644 --- a/gen/gen_env.go +++ b/gen/gen_env.go @@ -20,7 +20,7 @@ type EnvEntry struct { } func generateExampleEnv() { - cfg := model.NewDefaultConfiguration() + cfg := model.NewDefaultConfiguration(model.RuntimeEnvUnknown) entries := make([]EnvEntry, 0) root := reflect.TypeOf(cfg).Elem() diff --git a/gen/gen_md.go b/gen/gen_md.go index 0dcf3822..a5abf587 100644 --- a/gen/gen_md.go +++ b/gen/gen_md.go @@ -21,7 +21,7 @@ type MarkdownEntry struct { } func generateMarkdown() { - cfg := model.NewDefaultConfiguration() + cfg := model.NewDefaultConfiguration(model.RuntimeEnvUnknown) entries := make([]MarkdownEntry, 0) root := reflect.TypeOf(cfg).Elem() diff --git a/internal/model/config.go b/internal/model/config.go index 23648794..e5141530 100644 --- a/internal/model/config.go +++ b/internal/model/config.go @@ -1,8 +1,27 @@ package model +import "os" + +type RuntimeEnv int + +const ( + RuntimeEnvUnknown RuntimeEnv = iota + RuntimeEnvDocker +) + +func DetectRuntimeEnv() RuntimeEnv { + env := os.Getenv("RUNTIME_ENV") + switch env { + case "docker": + return RuntimeEnvDocker + default: + return RuntimeEnvUnknown + } +} + // Default configuration -func NewDefaultConfiguration() *Config { - return &Config{ +func NewDefaultConfiguration(runtimeEnv RuntimeEnv) *Config { + cfg := &Config{ Database: DatabaseConfig{ Driver: "sqlite", Path: "./tinyauth.db", @@ -67,25 +86,36 @@ func NewDefaultConfiguration() *Config { }, LabelProvider: "auto", } + + // apply path overrides for docker runtime + if runtimeEnv == RuntimeEnvDocker { + cfg.Database.Path = "/data/tinyauth.db" + cfg.Resources.Path = "/data/resources" + cfg.OIDC.PrivateKeyPath = "/data/oidc/key.pem" + cfg.OIDC.PublicKeyPath = "/data/oidc/key.pub" + cfg.Tailscale.Dir = "/data/tailscale" + } + + return cfg } type Config struct { - AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"` - Database DatabaseConfig `description:"Database configuration." yaml:"database"` - Analytics AnalyticsConfig `description:"Analytics configuration." yaml:"analytics"` - Resources ResourcesConfig `description:"Resources configuration." yaml:"resources"` - Server ServerConfig `description:"Server configuration." yaml:"server"` - Auth AuthConfig `description:"Authentication configuration." yaml:"auth"` - Apps map[string]App `description:"Application ACLs configuration." yaml:"apps"` - OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"` - OIDC OIDCConfig `description:"OIDC configuration." yaml:"oidc"` - UI UIConfig `description:"UI customization." yaml:"ui"` - LDAP LDAPConfig `description:"LDAP configuration." yaml:"ldap"` - Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"` - LabelProvider string `description:"Label provider to use for ACLs (auto, docker, kubernetes or none to disable). auto detects the environment." yaml:"labelProvider"` - Log LogConfig `description:"Logging configuration." yaml:"log"` - Tailscale TailscaleConfig `description:"Tailscale configuration." yaml:"tailscale"` - ConfigFile string `description:"Path to config file." yaml:"-"` + AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"` + Database DatabaseConfig `description:"Database configuration." yaml:"database"` + Analytics AnalyticsConfig `description:"Analytics configuration." yaml:"analytics"` + Resources ResourcesConfig `description:"Resources configuration." yaml:"resources"` + Server ServerConfig `description:"Server configuration." yaml:"server"` + Auth AuthConfig `description:"Authentication configuration." yaml:"auth"` + Apps map[string]App `description:"Application ACLs configuration." yaml:"apps"` + OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"` + OIDC OIDCConfig `description:"OIDC configuration." yaml:"oidc"` + UI UIConfig `description:"UI customization." yaml:"ui"` + LDAP LDAPConfig `description:"LDAP configuration." yaml:"ldap"` + // Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"` + LabelProvider string `description:"Label provider to use for ACLs (auto, docker, kubernetes or none to disable). auto detects the environment." yaml:"labelProvider"` + Log LogConfig `description:"Logging configuration." yaml:"log"` + Tailscale TailscaleConfig `description:"Tailscale configuration." yaml:"tailscale"` + ConfigFile string `description:"Path to config file." yaml:"-"` } type DatabaseConfig struct {