Web UI: optional HTTP basic auth via UI_USER/UI_PASS

Both must be set together and require LISTEN_UI; constant-time compares,
challenge on every endpoint. Startup warns when LISTEN_UI binds a
non-loopback address without auth. Bind address and port were already
covered by LISTEN_UI itself (host:port).
This commit is contained in:
mram
2026-09-22 18:53:00 +02:00
parent 0796092b80
commit 18a7336c02
8 changed files with 139 additions and 8 deletions
+10
View File
@@ -20,6 +20,8 @@ type Config struct {
ListenOllama string `env:"LISTEN_OLLAMA"`
ListenComfy string `env:"LISTEN_COMFY"`
ListenUI string `env:"LISTEN_UI"`
UIUser string `env:"UI_USER"`
UIPass string `env:"UI_PASS"`
OllamaURL string `env:"OLLAMA_URL"`
ComfyURL string `env:"COMFY_URL"`
UnloadTimeout time.Duration `env:"UNLOAD_TIMEOUT"`
@@ -198,6 +200,8 @@ func Load(getenv func(string) string) (Config, error) {
{"LISTEN_OLLAMA", &cfg.ListenOllama},
{"LISTEN_COMFY", &cfg.ListenComfy},
{"LISTEN_UI", &cfg.ListenUI},
{"UI_USER", &cfg.UIUser},
{"UI_PASS", &cfg.UIPass},
{"OLLAMA_URL", &cfg.OllamaURL},
{"COMFY_URL", &cfg.ComfyURL},
{"WARM_MODEL", &cfg.WarmModel},
@@ -320,6 +324,12 @@ func Load(getenv func(string) string) (Config, error) {
default:
return cfg, fmt.Errorf("LOG_FORMAT: must be \"text\" or \"json\"")
}
if (cfg.UIUser == "") != (cfg.UIPass == "") {
return cfg, fmt.Errorf("UI_USER and UI_PASS must be set together (both empty = no auth)")
}
if cfg.UIUser != "" && cfg.ListenUI == "" {
return cfg, fmt.Errorf("UI_USER/UI_PASS have no effect without LISTEN_UI")
}
if cfg.ComfyCmd != "" && cfg.ComfyURL == "" {
return cfg, fmt.Errorf("COMFY_CMD requires COMFY_URL to be set (the proxy needs somewhere to forward)")
}