Add Authelia as centralized authentication and OIDC provider
Authelia runs as a managed native service on Wild Central, providing two integration patterns for network services: - Forward-auth via HAProxy for apps without native SSO (Lua auth-request script intercepts requests, redirects unauthenticated users to login portal) - OIDC provider for apps with native support (Gitea, Grafana, etc.) Backend: new internal/authelia/ package with service manager, config generation, file-based user management (argon2id), and OIDC client management. API endpoints for status, config, users CRUD, and OIDC clients CRUD. HAProxy: config generator extended with lua-load, auth-request directives, and Authelia backend. Auth directives scoped to session cookie domain — only subdomains of the auth portal's parent are eligible for forward-auth. Frontend: Authentication page with enable/disable, user management, OIDC client management (add/edit/delete), and protected domains toggles. Advanced subsystem page for raw config view. Dashboard service card and sidebar entries.
This commit is contained in:
@@ -39,6 +39,8 @@ type HTTPRouteBackend struct {
|
||||
HealthPath string // optional health check path
|
||||
Headers *domains.HeaderConfig // per-route headers
|
||||
IPAllow []string // per-route CIDR whitelist
|
||||
AuthEnabled bool // forward-auth protection via Authelia
|
||||
AuthPolicy string // one_factor, two_factor (for access control)
|
||||
}
|
||||
|
||||
// HTTPRoute represents an L7 HTTP reverse proxy route.
|
||||
@@ -80,8 +82,12 @@ func (m *Manager) GetConfigPath() string {
|
||||
|
||||
// GenerateOpts holds optional parameters for HAProxy config generation.
|
||||
type GenerateOpts struct {
|
||||
HTTPRoutes []HTTPRoute // L7 HTTP reverse proxy routes (TLS terminated by Central)
|
||||
CertsDir string // directory containing per-domain PEM files for L7 TLS
|
||||
HTTPRoutes []HTTPRoute // L7 HTTP reverse proxy routes (TLS terminated by Central)
|
||||
CertsDir string // directory containing per-domain PEM files for L7 TLS
|
||||
AuthEnabled bool // global Authelia forward-auth toggle
|
||||
AuthBackend string // Authelia backend address (e.g. "127.0.0.1:9091")
|
||||
AuthDomain string // Authelia login portal domain (excluded from protection)
|
||||
AuthSessionDomain string // session cookie scope (e.g. "payne.io") — only subdomains are eligible for forward-auth
|
||||
}
|
||||
|
||||
// GenerateWithOpts creates a complete HAProxy configuration with full options.
|
||||
@@ -97,7 +103,13 @@ global
|
||||
stats timeout 30s
|
||||
maxconn 50000
|
||||
daemon
|
||||
`)
|
||||
if opts.AuthEnabled {
|
||||
sb.WriteString(" lua-prepend-path /etc/haproxy/lua/?.lua\n")
|
||||
sb.WriteString(" lua-load /etc/haproxy/lua/haproxy-auth-request.lua\n")
|
||||
}
|
||||
|
||||
sb.WriteString(`
|
||||
defaults
|
||||
log global
|
||||
mode tcp
|
||||
@@ -284,7 +296,48 @@ frontend http_in
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString("\n")
|
||||
// Authelia forward-auth directives for protected routes
|
||||
if opts.AuthEnabled && opts.AuthBackend != "" {
|
||||
authDomainACL := ""
|
||||
if opts.AuthDomain != "" {
|
||||
authDomainACL = "host_" + sanitizeName(opts.AuthDomain)
|
||||
}
|
||||
|
||||
for _, httpRoute := range opts.HTTPRoutes {
|
||||
hostACL := "host_" + sanitizeName(httpRoute.Name)
|
||||
|
||||
// Skip auth portal domain (would cause infinite redirect)
|
||||
if hostACL == authDomainACL {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip domains outside the auth session scope — forward-auth
|
||||
// requires shared cookies, which only work within the same
|
||||
// parent domain. Other domains should use OIDC instead.
|
||||
if opts.AuthSessionDomain != "" &&
|
||||
httpRoute.Domain != opts.AuthSessionDomain &&
|
||||
!strings.HasSuffix(httpRoute.Domain, "."+opts.AuthSessionDomain) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if any route on this domain has auth enabled
|
||||
hasAuth := false
|
||||
for _, rb := range httpRoute.Routes {
|
||||
if rb.AuthEnabled {
|
||||
hasAuth = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasAuth {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintf(&sb, " # auth: %s\n", httpRoute.Domain)
|
||||
fmt.Fprintf(&sb, " http-request lua.auth-request be_authelia /api/authz/forward-auth if %s\n", hostACL)
|
||||
fmt.Fprintf(&sb, " http-request redirect location https://%s/?rd=https://%%[hdr(host)]%%[path] if %s !{ var(txn.auth_response_successful) -m bool }\n", opts.AuthDomain, hostACL)
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
// Routing rules: path-specific first, then catch-all
|
||||
for _, httpRoute := range opts.HTTPRoutes {
|
||||
@@ -333,6 +386,15 @@ frontend http_in
|
||||
}
|
||||
}
|
||||
|
||||
// Authelia backend for forward-auth
|
||||
if opts.AuthEnabled && opts.AuthBackend != "" {
|
||||
sb.WriteString("# Authelia forward-auth backend\n")
|
||||
sb.WriteString("backend be_authelia\n")
|
||||
sb.WriteString(" mode http\n")
|
||||
fmt.Fprintf(&sb, " server s0 %s\n", opts.AuthBackend)
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
for _, route := range custom {
|
||||
fmt.Fprintf(&sb, "# Custom route: %s\n", route.Name)
|
||||
fmt.Fprintf(&sb, "frontend fe_%s\n", sanitizeName(route.Name))
|
||||
|
||||
Reference in New Issue
Block a user