mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-12-22 08:02:28 +00:00
feat: add experimental config file support
This commit is contained in:
@@ -14,6 +14,8 @@ TINYAUTH_DISABLEANALYTICS=false
|
|||||||
TINYAUTH_DISABLERESOURCES=false
|
TINYAUTH_DISABLERESOURCES=false
|
||||||
# Disable UI warning messages
|
# Disable UI warning messages
|
||||||
TINYAUTH_DISABLEUIWARNINGS=false
|
TINYAUTH_DISABLEUIWARNINGS=false
|
||||||
|
# Enable JSON formatted logs
|
||||||
|
TINYAUTH_LOGJSON=false
|
||||||
|
|
||||||
# Server Configuration
|
# Server Configuration
|
||||||
|
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -27,3 +27,10 @@ internal/assets/version
|
|||||||
|
|
||||||
# data directory
|
# data directory
|
||||||
data
|
data
|
||||||
|
|
||||||
|
# config file
|
||||||
|
config.yml
|
||||||
|
|
||||||
|
# binary out
|
||||||
|
tinyauth.db
|
||||||
|
resources
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
app_url: "https://tinyauth.example.com"
|
|
||||||
log_level: "info"
|
|
||||||
resources_dir: "/etc/tinyauth/resources"
|
|
||||||
database_path: "/var/lib/tinyauth/tinyauth.db"
|
|
||||||
disable_analytics: false
|
|
||||||
disable_resources: false
|
|
||||||
disable_ui_warnings: false
|
|
||||||
|
|
||||||
server:
|
|
||||||
port: 8080
|
|
||||||
address: "0.0.0.0"
|
|
||||||
socket_path: "/var/run/tinyauth.sock"
|
|
||||||
trusted_proxies: "10.10.10.0/24"
|
|
||||||
|
|
||||||
auth:
|
|
||||||
users: "user:hash"
|
|
||||||
users_file: "/etc/tinyauth/users.yaml"
|
|
||||||
secure_cookie: true
|
|
||||||
session_expiry: 3600
|
|
||||||
login_timeout: 300
|
|
||||||
login_max_retries: 5
|
|
||||||
|
|
||||||
oauth:
|
|
||||||
whitelist: "example.com"
|
|
||||||
auto_redirect: "pocketid"
|
|
||||||
providers:
|
|
||||||
google:
|
|
||||||
client_id: "some-client-id"
|
|
||||||
client_secret: "some-client-secret"
|
|
||||||
client_secret_file: "some-client-secret-file"
|
|
||||||
scopes:
|
|
||||||
- "openid"
|
|
||||||
- "email"
|
|
||||||
- "profile"
|
|
||||||
redirect_url: "https://tinyauth.example.com/oauth/callback/google"
|
|
||||||
auth_url: "https://accounts.google.com/o/oauth2/auth"
|
|
||||||
token_url: "https://oauth2.googleapis.com/token"
|
|
||||||
user_info_url: "https://www.googleapis.com/oauth2/v3/userinfo"
|
|
||||||
insecure: false
|
|
||||||
name: "Google"
|
|
||||||
|
|
||||||
ui:
|
|
||||||
title: "Tinyauth"
|
|
||||||
forgot_password_message: "Contact your administrator to reset your password."
|
|
||||||
background_image: "/static/background.jpg"
|
|
||||||
|
|
||||||
ldap:
|
|
||||||
address: "ldap.example.com:389"
|
|
||||||
base_dn: "dc=example,dc=com"
|
|
||||||
bind_dn: "cn=admin,dc=example,dc=com"
|
|
||||||
bind_password: "password"
|
|
||||||
search_filter: "(uid={username})"
|
|
||||||
insecure: false
|
|
||||||
@@ -52,6 +52,10 @@ EXPOSE 3000
|
|||||||
|
|
||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
|
ENV DATABASEPATH=/data/tinyauth.db
|
||||||
|
|
||||||
|
ENV RESOURCESDIR=/data/resources
|
||||||
|
|
||||||
ENV GIN_MODE=release
|
ENV GIN_MODE=release
|
||||||
|
|
||||||
ENV PATH=$PATH:/tinyauth
|
ENV PATH=$PATH:/tinyauth
|
||||||
|
|||||||
@@ -16,4 +16,8 @@ COPY ./air.toml ./
|
|||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV DATABASEPATH=/data/tinyauth.db
|
||||||
|
|
||||||
|
ENV RESOURCESDIR=/data/resources
|
||||||
|
|
||||||
ENTRYPOINT ["air", "-c", "air.toml"]
|
ENTRYPOINT ["air", "-c", "air.toml"]
|
||||||
@@ -33,7 +33,6 @@ COPY go.sum ./
|
|||||||
|
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
||||||
COPY ./main.go ./
|
|
||||||
COPY ./cmd ./cmd
|
COPY ./cmd ./cmd
|
||||||
COPY ./internal ./internal
|
COPY ./internal ./internal
|
||||||
COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
|
COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
|
||||||
@@ -56,6 +55,10 @@ EXPOSE 3000
|
|||||||
|
|
||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
|
ENV DATABASEPATH=/data/tinyauth.db
|
||||||
|
|
||||||
|
ENV RESOURCESDIR=/data/resources
|
||||||
|
|
||||||
ENV GIN_MODE=release
|
ENV GIN_MODE=release
|
||||||
|
|
||||||
ENV PATH=$PATH:/tinyauth
|
ENV PATH=$PATH:/tinyauth
|
||||||
|
|||||||
@@ -14,17 +14,28 @@ import (
|
|||||||
"github.com/traefik/paerser/cli"
|
"github.com/traefik/paerser/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TinyauthCmdConfiguration struct {
|
func NewTinyauthCmdConfiguration() *config.Config {
|
||||||
config.Config
|
return &config.Config{
|
||||||
// ConfigFile string `description:"Path to config file."`
|
LogLevel: "info",
|
||||||
}
|
ResourcesDir: "./resources",
|
||||||
|
DatabasePath: "./tinyauth.db",
|
||||||
func NewTinyauthCmdConfiguration() *TinyauthCmdConfiguration {
|
Server: config.ServerConfig{
|
||||||
return &TinyauthCmdConfiguration{
|
Port: 3000,
|
||||||
Config: config.Config{
|
Address: "0.0.0.0",
|
||||||
LogLevel: "info",
|
},
|
||||||
|
Auth: config.AuthConfig{
|
||||||
|
SessionExpiry: 3600,
|
||||||
|
LoginTimeout: 300,
|
||||||
|
LoginMaxRetries: 3,
|
||||||
|
},
|
||||||
|
UI: config.UIConfig{
|
||||||
|
Title: "Tinyauth",
|
||||||
|
ForgotPasswordMessage: "You can change your password by changing the configuration.",
|
||||||
|
BackgroundImage: "/background.jpg",
|
||||||
|
},
|
||||||
|
Experimental: config.ExperimentalConfig{
|
||||||
|
ConfigFile: "",
|
||||||
},
|
},
|
||||||
// ConfigFile: "",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,8 +43,9 @@ func main() {
|
|||||||
tConfig := NewTinyauthCmdConfiguration()
|
tConfig := NewTinyauthCmdConfiguration()
|
||||||
|
|
||||||
loaders := []cli.ResourceLoader{
|
loaders := []cli.ResourceLoader{
|
||||||
&loaders.EnvLoader{},
|
&loaders.FileLoader{},
|
||||||
&loaders.FlagLoader{},
|
&loaders.FlagLoader{},
|
||||||
|
&loaders.EnvLoader{},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdTinyauth := &cli.Command{
|
cmdTinyauth := &cli.Command{
|
||||||
@@ -42,7 +54,7 @@ func main() {
|
|||||||
Configuration: tConfig,
|
Configuration: tConfig,
|
||||||
Resources: loaders,
|
Resources: loaders,
|
||||||
Run: func(_ []string) error {
|
Run: func(_ []string) error {
|
||||||
return runCmd(&tConfig.Config)
|
return runCmd(*tConfig)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +95,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCmd(cfg *config.Config) error {
|
func runCmd(cfg config.Config) error {
|
||||||
logLevel, err := zerolog.ParseLevel(strings.ToLower(cfg.LogLevel))
|
logLevel, err := zerolog.ParseLevel(strings.ToLower(cfg.LogLevel))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -92,11 +104,15 @@ func runCmd(cfg *config.Config) error {
|
|||||||
zerolog.SetGlobalLevel(logLevel)
|
zerolog.SetGlobalLevel(logLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).With().Caller().Logger()
|
log.Logger = log.With().Caller().Logger()
|
||||||
|
|
||||||
|
if !cfg.LogJSON {
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339})
|
||||||
|
}
|
||||||
|
|
||||||
log.Info().Str("version", config.Version).Msg("Starting tinyauth")
|
log.Info().Str("version", config.Version).Msg("Starting tinyauth")
|
||||||
|
|
||||||
app := bootstrap.NewBootstrapApp(*cfg)
|
app := bootstrap.NewBootstrapApp(cfg)
|
||||||
|
|
||||||
err = app.Setup()
|
err = app.Setup()
|
||||||
|
|
||||||
|
|||||||
88
config.example.yaml
Normal file
88
config.example.yaml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Tinyauth Example Configuration
|
||||||
|
|
||||||
|
# The base URL where Tinyauth is accessible
|
||||||
|
appUrl: "https://auth.example.com"
|
||||||
|
# Log level: trace, debug, info, warn, error
|
||||||
|
logLevel: "info"
|
||||||
|
# Directory for static resources
|
||||||
|
resourcesDir: "./resources"
|
||||||
|
# Path to SQLite database file
|
||||||
|
databasePath: "./tinyauth.db"
|
||||||
|
# Disable usage analytics
|
||||||
|
disableAnalytics: false
|
||||||
|
# Disable static resource serving
|
||||||
|
disableResources: false
|
||||||
|
# Disable UI warning messages
|
||||||
|
disableUIWarnings: false
|
||||||
|
# Enable JSON formatted logs
|
||||||
|
logJSON: false
|
||||||
|
|
||||||
|
# Server Configuration
|
||||||
|
server:
|
||||||
|
# Port to listen on
|
||||||
|
port: 3000
|
||||||
|
# Interface to bind to (0.0.0.0 for all interfaces)
|
||||||
|
address: "0.0.0.0"
|
||||||
|
# Unix socket path (optional, overrides port/address if set)
|
||||||
|
socketPath: ""
|
||||||
|
# Comma-separated list of trusted proxy IPs/CIDRs
|
||||||
|
trustedProxies: ""
|
||||||
|
|
||||||
|
# Authentication Configuration
|
||||||
|
auth:
|
||||||
|
# Format: username:bcrypt_hash (use bcrypt to generate hash)
|
||||||
|
users: "admin:$2a$10$example_bcrypt_hash_here"
|
||||||
|
# Path to external users file (optional)
|
||||||
|
usersFile: ""
|
||||||
|
# Enable secure cookies (requires HTTPS)
|
||||||
|
secureCookie: false
|
||||||
|
# Session expiry in seconds (3600 = 1 hour)
|
||||||
|
sessionExpiry: 3600
|
||||||
|
# Login timeout in seconds (300 = 5 minutes)
|
||||||
|
loginTimeout: 300
|
||||||
|
# Maximum login retries before lockout
|
||||||
|
loginMaxRetries: 3
|
||||||
|
|
||||||
|
# OAuth Configuration
|
||||||
|
oauth:
|
||||||
|
# Regex pattern for allowed email addresses (e.g., /@example\.com$/)
|
||||||
|
whitelist: ""
|
||||||
|
# Provider ID to auto-redirect to (skips login page)
|
||||||
|
autoRedirect: ""
|
||||||
|
# OAuth Provider Configuration (replace myprovider with your provider name)
|
||||||
|
providers:
|
||||||
|
myprovider:
|
||||||
|
clientId: "your_client_id_here"
|
||||||
|
clientSecret: "your_client_secret_here"
|
||||||
|
authUrl: "https://provider.example.com/oauth/authorize"
|
||||||
|
tokenUrl: "https://provider.example.com/oauth/token"
|
||||||
|
userInfoUrl: "https://provider.example.com/oauth/userinfo"
|
||||||
|
redirectUrl: "https://auth.example.com/api/oauth/callback/myprovider"
|
||||||
|
scopes: "openid email profile"
|
||||||
|
name: "My OAuth Provider"
|
||||||
|
# Allow insecure connections (self-signed certificates)
|
||||||
|
insecure: false
|
||||||
|
|
||||||
|
# UI Customization
|
||||||
|
ui:
|
||||||
|
# Custom title for login page
|
||||||
|
title: "Tinyauth"
|
||||||
|
# Message shown on forgot password page
|
||||||
|
forgotPasswordMessage: "Contact your administrator to reset your password"
|
||||||
|
# Background image URL for login page
|
||||||
|
backgroundImage: ""
|
||||||
|
|
||||||
|
# LDAP Configuration (optional)
|
||||||
|
ldap:
|
||||||
|
# LDAP server address
|
||||||
|
address: "ldap://ldap.example.com:389"
|
||||||
|
# DN for binding to LDAP server
|
||||||
|
bindDn: "cn=readonly,dc=example,dc=com"
|
||||||
|
# Password for bind DN
|
||||||
|
bindPassword: "your_bind_password"
|
||||||
|
# Base DN for user searches
|
||||||
|
baseDn: "dc=example,dc=com"
|
||||||
|
# Search filter (%s will be replaced with username)
|
||||||
|
searchFilter: "(&(uid=%s)(memberOf=cn=users,ou=groups,dc=example,dc=com))"
|
||||||
|
# Allow insecure LDAP connections
|
||||||
|
insecure: false
|
||||||
@@ -15,55 +15,61 @@ var RedirectCookieName = "tinyauth-redirect"
|
|||||||
// Main app config
|
// Main app config
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
AppURL string `description:"The base URL where the app is hosted."`
|
AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"`
|
||||||
LogLevel string `description:"Log level (trace, debug, info, warn, error)."`
|
LogLevel string `description:"Log level (trace, debug, info, warn, error)." yaml:"logLevel"`
|
||||||
ResourcesDir string `description:"The directory where resources are stored."`
|
ResourcesDir string `description:"The directory where resources are stored." yaml:"resourcesDir"`
|
||||||
DatabasePath string `description:"The path to the database file."`
|
DatabasePath string `description:"The path to the database file." yaml:"databasePath"`
|
||||||
DisableAnalytics bool `description:"Disable analytics."`
|
DisableAnalytics bool `description:"Disable analytics." yaml:"disableAnalytics"`
|
||||||
DisableResources bool `description:"Disable resources server."`
|
DisableResources bool `description:"Disable resources server." yaml:"disableResources"`
|
||||||
DisableUIWarnings bool `description:"Disable UI warnings."`
|
DisableUIWarnings bool `description:"Disable UI warnings." yaml:"disableUIWarnings"`
|
||||||
Server ServerConfig
|
LogJSON bool `description:"Enable JSON formatted logs." yaml:"logJSON"`
|
||||||
Auth AuthConfig
|
Server ServerConfig `description:"Server configuration." yaml:"server"`
|
||||||
OAuth OAuthConfig
|
Auth AuthConfig `description:"Authentication configuration." yaml:"auth"`
|
||||||
UI UIConfig
|
OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"`
|
||||||
Ldap LdapConfig
|
UI UIConfig `description:"UI customization." yaml:"ui"`
|
||||||
|
Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"`
|
||||||
|
Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Port int `description:"The port on which the server listens."`
|
Port int `description:"The port on which the server listens." yaml:"port"`
|
||||||
Address string `description:"The address on which the server listens."`
|
Address string `description:"The address on which the server listens." yaml:"address"`
|
||||||
SocketPath string `description:"The path to the Unix socket."`
|
SocketPath string `description:"The path to the Unix socket." yaml:"socketPath"`
|
||||||
TrustedProxies string `description:"Comma-separated list of trusted proxy addresses."`
|
TrustedProxies string `description:"Comma-separated list of trusted proxy addresses." yaml:"trustedProxies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthConfig struct {
|
type AuthConfig struct {
|
||||||
Users string `description:"Comma-separated list of users (username:hashed_password)."`
|
Users string `description:"Comma-separated list of users (username:hashed_password)." yaml:"users"`
|
||||||
UsersFile string `description:"Path to the users file."`
|
UsersFile string `description:"Path to the users file." yaml:"usersFile"`
|
||||||
SecureCookie bool `description:"Enable secure cookies."`
|
SecureCookie bool `description:"Enable secure cookies." yaml:"secureCookie"`
|
||||||
SessionExpiry int `description:"Session expiry time in seconds."`
|
SessionExpiry int `description:"Session expiry time in seconds." yaml:"sessionExpiry"`
|
||||||
LoginTimeout int `description:"Login timeout in seconds."`
|
LoginTimeout int `description:"Login timeout in seconds." yaml:"loginTimeout"`
|
||||||
LoginMaxRetries int `description:"Maximum login retries."`
|
LoginMaxRetries int `description:"Maximum login retries." yaml:"loginMaxRetries"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OAuthConfig struct {
|
type OAuthConfig struct {
|
||||||
Whitelist string `description:"Comma-separated list of allowed OAuth domains."`
|
Whitelist string `description:"Comma-separated list of allowed OAuth domains." yaml:"whitelist"`
|
||||||
AutoRedirect string `description:"The OAuth provider to use for automatic redirection."`
|
AutoRedirect string `description:"The OAuth provider to use for automatic redirection." yaml:"autoRedirect"`
|
||||||
Providers map[string]OAuthServiceConfig
|
Providers map[string]OAuthServiceConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type UIConfig struct {
|
type UIConfig struct {
|
||||||
Title string `description:"The title of the UI."`
|
Title string `description:"The title of the UI." yaml:"title"`
|
||||||
ForgotPasswordMessage string `description:"Message displayed on the forgot password page."`
|
ForgotPasswordMessage string `description:"Message displayed on the forgot password page." yaml:"forgotPasswordMessage"`
|
||||||
BackgroundImage string `description:"Path to the background image."`
|
BackgroundImage string `description:"Path to the background image." yaml:"backgroundImage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LdapConfig struct {
|
type LdapConfig struct {
|
||||||
Address string `description:"LDAP server address."`
|
Address string `description:"LDAP server address." yaml:"address"`
|
||||||
BindDN string `description:"Bind DN for LDAP authentication."`
|
BindDN string `description:"Bind DN for LDAP authentication." yaml:"bindDn"`
|
||||||
BindPassword string `description:"Bind password for LDAP authentication."`
|
BindPassword string `description:"Bind password for LDAP authentication." yaml:"bindPassword"`
|
||||||
BaseDN string `description:"Base DN for LDAP searches."`
|
BaseDN string `description:"Base DN for LDAP searches." yaml:"baseDn"`
|
||||||
Insecure bool `description:"Allow insecure LDAP connections."`
|
Insecure bool `description:"Allow insecure LDAP connections." yaml:"insecure"`
|
||||||
SearchFilter string `description:"LDAP search filter."`
|
SearchFilter string `description:"LDAP search filter." yaml:"searchFilter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExperimentalConfig struct {
|
||||||
|
ConfigFile string `description:"Path to config file." yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config loader options
|
// Config loader options
|
||||||
|
|||||||
35
internal/utils/loaders/loader_file.go
Normal file
35
internal/utils/loaders/loader_file.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package loaders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/traefik/paerser/cli"
|
||||||
|
"github.com/traefik/paerser/file"
|
||||||
|
"github.com/traefik/paerser/flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileLoader struct{}
|
||||||
|
|
||||||
|
func (f *FileLoader) Load(args []string, cmd *cli.Command) (bool, error) {
|
||||||
|
flags, err := flag.Parse(args, cmd.Configuration)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// I guess we are using traefik as the root name
|
||||||
|
configFileFlag := "traefik.experimental.configFile"
|
||||||
|
|
||||||
|
if _, ok := flags[configFileFlag]; !ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warn().Msg("Using experimental file config loader, this feature is experimental and may change or be removed in future releases")
|
||||||
|
|
||||||
|
err = file.Decode(flags[configFileFlag], cmd.Configuration)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user